diff options
author | Tom Rini <trini@konsulko.com> | 2015-08-31 11:43:47 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2015-08-31 11:43:47 -0400 |
commit | 80cd58b99e8690b05e8537dbf76276e24fcfa652 (patch) | |
tree | 0684c14480ab87bb3a74c1c70275b5fdc9c8e3c9 | |
parent | 7c0e5d865ff0b86dfce492b656238919c659d756 (diff) | |
parent | 897705ec39682ab3bf5bb87bc49d7a491d522051 (diff) |
Merge git://git.denx.de/u-boot-dm
83 files changed, 3305 insertions, 1377 deletions
@@ -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/arch/arm/dts/exynos5250-snow.dts b/arch/arm/dts/exynos5250-snow.dts index 32c0098bd8..bda5499988 100644 --- a/arch/arm/dts/exynos5250-snow.dts +++ b/arch/arm/dts/exynos5250-snow.dts @@ -206,6 +206,15 @@ }; }; + i2c@12C90000 { + clock-frequency = <100000>; + tpm@20 { + reg = <0x20>; + u-boot,i2c-offset-len = <0>; + compatible = "infineon,slb9635tt"; + }; + }; + spi@12d30000 { spi-max-frequency = <50000000>; firmware_storage_spi: flash@0 { diff --git a/arch/arm/dts/exynos5250-spring.dts b/arch/arm/dts/exynos5250-spring.dts index 76d5323dc3..81b3d29f9c 100644 --- a/arch/arm/dts/exynos5250-spring.dts +++ b/arch/arm/dts/exynos5250-spring.dts @@ -59,6 +59,14 @@ <&gpy4 2 0>; }; + i2c@12C90000 { + clock-frequency = <100000>; + tpm@20 { + reg = <0x20>; + compatible = "infineon,slb9645tt"; + }; + }; + mmc@12200000 { samsung,bus-width = <8>; samsung,timing = <1 3 3>; diff --git a/arch/arm/dts/exynos5420-peach-pit.dts b/arch/arm/dts/exynos5420-peach-pit.dts index 2d2b7c9bde..16d52f4928 100644 --- a/arch/arm/dts/exynos5420-peach-pit.dts +++ b/arch/arm/dts/exynos5420-peach-pit.dts @@ -197,9 +197,9 @@ i2c@12E10000 { /* i2c9 */ clock-frequency = <400000>; - tpm@20 { - compatible = "infineon,slb9645tt"; - reg = <0x20>; + tpm@20 { + compatible = "infineon,slb9645tt"; + reg = <0x20>; }; }; diff --git a/arch/arm/dts/exynos5800-peach-pi.dts b/arch/arm/dts/exynos5800-peach-pi.dts index 600c2948cf..1d7ff23c93 100644 --- a/arch/arm/dts/exynos5800-peach-pi.dts +++ b/arch/arm/dts/exynos5800-peach-pi.dts @@ -72,9 +72,9 @@ i2c@12E10000 { /* i2c9 */ clock-frequency = <400000>; - tpm@20 { - compatible = "infineon,slb9645tt"; - reg = <0x20>; + tpm@20 { + compatible = "infineon,slb9645tt"; + reg = <0x20>; }; }; diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts index 8927527141..65b9125f5f 100644 --- a/arch/sandbox/dts/sandbox.dts +++ b/arch/sandbox/dts/sandbox.dts @@ -92,6 +92,8 @@ reg = <0 0>; compatible = "sandbox,i2c"; clock-frequency = <400000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c0>; eeprom@2c { reg = <0x2c>; compatible = "i2c-eeprom"; @@ -136,6 +138,21 @@ }; }; + pinctrl { + compatible = "sandbox,pinctrl"; + + pinctrl_i2c0: i2c0 { + groups = "i2c"; + function = "i2c"; + bias-pull-up; + }; + + pinctrl_serial0: uart0 { + groups = "serial_a"; + function = "serial"; + }; + }; + spi@0 { #address-cells = <1>; #size-cells = <0>; @@ -156,6 +173,10 @@ sides = <4>; }; + tpm { + compatible = "google,sandbox-tpm"; + }; + triangle { compatible = "demo-shape"; colour = "cyan"; @@ -168,6 +189,8 @@ uart0: serial { compatible = "sandbox,serial"; sandbox,text-colour = "cyan"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_serial0>; }; usb@0 { diff --git a/arch/x86/dts/chromebook_link.dts b/arch/x86/dts/chromebook_link.dts index ad390bf117..4291141dfe 100644 --- a/arch/x86/dts/chromebook_link.dts +++ b/arch/x86/dts/chromebook_link.dts @@ -237,6 +237,11 @@ }; }; + tpm { + reg = <0xfed40000 0x5000>; + compatible = "infineon,slb9635lpc"; + }; + microcode { update@0 { #include "microcode/m12306a9_0000001b.dtsi" diff --git a/arch/x86/dts/chromebox_panther.dts b/arch/x86/dts/chromebox_panther.dts index 84eae3ab65..36feb96a94 100644 --- a/arch/x86/dts/chromebox_panther.dts +++ b/arch/x86/dts/chromebox_panther.dts @@ -62,4 +62,9 @@ }; }; + tpm { + reg = <0xfed40000 0x5000>; + compatible = "infineon,slb9635lpc"; + }; + }; diff --git a/board/samsung/common/exynos5-dt.c b/board/samsung/common/exynos5-dt.c index 7d1b88a9b8..4250f722da 100644 --- a/board/samsung/common/exynos5-dt.c +++ b/board/samsung/common/exynos5-dt.c @@ -121,11 +121,12 @@ int exynos_power_init(void) return ret; /* - * This would normally be 1.3V, but since we are running slowly 1V + * This would normally be 1.3V, but since we are running slowly 1.1V * is enough. For spring it helps reduce CPU temperature and avoid - * hangs with the case open. + * hangs with the case open. 1.1V is minimum voltage borderline for + * chained bootloaders. */ - ret = exynos_set_regulator("vdd_arm", 1000000); + ret = exynos_set_regulator("vdd_arm", 1100000); if (ret) return ret; ret = exynos_set_regulator("vdd_int", 1012500); diff --git a/common/Kconfig b/common/Kconfig index 88dc016079..2c42b8e4d0 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -625,4 +625,26 @@ config CMD_REGULATOR endmenu +menu "Security commands" +config CMD_TPM + bool "Enable the 'tpm' command" + depends on TPM + help + This provides a means to talk to a TPM from the command line. A wide + range of commands if provided - see 'tpm help' for details. The + command requires a suitable TPM on your board and the correct driver + must be enabled. + +config CMD_TPM_TEST + bool "Enable the 'tpm test' command" + depends on CMD_TPM + help + This provides a a series of tests to confirm that the TPM is working + correctly. The tests cover initialisation, non-volatile RAM, extend, + global lock and checking that timing is within expectations. The + tests pass correctly on Infineon TPMs but may need to be adjusted + for other devices. + +endmenu + endmenu diff --git a/common/Makefile b/common/Makefile index dc82433e90..f4ba8782f5 100644 --- a/common/Makefile +++ b/common/Makefile @@ -169,6 +169,7 @@ obj-$(CONFIG_CMD_TIME) += cmd_time.o obj-$(CONFIG_CMD_TRACE) += cmd_trace.o obj-$(CONFIG_SYS_HUSH_PARSER) += cmd_test.o obj-$(CONFIG_CMD_TPM) += cmd_tpm.o +obj-$(CONFIG_CMD_TPM_TEST) += cmd_tpm_test.o obj-$(CONFIG_CMD_TSI148) += cmd_tsi148.o obj-$(CONFIG_CMD_UBI) += cmd_ubi.o obj-$(CONFIG_CMD_UBIFS) += cmd_ubifs.o diff --git a/common/cmd_i2c.c b/common/cmd_i2c.c index 1bc0db860c..864b2596cc 100644 --- a/common/cmd_i2c.c +++ b/common/cmd_i2c.c @@ -453,6 +453,37 @@ static int do_i2c_flags(cmd_tbl_t *cmdtp, int flag, int argc, return 0; } + +static int do_i2c_olen(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + struct udevice *dev; + uint olen; + int chip; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + chip = simple_strtoul(argv[1], NULL, 16); + ret = i2c_get_cur_bus_chip(chip, &dev); + if (ret) + return i2c_report_err(ret, I2C_ERR_READ); + + if (argc > 2) { + olen = simple_strtoul(argv[2], NULL, 16); + ret = i2c_set_chip_offset_len(dev, olen); + } else { + ret = i2c_get_chip_offset_len(dev); + if (ret >= 0) { + printf("%x\n", ret); + ret = 0; + } + } + if (ret) + return i2c_report_err(ret, I2C_ERR_READ); + + return 0; +} #endif /** @@ -1903,6 +1934,7 @@ static cmd_tbl_t cmd_i2c_sub[] = { U_BOOT_CMD_MKENT(write, 6, 0, do_i2c_write, "", ""), #ifdef CONFIG_DM_I2C U_BOOT_CMD_MKENT(flags, 2, 1, do_i2c_flags, "", ""), + U_BOOT_CMD_MKENT(olen, 2, 1, do_i2c_olen, "", ""), #endif U_BOOT_CMD_MKENT(reset, 0, 1, do_i2c_reset, "", ""), #if defined(CONFIG_CMD_SDRAM) @@ -1971,6 +2003,7 @@ static char i2c_help_text[] = " to I2C; the -s option selects bulk write in a single transaction\n" #ifdef CONFIG_DM_I2C "i2c flags chip [flags] - set or get chip flags\n" + "i2c olen chip [offset_length] - set or get chip offset length\n" #endif "i2c reset - re-init the I2C Controller\n" #if defined(CONFIG_CMD_SDRAM) diff --git a/common/cmd_tpm.c b/common/cmd_tpm.c index 0294952e69..97501cc3d1 100644 --- a/common/cmd_tpm.c +++ b/common/cmd_tpm.c @@ -6,6 +6,7 @@ #include <common.h> #include <command.h> +#include <dm.h> #include <malloc.h> #include <tpm.h> #include <asm/unaligned.h> @@ -57,6 +58,8 @@ static void *parse_byte_string(char *bytes, uint8_t *data, size_t *count_ptr) size_t count, length; int i; + if (!bytes) + return NULL; length = strlen(bytes); count = length / 2; @@ -79,17 +82,19 @@ static void *parse_byte_string(char *bytes, uint8_t *data, size_t *count_ptr) } /** - * Convert TPM command return code to U-Boot command error codes. + * report_return_code() - Report any error and return failure or success * * @param return_code TPM command return code * @return value of enum command_ret_t */ -static int convert_return_code(uint32_t return_code) +static int report_return_code(int return_code) { - if (return_code) + if (return_code) { + printf("Error: %d\n", return_code); return CMD_RET_FAILURE; - else + } else { return CMD_RET_SUCCESS; + } } /** @@ -251,7 +256,7 @@ static int do_tpm_startup(cmd_tbl_t *cmdtp, int flag, return CMD_RET_FAILURE; } - return convert_return_code(tpm_startup(mode)); + return report_return_code(tpm_startup(mode)); } static int do_tpm_nv_define_space(cmd_tbl_t *cmdtp, int flag, @@ -265,7 +270,7 @@ static int do_tpm_nv_define_space(cmd_tbl_t *cmdtp, int flag, perm = simple_strtoul(argv[2], NULL, 0); size = simple_strtoul(argv[3], NULL, 0); - return convert_return_code(tpm_nv_define_space(index, perm, size)); + return report_return_code(tpm_nv_define_space(index, perm, size)); } static int do_tpm_nv_read_value(cmd_tbl_t *cmdtp, int flag, @@ -286,7 +291,7 @@ static int do_tpm_nv_read_value(cmd_tbl_t *cmdtp, int flag, print_byte_string(data, count); } - return convert_return_code(rc); + return report_return_code(rc); } static int do_tpm_nv_write_value(cmd_tbl_t *cmdtp, int flag, @@ -308,7 +313,7 @@ static int do_tpm_nv_write_value(cmd_tbl_t *cmdtp, int flag, rc = tpm_nv_write_value(index, data, count); free(data); - return convert_return_code(rc); + return report_return_code(rc); } static int do_tpm_extend(cmd_tbl_t *cmdtp, int flag, @@ -331,7 +336,7 @@ static int do_tpm_extend(cmd_tbl_t *cmdtp, int flag, print_byte_string(out_digest, sizeof(out_digest)); } - return convert_return_code(rc); + return report_return_code(rc); } static int do_tpm_pcr_read(cmd_tbl_t *cmdtp, int flag, @@ -352,7 +357,7 @@ static int do_tpm_pcr_read(cmd_tbl_t *cmdtp, int flag, print_byte_string(data, count); } - return convert_return_code(rc); + return report_return_code(rc); } static int do_tpm_tsc_physical_presence(cmd_tbl_t *cmdtp, int flag, @@ -364,7 +369,7 @@ static int do_tpm_tsc_physical_presence(cmd_tbl_t *cmdtp, int flag, return CMD_RET_USAGE; presence = (uint16_t)simple_strtoul(argv[1], NULL, 0); - return convert_return_code(tpm_tsc_physical_presence(presence)); + return report_return_code(tpm_tsc_physical_presence(presence)); } static int do_tpm_read_pubek(cmd_tbl_t *cmdtp, int flag, @@ -384,7 +389,7 @@ static int do_tpm_read_pubek(cmd_tbl_t *cmdtp, int flag, print_byte_string(data, count); } - return convert_return_code(rc); + return report_return_code(rc); } static int do_tpm_physical_set_deactivated(cmd_tbl_t *cmdtp, int flag, @@ -396,7 +401,7 @@ static int do_tpm_physical_set_deactivated(cmd_tbl_t *cmdtp, int flag, return CMD_RET_USAGE; state = (uint8_t)simple_strtoul(argv[1], NULL, 0); - return convert_return_code(tpm_physical_set_deactivated(state)); + return report_return_code(tpm_physical_set_deactivated(state)); } static int do_tpm_get_capability(cmd_tbl_t *cmdtp, int flag, @@ -419,7 +424,7 @@ static int do_tpm_get_capability(cmd_tbl_t *cmdtp, int flag, print_byte_string(cap, count); } - return convert_return_code(rc); + return report_return_code(rc); } #define TPM_COMMAND_NO_ARG(cmd) \ @@ -428,7 +433,7 @@ static int do_##cmd(cmd_tbl_t *cmdtp, int flag, \ { \ if (argc != 1) \ return CMD_RET_USAGE; \ - return convert_return_code(cmd()); \ + return report_return_code(cmd()); \ } TPM_COMMAND_NO_ARG(tpm_init) @@ -438,6 +443,41 @@ 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; +} + +static int do_tpm_info(cmd_tbl_t *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + char buf[80]; + int rc; + + rc = get_tpm(&dev); + if (rc) + return rc; + rc = tpm_get_desc(dev, buf, sizeof(buf)); + if (rc < 0) { + printf("Couldn't get TPM info (%d)\n", rc); + return CMD_RET_FAILURE; + } + printf("%s\n", buf); + + return 0; +} +#endif + static int do_tpm_raw_transfer(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { @@ -452,14 +492,24 @@ 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"); print_byte_string(response, response_length); } - return convert_return_code(rc); + return report_return_code(rc); } static int do_tpm_nv_define(cmd_tbl_t *cmdtp, int flag, @@ -477,7 +527,7 @@ static int do_tpm_nv_define(cmd_tbl_t *cmdtp, int flag, index = simple_strtoul(argv[2], NULL, 0); perm = simple_strtoul(argv[3], NULL, 0); - return convert_return_code(tpm_nv_define_space(index, perm, size)); + return report_return_code(tpm_nv_define_space(index, perm, size)); } static int do_tpm_nv_read(cmd_tbl_t *cmdtp, int flag, @@ -506,7 +556,7 @@ static int do_tpm_nv_read(cmd_tbl_t *cmdtp, int flag, } free(data); - return convert_return_code(err); + return report_return_code(err); } static int do_tpm_nv_write(cmd_tbl_t *cmdtp, int flag, @@ -534,7 +584,7 @@ static int do_tpm_nv_write(cmd_tbl_t *cmdtp, int flag, err = tpm_nv_write_value(index, data, count); free(data); - return convert_return_code(err); + return report_return_code(err); } #ifdef CONFIG_TPM_AUTH_SESSIONS @@ -546,7 +596,7 @@ static int do_tpm_oiap(cmd_tbl_t *cmdtp, int flag, err = tpm_oiap(&auth_handle); - return convert_return_code(err); + return report_return_code(err); } static int do_tpm_load_key2_oiap(cmd_tbl_t *cmdtp, int flag, @@ -571,7 +621,7 @@ static int do_tpm_load_key2_oiap(cmd_tbl_t *cmdtp, int flag, if (!err) printf("Key handle is 0x%x\n", key_handle); - return convert_return_code(err); + return report_return_code(err); } static int do_tpm_get_pub_key_oiap(cmd_tbl_t *cmdtp, int flag, @@ -596,7 +646,7 @@ static int do_tpm_get_pub_key_oiap(cmd_tbl_t *cmdtp, int flag, printf("dump of received pub key structure:\n"); print_byte_string(pub_key_buffer, pub_key_len); } - return convert_return_code(err); + return report_return_code(err); } TPM_COMMAND_NO_ARG(tpm_end_oiap) @@ -607,6 +657,9 @@ TPM_COMMAND_NO_ARG(tpm_end_oiap) U_BOOT_CMD_MKENT(cmd, 0, 1, do_tpm_ ## cmd, "", "") static cmd_tbl_t tpm_commands[] = { +#ifdef CONFIG_DM_TPM + U_BOOT_CMD_MKENT(info, 0, 1, do_tpm_info, "", ""), +#endif U_BOOT_CMD_MKENT(init, 0, 1, do_tpm_init, "", ""), U_BOOT_CMD_MKENT(startup, 0, 1, @@ -677,6 +730,9 @@ U_BOOT_CMD(tpm, CONFIG_SYS_MAXARGS, 1, do_tpm, "cmd args...\n" " - Issue TPM command <cmd> with arguments <args...>.\n" "Admin Startup and State Commands:\n" +#ifdef CONFIG_DM_TPM +" info - Show information about the TPM\n" +#endif " init\n" " - Put TPM into a state where it waits for 'startup' command.\n" " startup mode\n" diff --git a/common/cmd_tpm_test.c b/common/cmd_tpm_test.c new file mode 100644 index 0000000000..65332d1117 --- /dev/null +++ b/common/cmd_tpm_test.c @@ -0,0 +1,564 @@ +/* + * Copyright (c) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <environment.h> +#include <tpm.h> + +/* Prints error and returns on failure */ +#define TPM_CHECK(tpm_command) do { \ + uint32_t result; \ + \ + result = (tpm_command); \ + if (result != TPM_SUCCESS) { \ + printf("TEST FAILED: line %d: " #tpm_command ": 0x%x\n", \ + __LINE__, result); \ + return result; \ + } \ +} while (0) + +#define INDEX0 0xda70 +#define INDEX1 0xda71 +#define INDEX2 0xda72 +#define INDEX3 0xda73 +#define INDEX_INITIALISED 0xda80 +#define PHYS_PRESENCE 4 +#define PRESENCE 8 + +static uint32_t TlclStartupIfNeeded(void) +{ + uint32_t result = tpm_startup(TPM_ST_CLEAR); + + return result == TPM_INVALID_POSTINIT ? TPM_SUCCESS : result; +} + +static int test_timer(void) +{ + printf("get_timer(0) = %lu\n", get_timer(0)); + return 0; +} + +static uint32_t tpm_get_flags(uint8_t *disable, uint8_t *deactivated, + uint8_t *nvlocked) +{ + struct tpm_permanent_flags pflags; + uint32_t result; + + result = tpm_get_permanent_flags(&pflags); + if (result) + return result; + if (disable) + *disable = pflags.disable; + if (deactivated) + *deactivated = pflags.deactivated; + if (nvlocked) + *nvlocked = pflags.nv_locked; + debug("TPM: Got flags disable=%d, deactivated=%d, nvlocked=%d\n", + pflags.disable, pflags.deactivated, pflags.nv_locked); + + return 0; +} + +static uint32_t tpm_set_global_lock(void) +{ + uint32_t x; + + debug("TPM: Set global lock\n"); + return tpm_nv_write_value(INDEX0, (uint8_t *)&x, 0); +} + +static uint32_t tpm_nv_write_value_lock(uint32_t index) +{ + debug("TPM: Write lock 0x%x\n", index); + + return tpm_nv_write_value(index, NULL, 0); +} + +static uint32_t tpm_nv_set_locked(void) +{ + debug("TPM: Set NV locked\n"); + + return tpm_nv_define_space(TPM_NV_INDEX_LOCK, 0, 0); +} + +static int tpm_is_owned(void) +{ + uint8_t response[TPM_PUBEK_SIZE]; + uint32_t result; + + result = tpm_read_pubek(response, sizeof(response)); + + return result != TPM_SUCCESS; +} + +static int test_early_extend(void) +{ + uint8_t value_in[20]; + uint8_t value_out[20]; + + printf("Testing earlyextend ..."); + tpm_init(); + TPM_CHECK(tpm_startup(TPM_ST_CLEAR)); + TPM_CHECK(tpm_continue_self_test()); + TPM_CHECK(tpm_extend(1, value_in, value_out)); + printf("done\n"); + return 0; +} + +static int test_early_nvram(void) +{ + uint32_t x; + + printf("Testing earlynvram ..."); + tpm_init(); + TPM_CHECK(tpm_startup(TPM_ST_CLEAR)); + TPM_CHECK(tpm_continue_self_test()); + TPM_CHECK(tpm_tsc_physical_presence(PRESENCE)); + TPM_CHECK(tpm_nv_read_value(INDEX0, (uint8_t *)&x, sizeof(x))); + printf("done\n"); + return 0; +} + +static int test_early_nvram2(void) +{ + uint32_t x; + + printf("Testing earlynvram2 ..."); + tpm_init(); + TPM_CHECK(tpm_startup(TPM_ST_CLEAR)); + TPM_CHECK(tpm_continue_self_test()); + TPM_CHECK(tpm_tsc_physical_presence(PRESENCE)); + TPM_CHECK(tpm_nv_write_value(INDEX0, (uint8_t *)&x, sizeof(x))); + printf("done\n"); + return 0; +} + +static int test_enable(void) +{ + uint8_t disable = 0, deactivated = 0; + + printf("Testing enable ...\n"); + tpm_init(); + TPM_CHECK(TlclStartupIfNeeded()); + TPM_CHECK(tpm_self_test_full()); + TPM_CHECK(tpm_tsc_physical_presence(PRESENCE)); + TPM_CHECK(tpm_get_flags(&disable, &deactivated, NULL)); + printf("\tdisable is %d, deactivated is %d\n", disable, deactivated); + TPM_CHECK(tpm_physical_enable()); + TPM_CHECK(tpm_physical_set_deactivated(0)); + TPM_CHECK(tpm_get_flags(&disable, &deactivated, NULL)); + printf("\tdisable is %d, deactivated is %d\n", disable, deactivated); + if (disable == 1 || deactivated == 1) + printf("\tfailed to enable or activate\n"); + printf("\tdone\n"); + return 0; +} + +#define reboot() do { \ + printf("\trebooting...\n"); \ + reset_cpu(0); \ +} while (0) + +static int test_fast_enable(void) +{ + uint8_t disable = 0, deactivated = 0; + int i; + + printf("Testing fastenable ...\n"); + tpm_init(); + TPM_CHECK(TlclStartupIfNeeded()); + TPM_CHECK(tpm_self_test_full()); + TPM_CHECK(tpm_tsc_physical_presence(PRESENCE)); + TPM_CHECK(tpm_get_flags(&disable, &deactivated, NULL)); + printf("\tdisable is %d, deactivated is %d\n", disable, deactivated); + for (i = 0; i < 2; i++) { + TPM_CHECK(tpm_force_clear()); + TPM_CHECK(tpm_get_flags(&disable, &deactivated, NULL)); + printf("\tdisable is %d, deactivated is %d\n", disable, + deactivated); + assert(disable == 1 && deactivated == 1); + TPM_CHECK(tpm_physical_enable()); + TPM_CHECK(tpm_physical_set_deactivated(0)); + TPM_CHECK(tpm_get_flags(&disable, &deactivated, NULL)); + printf("\tdisable is %d, deactivated is %d\n", disable, + deactivated); + assert(disable == 0 && deactivated == 0); + } + printf("\tdone\n"); + return 0; +} + +static int test_global_lock(void) +{ + uint32_t zero = 0; + uint32_t result; + uint32_t x; + + printf("Testing globallock ...\n"); + tpm_init(); + TPM_CHECK(TlclStartupIfNeeded()); + TPM_CHECK(tpm_self_test_full()); + TPM_CHECK(tpm_tsc_physical_presence(PRESENCE)); + TPM_CHECK(tpm_nv_read_value(INDEX0, (uint8_t *)&x, sizeof(x))); + TPM_CHECK(tpm_nv_write_value(INDEX0, (uint8_t *)&zero, + sizeof(uint32_t))); + TPM_CHECK(tpm_nv_read_value(INDEX1, (uint8_t *)&x, sizeof(x))); + TPM_CHECK(tpm_nv_write_value(INDEX1, (uint8_t *)&zero, + sizeof(uint32_t))); + TPM_CHECK(tpm_set_global_lock()); + /* Verifies that write to index0 fails */ + x = 1; + result = tpm_nv_write_value(INDEX0, (uint8_t *)&x, sizeof(x)); + assert(result == TPM_AREA_LOCKED); + TPM_CHECK(tpm_nv_read_value(INDEX0, (uint8_t *)&x, sizeof(x))); + assert(x == 0); + /* Verifies that write to index1 is still possible */ + x = 2; + TPM_CHECK(tpm_nv_write_value(INDEX1, (uint8_t *)&x, sizeof(x))); + TPM_CHECK(tpm_nv_read_value(INDEX1, (uint8_t *)&x, sizeof(x))); + assert(x == 2); + /* Turns off PP */ + tpm_tsc_physical_presence(PHYS_PRESENCE); + /* Verifies that write to index1 fails */ + x = 3; + result = tpm_nv_write_value(INDEX1, (uint8_t *)&x, sizeof(x)); + assert(result == TPM_BAD_PRESENCE); + TPM_CHECK(tpm_nv_read_value(INDEX1, (uint8_t *)&x, sizeof(x))); + assert(x == 2); + printf("\tdone\n"); + return 0; +} + +static int test_lock(void) +{ + printf("Testing lock ...\n"); + tpm_init(); + tpm_startup(TPM_ST_CLEAR); + tpm_self_test_full(); + tpm_tsc_physical_presence(PRESENCE); + tpm_nv_write_value_lock(INDEX0); + printf("\tLocked 0x%x\n", INDEX0); + printf("\tdone\n"); + return 0; +} + +static void initialise_spaces(void) +{ + uint32_t zero = 0; + uint32_t perm = TPM_NV_PER_WRITE_STCLEAR | TPM_NV_PER_PPWRITE; + + printf("\tInitialising spaces\n"); + tpm_nv_set_locked(); /* useful only the first time */ + tpm_nv_define_space(INDEX0, perm, 4); + tpm_nv_write_value(INDEX0, (uint8_t *)&zero, 4); + tpm_nv_define_space(INDEX1, perm, 4); + tpm_nv_write_value(INDEX1, (uint8_t *)&zero, 4); + tpm_nv_define_space(INDEX2, perm, 4); + tpm_nv_write_value(INDEX2, (uint8_t *)&zero, 4); + tpm_nv_define_space(INDEX3, perm, 4); + tpm_nv_write_value(INDEX3, (uint8_t *)&zero, 4); + perm = TPM_NV_PER_READ_STCLEAR | TPM_NV_PER_WRITE_STCLEAR | + TPM_NV_PER_PPWRITE; + tpm_nv_define_space(INDEX_INITIALISED, perm, 1); +} + +static int test_readonly(void) +{ + uint8_t c; + uint32_t index_0, index_1, index_2, index_3; + int read0, read1, read2, read3; + + printf("Testing readonly ...\n"); + tpm_init(); + tpm_startup(TPM_ST_CLEAR); + tpm_self_test_full(); + tpm_tsc_physical_presence(PRESENCE); + /* + * Checks if initialisation has completed by trying to read-lock a + * space that's created at the end of initialisation + */ + if (tpm_nv_read_value(INDEX_INITIALISED, &c, 0) == TPM_BADINDEX) { + /* The initialisation did not complete */ + initialise_spaces(); + } + + /* Checks if spaces are OK or messed up */ + read0 = tpm_nv_read_value(INDEX0, (uint8_t *)&index_0, sizeof(index_0)); + read1 = tpm_nv_read_value(INDEX1, (uint8_t *)&index_1, sizeof(index_1)); + read2 = tpm_nv_read_value(INDEX2, (uint8_t *)&index_2, sizeof(index_2)); + read3 = tpm_nv_read_value(INDEX3, (uint8_t *)&index_3, sizeof(index_3)); + if (read0 || read1 || read2 || read3) { + printf("Invalid contents\n"); + return 0; + } + + /* + * Writes space, and locks it. Then attempts to write again. + * I really wish I could use the imperative. + */ + index_0 += 1; + if (tpm_nv_write_value(INDEX0, (uint8_t *)&index_0, sizeof(index_0) != + TPM_SUCCESS)) { + error("\tcould not write index 0\n"); + } + tpm_nv_write_value_lock(INDEX0); + if (tpm_nv_write_value(INDEX0, (uint8_t *)&index_0, sizeof(index_0)) == + TPM_SUCCESS) + error("\tindex 0 is not locked\n"); + + printf("\tdone\n"); + return 0; +} + +static int test_redefine_unowned(void) +{ + uint32_t perm; + uint32_t result; + uint32_t x; + + printf("Testing redefine_unowned ..."); + tpm_init(); + TPM_CHECK(TlclStartupIfNeeded()); + TPM_CHECK(tpm_self_test_full()); + TPM_CHECK(tpm_tsc_physical_presence(PRESENCE)); + assert(!tpm_is_owned()); + + /* Ensures spaces exist. */ + TPM_CHECK(tpm_nv_read_value(INDEX0, (uint8_t *)&x, sizeof(x))); + TPM_CHECK(tpm_nv_read_value(INDEX1, (uint8_t *)&x, sizeof(x))); + + /* Redefines spaces a couple of times. */ + perm = TPM_NV_PER_PPWRITE | TPM_NV_PER_GLOBALLOCK; + TPM_CHECK(tpm_nv_define_space(INDEX0, perm, 2 * sizeof(uint32_t))); + TPM_CHECK(tpm_nv_define_space(INDEX0, perm, sizeof(uint32_t))); + perm = TPM_NV_PER_PPWRITE; + TPM_CHECK(tpm_nv_define_space(INDEX1, perm, 2 * sizeof(uint32_t))); + TPM_CHECK(tpm_nv_define_space(INDEX1, perm, sizeof(uint32_t))); + + /* Sets the global lock */ + tpm_set_global_lock(); + + /* Verifies that index0 cannot be redefined */ + result = tpm_nv_define_space(INDEX0, perm, sizeof(uint32_t)); + assert(result == TPM_AREA_LOCKED); + + /* Checks that index1 can */ + TPM_CHECK(tpm_nv_define_space(INDEX1, perm, 2 * sizeof(uint32_t))); + TPM_CHECK(tpm_nv_define_space(INDEX1, perm, sizeof(uint32_t))); + + /* Turns off PP */ + tpm_tsc_physical_presence(PHYS_PRESENCE); + + /* Verifies that neither index0 nor index1 can be redefined */ + result = tpm_nv_define_space(INDEX0, perm, sizeof(uint32_t)); + assert(result == TPM_BAD_PRESENCE); + result = tpm_nv_define_space(INDEX1, perm, sizeof(uint32_t)); + assert(result == TPM_BAD_PRESENCE); + + printf("done\n"); + return 0; +} + +#define PERMPPGL (TPM_NV_PER_PPWRITE | TPM_NV_PER_GLOBALLOCK) +#define PERMPP TPM_NV_PER_PPWRITE + +static int test_space_perm(void) +{ + uint32_t perm; + + printf("Testing spaceperm ..."); + tpm_init(); + TPM_CHECK(TlclStartupIfNeeded()); + TPM_CHECK(tpm_continue_self_test()); + TPM_CHECK(tpm_tsc_physical_presence(PRESENCE)); + TPM_CHECK(tpm_get_permissions(INDEX0, &perm)); + assert((perm & PERMPPGL) == PERMPPGL); + TPM_CHECK(tpm_get_permissions(INDEX1, &perm)); + assert((perm & PERMPP) == PERMPP); + printf("done\n"); + return 0; +} + +static int test_startup(void) +{ + uint32_t result; + printf("Testing startup ...\n"); + + tpm_init(); + result = tpm_startup(TPM_ST_CLEAR); + if (result != 0 && result != TPM_INVALID_POSTINIT) + printf("\ttpm startup failed with 0x%x\n", result); + result = tpm_get_flags(NULL, NULL, NULL); + if (result != 0) + printf("\ttpm getflags failed with 0x%x\n", result); + printf("\texecuting SelfTestFull\n"); + tpm_self_test_full(); + result = tpm_get_flags(NULL, NULL, NULL); + if (result != 0) + printf("\ttpm getflags failed with 0x%x\n", result); + printf("\tdone\n"); + return 0; +} + +/* + * Runs [op] and ensures it returns success and doesn't run longer than + * [time_limit] in milliseconds. + */ +#define TTPM_CHECK(op, time_limit) do { \ + ulong start, time; \ + uint32_t __result; \ + \ + start = get_timer(0); \ + __result = op; \ + if (__result != TPM_SUCCESS) { \ + printf("\t" #op ": error 0x%x\n", __result); \ + return -1; \ + } \ + time = get_timer(start); \ + printf("\t" #op ": %lu ms\n", time); \ + if (time > (ulong)time_limit) { \ + printf("\t" #op " exceeded " #time_limit " ms\n"); \ + } \ +} while (0) + + +static int test_timing(void) +{ + uint32_t x; + uint8_t in[20], out[20]; + + printf("Testing timing ..."); + tpm_init(); + TTPM_CHECK(TlclStartupIfNeeded(), 50); + TTPM_CHECK(tpm_continue_self_test(), 100); + TTPM_CHECK(tpm_self_test_full(), 1000); + TTPM_CHECK(tpm_tsc_physical_presence(PRESENCE), 100); + TTPM_CHECK(tpm_nv_write_value(INDEX0, (uint8_t *)&x, sizeof(x)), 100); + TTPM_CHECK(tpm_nv_read_value(INDEX0, (uint8_t *)&x, sizeof(x)), 100); + TTPM_CHECK(tpm_extend(0, in, out), 200); + TTPM_CHECK(tpm_set_global_lock(), 50); + TTPM_CHECK(tpm_tsc_physical_presence(PHYS_PRESENCE), 100); + printf("done\n"); + return 0; +} + +#define TPM_MAX_NV_WRITES_NOOWNER 64 + +static int test_write_limit(void) +{ + printf("Testing writelimit ...\n"); + int i; + uint32_t result; + + tpm_init(); + TPM_CHECK(TlclStartupIfNeeded()); + TPM_CHECK(tpm_self_test_full()); + TPM_CHECK(tpm_tsc_physical_presence(PRESENCE)); + TPM_CHECK(tpm_force_clear()); + TPM_CHECK(tpm_physical_enable()); + TPM_CHECK(tpm_physical_set_deactivated(0)); + + for (i = 0; i < TPM_MAX_NV_WRITES_NOOWNER + 2; i++) { + printf("\twriting %d\n", i); + result = tpm_nv_write_value(INDEX0, (uint8_t *)&i, sizeof(i)); + switch (result) { + case TPM_SUCCESS: + break; + case TPM_MAXNVWRITES: + assert(i >= TPM_MAX_NV_WRITES_NOOWNER); + default: + error("\tunexpected error code %d (0x%x)\n", + result, result); + } + } + + /* Reset write count */ + TPM_CHECK(tpm_force_clear()); + TPM_CHECK(tpm_physical_enable()); + TPM_CHECK(tpm_physical_set_deactivated(0)); + + /* Try writing again. */ + TPM_CHECK(tpm_nv_write_value(INDEX0, (uint8_t *)&i, sizeof(i))); + printf("\tdone\n"); + return 0; +} + +#define VOIDTEST(XFUNC) \ + int do_test_##XFUNC(cmd_tbl_t *cmd_tbl, int flag, int argc, \ + char * const argv[]) \ + { \ + return test_##XFUNC(); \ + } + +#define VOIDENT(XNAME) \ + U_BOOT_CMD_MKENT(XNAME, 0, 1, do_test_##XNAME, "", ""), + +VOIDTEST(early_extend) +VOIDTEST(early_nvram) +VOIDTEST(early_nvram2) +VOIDTEST(enable) +VOIDTEST(fast_enable) +VOIDTEST(global_lock) +VOIDTEST(lock) +VOIDTEST(readonly) +VOIDTEST(redefine_unowned) +VOIDTEST(space_perm) +VOIDTEST(startup) +VOIDTEST(timing) +VOIDTEST(write_limit) +VOIDTEST(timer) + +static cmd_tbl_t cmd_cros_tpm_sub[] = { + VOIDENT(early_extend) + VOIDENT(early_nvram) + VOIDENT(early_nvram2) + VOIDENT(enable) + VOIDENT(fast_enable) + VOIDENT(global_lock) + VOIDENT(lock) + VOIDENT(readonly) + VOIDENT(redefine_unowned) + VOIDENT(space_perm) + VOIDENT(startup) + VOIDENT(timing) + VOIDENT(write_limit) + VOIDENT(timer) +}; + +static int do_tpmtest(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + cmd_tbl_t *c; + + printf("argc = %d, argv = ", argc); + do { + int i = 0; + + for (i = 0; i < argc; i++) + printf(" %s", argv[i]); + printf("\n------\n"); + } while (0); + argc--; + argv++; + c = find_cmd_tbl(argv[0], cmd_cros_tpm_sub, + ARRAY_SIZE(cmd_cros_tpm_sub)); + return c ? c->cmd(cmdtp, flag, argc, argv) : cmd_usage(cmdtp); +} + +U_BOOT_CMD(tpmtest, 2, 1, do_tpmtest, "TPM tests", + "\n\tearly_extend\n" + "\tearly_nvram\n" + "\tearly_nvram2\n" + "\tenable\n" + "\tfast_enable\n" + "\tglobal_lock\n" + "\tlock\n" + "\treadonly\n" + "\tredefine_unowned\n" + "\tspace_perm\n" + "\tstartup\n" + "\ttiming\n" + "\twrite_limit\n"); diff --git a/configs/chromebook_link_defconfig b/configs/chromebook_link_defconfig index b987f3f52f..985573667a 100644 --- a/configs/chromebook_link_defconfig +++ b/configs/chromebook_link_defconfig @@ -11,15 +11,20 @@ CONFIG_HAVE_VGA_BIOS=y CONFIG_BOOTSTAGE=y CONFIG_BOOTSTAGE_REPORT=y CONFIG_CMD_BOOTSTAGE=y +CONFIG_CMD_TPM=y +CONFIG_CMD_TPM_TEST=y CONFIG_OF_CONTROL=y CONFIG_DM_PCI=y CONFIG_SPI_FLASH=y CONFIG_CMD_CROS_EC=y CONFIG_CROS_EC=y CONFIG_CROS_EC_LPC=y +CONFIG_DM_TPM=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 e82c8ecf64..c75b20d24d 100644 --- a/configs/chromebox_panther_defconfig +++ b/configs/chromebox_panther_defconfig @@ -11,15 +11,20 @@ CONFIG_HAVE_VGA_BIOS=y CONFIG_BOOTSTAGE=y CONFIG_BOOTSTAGE_REPORT=y CONFIG_CMD_BOOTSTAGE=y +CONFIG_CMD_TPM=y +CONFIG_CMD_TPM_TEST=y CONFIG_OF_CONTROL=y CONFIG_DM_PCI=y CONFIG_SPI_FLASH=y CONFIG_CMD_CROS_EC=y CONFIG_CROS_EC=y CONFIG_CROS_EC_LPC=y +CONFIG_DM_TPM=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 37ead03a43..c8ab862edc 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 7166edbf9d..21c0eab53d 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 d99fcd4513..c3a0920d7c 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 3557aea1eb..08aae9ae2d 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 df3a624ecd..ebaf86bc7f 100644 --- a/configs/coreboot-x86_defconfig +++ b/configs/coreboot-x86_defconfig @@ -9,10 +9,15 @@ CONFIG_TSC_CALIBRATION_BYPASS=y CONFIG_BOOTSTAGE=y CONFIG_BOOTSTAGE_REPORT=y CONFIG_CMD_BOOTSTAGE=y +CONFIG_CMD_TPM=y +CONFIG_CMD_TPM_TEST=y CONFIG_OF_CONTROL=y CONFIG_DM_PCI=y CONFIG_SPI_FLASH=y CONFIG_NETDEVICES=y CONFIG_E1000=y +CONFIG_DM_TPM=y +CONFIG_TPM_TIS_LPC=y CONFIG_USE_PRIVATE_LIBGCC=y CONFIG_SYS_VSNPRINTF=y +CONFIG_TPM=y diff --git a/configs/nyan-big_defconfig b/configs/nyan-big_defconfig index 274e955a37..6464c379a4 100644 --- a/configs/nyan-big_defconfig +++ b/configs/nyan-big_defconfig @@ -9,16 +9,21 @@ CONFIG_DEFAULT_DEVICE_TREE="tegra124-nyan-big" # CONFIG_CMD_FPGA is not set # CONFIG_CMD_SETEXPR is not set # CONFIG_CMD_NFS is not set +CONFIG_CMD_TPM=y +CONFIG_CMD_TPM_TEST=y CONFIG_SPL_DM=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_DM_TPM=y +CONFIG_TPM_TIS_I2C=y CONFIG_TEGRA114_SPI=y CONFIG_DISPLAY_PORT=y CONFIG_VIDEO_TEGRA124=y CONFIG_USB=y CONFIG_DM_USB=y +CONFIG_TPM=y CONFIG_USE_PRIVATE_LIBGCC=y CONFIG_SYS_PROMPT="Tegra124 (Nyan-big) # " diff --git a/configs/peach-pi_defconfig b/configs/peach-pi_defconfig index 323a2d38b3..56a518556c 100644 --- a/configs/peach-pi_defconfig +++ b/configs/peach-pi_defconfig @@ -7,11 +7,15 @@ CONFIG_SPL=y # CONFIG_CMD_SETEXPR is not set CONFIG_CMD_PMIC=y CONFIG_CMD_REGULATOR=y +CONFIG_CMD_TPM=y +CONFIG_CMD_TPM_TEST=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_DM_TPM=y +CONFIG_TPM_TIS_I2C=y CONFIG_DM_I2C=y CONFIG_DM_I2C_COMPAT=y CONFIG_I2C_CROS_EC_TUNNEL=y @@ -30,5 +34,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 6a082968b9..1934bf38a1 100644 --- a/configs/peach-pit_defconfig +++ b/configs/peach-pit_defconfig @@ -7,11 +7,15 @@ CONFIG_SPL=y # CONFIG_CMD_SETEXPR is not set CONFIG_CMD_PMIC=y CONFIG_CMD_REGULATOR=y +CONFIG_CMD_TPM=y +CONFIG_CMD_TPM_TEST=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_DM_TPM=y +CONFIG_TPM_TIS_I2C=y CONFIG_DM_I2C=y CONFIG_DM_I2C_COMPAT=y CONFIG_I2C_CROS_EC_TUNNEL=y @@ -30,5 +34,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 85ff95df79..e9e1597f9a 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -11,6 +11,8 @@ CONFIG_BOOTSTAGE=y CONFIG_BOOTSTAGE_REPORT=y CONFIG_CMD_PMIC=y CONFIG_CMD_REGULATOR=y +CONFIG_CMD_TPM=y +CONFIG_CMD_TPM_TEST=y CONFIG_OF_CONTROL=y CONFIG_OF_HOSTFILE=y CONFIG_CLK=y @@ -30,9 +32,13 @@ CONFIG_CROS_EC_KEYB=y CONFIG_LED=y CONFIG_LED_GPIO=y CONFIG_SANDBOX_SERIAL=y +CONFIG_DM_TPM=y CONFIG_TPM_TIS_SANDBOX=y CONFIG_SYS_I2C_SANDBOX=y CONFIG_SANDBOX_SPI=y +CONFIG_PINCTRL=y +CONFIG_PINCONF=y +CONFIG_PINCTRL_SANDBOX=y CONFIG_SANDBOX_GPIO=y CONFIG_DM_PMIC=y CONFIG_DM_PMIC_SANDBOX=y @@ -49,6 +55,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 f5decd5c69..32c7c5deab 100644 --- a/configs/snow_defconfig +++ b/configs/snow_defconfig @@ -8,6 +8,8 @@ CONFIG_SPL=y CONFIG_CMD_SOUND=y CONFIG_CMD_PMIC=y CONFIG_CMD_REGULATOR=y +CONFIG_CMD_TPM=y +CONFIG_CMD_TPM_TEST=y CONFIG_SPI_FLASH=y CONFIG_CMD_CROS_EC=y CONFIG_CROS_EC=y @@ -17,6 +19,8 @@ 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 CONFIG_I2C_CROS_EC_LDO=y @@ -41,4 +45,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 f1d9a58ad1..b20bfed096 100644 --- a/configs/spring_defconfig +++ b/configs/spring_defconfig @@ -8,6 +8,8 @@ CONFIG_SPL=y CONFIG_CMD_SOUND=y CONFIG_CMD_PMIC=y CONFIG_CMD_REGULATOR=y +CONFIG_CMD_TPM=y +CONFIG_CMD_TPM_TEST=y CONFIG_SPI_FLASH=y CONFIG_CMD_CROS_EC=y CONFIG_CROS_EC=y @@ -17,6 +19,8 @@ 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 CONFIG_I2C_CROS_EC_LDO=y @@ -40,4 +44,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/drivers/Kconfig b/drivers/Kconfig index 092bc02b30..63c92c594a 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -1,66 +1,68 @@ menu "Device Drivers" -source "drivers/clk/Kconfig" - source "drivers/core/Kconfig" +# types of drivers sorted in alphabetical order + +source "drivers/block/Kconfig" + +source "drivers/clk/Kconfig" + source "drivers/cpu/Kconfig" -source "drivers/demo/Kconfig" +source "drivers/crypto/Kconfig" -source "drivers/pci/Kconfig" +source "drivers/demo/Kconfig" -source "drivers/pcmcia/Kconfig" +source "drivers/dfu/Kconfig" -source "drivers/mtd/Kconfig" +source "drivers/dma/Kconfig" -source "drivers/block/Kconfig" +source "drivers/gpio/Kconfig" -source "drivers/misc/Kconfig" +source "drivers/hwmon/Kconfig" -source "drivers/net/Kconfig" +source "drivers/i2c/Kconfig" source "drivers/input/Kconfig" source "drivers/led/Kconfig" -source "drivers/serial/Kconfig" +source "drivers/misc/Kconfig" -source "drivers/tpm/Kconfig" +source "drivers/mmc/Kconfig" -source "drivers/i2c/Kconfig" +source "drivers/mtd/Kconfig" -source "drivers/spi/Kconfig" +source "drivers/net/Kconfig" -source "drivers/gpio/Kconfig" +source "drivers/pci/Kconfig" + +source "drivers/pcmcia/Kconfig" + +source "drivers/pinctrl/Kconfig" source "drivers/power/Kconfig" source "drivers/ram/Kconfig" -source "drivers/hwmon/Kconfig" - -source "drivers/watchdog/Kconfig" +source "drivers/rtc/Kconfig" -source "drivers/video/Kconfig" +source "drivers/serial/Kconfig" source "drivers/sound/Kconfig" -source "drivers/usb/Kconfig" - -source "drivers/dfu/Kconfig" - -source "drivers/mmc/Kconfig" +source "drivers/spi/Kconfig" -source "drivers/rtc/Kconfig" +source "drivers/thermal/Kconfig" -source "drivers/dma/Kconfig" +source "drivers/tpm/Kconfig" -source "drivers/crypto/Kconfig" +source "drivers/usb/Kconfig" -source "drivers/thermal/Kconfig" +source "drivers/video/Kconfig" -endmenu +source "drivers/watchdog/Kconfig" config PHYS_TO_BUS bool "Custom physical to bus address mapping" @@ -69,3 +71,5 @@ config PHYS_TO_BUS peripheral DMA master accesses. If yours does, select this option in your platform's Kconfig, and implement the appropriate mapping functions in your platform's support code. + +endmenu diff --git a/drivers/Makefile b/drivers/Makefile index a721ec86df..9d0a5959a8 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/Kconfig b/drivers/core/Kconfig index 788f8b739b..41f4e695e8 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -105,4 +105,19 @@ config DEBUG_DEVRES If you are unsure about this, Say N here. +config SIMPLE_BUS + bool "Support simple-bus driver" + depends on DM && OF_CONTROL + default y + help + Supports the 'simple-bus' driver, which is used on some systems. + +config SPL_SIMPLE_BUS + bool "Support simple-bus driver in SPL" + depends on SPL_DM && SPL_OF_CONTROL + default n + help + Supports the 'simple-bus' driver, which is used on some systems + in SPL. + endmenu diff --git a/drivers/core/Makefile b/drivers/core/Makefile index 11e0276e56..f19f67d30f 100644 --- a/drivers/core/Makefile +++ b/drivers/core/Makefile @@ -6,10 +6,8 @@ obj-y += device.o lists.o root.o uclass.o util.o obj-$(CONFIG_DEVRES) += devres.o -ifndef CONFIG_SPL_BUILD -obj-$(CONFIG_$(SPL_)OF_CONTROL) += simple-bus.o -endif obj-$(CONFIG_$(SPL_)DM_DEVICE_REMOVE) += device-remove.o +obj-$(CONFIG_$(SPL_)SIMPLE_BUS) += simple-bus.o obj-$(CONFIG_DM) += dump.o obj-$(CONFIG_REGMAP) += regmap.o obj-$(CONFIG_SYSCON) += syscon-uclass.o diff --git a/drivers/core/device.c b/drivers/core/device.c index a31e25f6b5..a6cd93698f 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -15,6 +15,7 @@ #include <dm/device.h> #include <dm/device-internal.h> #include <dm/lists.h> +#include <dm/pinctrl.h> #include <dm/platdata.h> #include <dm/uclass.h> #include <dm/uclass-internal.h> @@ -32,7 +33,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 +135,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; @@ -284,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; @@ -574,7 +580,7 @@ fdt_addr_t dev_get_addr(struct udevice *dev) fdt_addr_t addr; addr = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg"); - if (addr != FDT_ADDR_T_NONE) { + if (CONFIG_IS_ENABLED(SIMPLE_BUS) && addr != FDT_ADDR_T_NONE) { if (device_get_uclass_id(dev->parent) == UCLASS_SIMPLE_BUS) addr = simple_bus_translate(dev->parent, addr); } diff --git a/drivers/gpio/s5p_gpio.c b/drivers/gpio/s5p_gpio.c index 49b1054660..17fcfbf4d3 100644 --- a/drivers/gpio/s5p_gpio.c +++ b/drivers/gpio/s5p_gpio.c @@ -327,8 +327,7 @@ static int gpio_exynos_bind(struct udevice *parent) if (plat) return 0; - base = (struct s5p_gpio_bank *)fdtdec_get_addr(gd->fdt_blob, - parent->of_offset, "reg"); + base = (struct s5p_gpio_bank *)dev_get_addr(parent); for (node = fdt_first_subnode(blob, parent->of_offset), bank = base; node > 0; node = fdt_next_subnode(blob, node), bank++) { diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c index 57b78e55e2..9d8f11ef30 100644 --- a/drivers/gpio/sunxi_gpio.c +++ b/drivers/gpio/sunxi_gpio.c @@ -285,8 +285,7 @@ static int gpio_sunxi_bind(struct udevice *parent) no_banks = SUNXI_GPIO_BANKS; } - ctlr = (struct sunxi_gpio_reg *)fdtdec_get_addr(gd->fdt_blob, - parent->of_offset, "reg"); + ctlr = (struct sunxi_gpio_reg *)dev_get_addr(parent); for (bank = 0; bank < no_banks; bank++) { struct sunxi_gpio_platdata *plat; struct udevice *dev; diff --git a/drivers/gpio/tegra_gpio.c b/drivers/gpio/tegra_gpio.c index 8017e359f5..4921f0ff42 100644 --- a/drivers/gpio/tegra_gpio.c +++ b/drivers/gpio/tegra_gpio.c @@ -343,8 +343,7 @@ static int gpio_tegra_bind(struct udevice *parent) if (!fdt_getprop(gd->fdt_blob, parent->of_offset, "interrupts", &len)) return -EINVAL; bank_count = len / 3 / sizeof(u32); - ctlr = (struct gpio_ctlr *)fdtdec_get_addr(gd->fdt_blob, - parent->of_offset, "reg"); + ctlr = (struct gpio_ctlr *)dev_get_addr(parent); } #endif for (bank = 0; bank < bank_count; bank++) { diff --git a/drivers/i2c/s3c24x0_i2c.c b/drivers/i2c/s3c24x0_i2c.c index ae6f436385..dc9b661c1c 100644 --- a/drivers/i2c/s3c24x0_i2c.c +++ b/drivers/i2c/s3c24x0_i2c.c @@ -1397,12 +1397,10 @@ static int s3c_i2c_ofdata_to_platdata(struct udevice *dev) if (i2c_bus->is_highspeed) { flags = PINMUX_FLAG_HS_MODE; - i2c_bus->hsregs = (struct exynos5_hsi2c *) - fdtdec_get_addr(blob, node, "reg"); + i2c_bus->hsregs = (struct exynos5_hsi2c *)dev_get_addr(dev); } else { flags = 0; - i2c_bus->regs = (struct s3c24x0_i2c *) - fdtdec_get_addr(blob, node, "reg"); + i2c_bus->regs = (struct s3c24x0_i2c *)dev_get_addr(dev); } i2c_bus->id = pinmux_decode_periph_id(blob, node); diff --git a/drivers/i2c/tegra_i2c.c b/drivers/i2c/tegra_i2c.c index a4289788a6..2fa07f9c57 100644 --- a/drivers/i2c/tegra_i2c.c +++ b/drivers/i2c/tegra_i2c.c @@ -339,7 +339,7 @@ static int tegra_i2c_probe(struct udevice *dev) i2c_bus->id = dev->seq; i2c_bus->type = dev_get_driver_data(dev); - i2c_bus->regs = (struct i2c_ctlr *)fdtdec_get_addr(blob, node, "reg"); + i2c_bus->regs = (struct i2c_ctlr *)dev_get_addr(dev); /* * We don't have a binding for pinmux yet. Leave it out for now. So diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig new file mode 100644 index 0000000000..30b8e452ef --- /dev/null +++ b/drivers/pinctrl/Kconfig @@ -0,0 +1,109 @@ +# +# 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 + +config PINCTRL_SANDBOX + bool "Sandbox pinctrl driver" + depends on SANDBOX + help + This enables pinctrl driver for sandbox. Currently, this driver + actually does nothing but print debug messages when pinctrl + operations are invoked. + +endif + +endmenu diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile new file mode 100644 index 0000000000..35decf49c3 --- /dev/null +++ b/drivers/pinctrl/Makefile @@ -0,0 +1,4 @@ +obj-y += pinctrl-uclass.o +obj-$(CONFIG_$(SPL_)PINCTRL_GENERIC) += pinctrl-generic.o + +obj-$(CONFIG_PINCTRL_SANDBOX) += pinctrl-sandbox.o diff --git a/drivers/pinctrl/pinctrl-generic.c b/drivers/pinctrl/pinctrl-generic.c new file mode 100644 index 0000000000..e86b72a8de --- /dev/null +++ b/drivers/pinctrl/pinctrl-generic.c @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <linux/compat.h> +#include <dm/device.h> +#include <dm/pinctrl.h> + +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-sandbox.c b/drivers/pinctrl/pinctrl-sandbox.c new file mode 100644 index 0000000000..ab03d8bad4 --- /dev/null +++ b/drivers/pinctrl/pinctrl-sandbox.c @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* #define DEBUG */ + +#include <common.h> +#include <dm/device.h> +#include <dm/pinctrl.h> + +static const char * const sandbox_pins[] = { + "SCL", + "SDA", + "TX", + "RX", +}; + +static const char * const sandbox_groups[] = { + "i2c", + "serial_a", + "serial_b", + "spi", +}; + +static const char * const sandbox_functions[] = { + "i2c", + "serial", + "spi", +}; + +static const struct pinconf_param sandbox_conf_params[] = { + { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 }, + { "bias-high-impedance", PIN_CONFIG_BIAS_HIGH_IMPEDANCE, 0 }, + { "bias-bus-hold", PIN_CONFIG_BIAS_BUS_HOLD, 0 }, + { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 }, + { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 }, + { "bias-pull-pin-default", PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 1 }, + { "drive-open-drain", PIN_CONFIG_DRIVE_OPEN_DRAIN, 0 }, + { "drive-open-source", PIN_CONFIG_DRIVE_OPEN_SOURCE, 0 }, + { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 }, + { "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 }, + { "input-disable", PIN_CONFIG_INPUT_ENABLE, 0 }, +}; + +static int sandbox_get_pins_count(struct udevice *dev) +{ + return ARRAY_SIZE(sandbox_pins); +} + +static const char *sandbox_get_pin_name(struct udevice *dev, unsigned selector) +{ + return sandbox_pins[selector]; +} + +static int sandbox_get_groups_count(struct udevice *dev) +{ + return ARRAY_SIZE(sandbox_groups); +} + +static const char *sandbox_get_group_name(struct udevice *dev, + unsigned selector) +{ + return sandbox_groups[selector]; +} + +static int sandbox_get_functions_count(struct udevice *dev) +{ + return ARRAY_SIZE(sandbox_functions); +} + +static const char *sandbox_get_function_name(struct udevice *dev, + unsigned selector) +{ + return sandbox_functions[selector]; +} + +static int sandbox_pinmux_set(struct udevice *dev, unsigned pin_selector, + unsigned func_selector) +{ + debug("sandbox pinmux: pin = %d (%s), function = %d (%s)\n", + pin_selector, sandbox_get_pin_name(dev, pin_selector), + func_selector, sandbox_get_function_name(dev, func_selector)); + + return 0; +} + +static int sandbox_pinmux_group_set(struct udevice *dev, + unsigned group_selector, + unsigned func_selector) +{ + debug("sandbox pinmux: group = %d (%s), function = %d (%s)\n", + group_selector, sandbox_get_group_name(dev, group_selector), + func_selector, sandbox_get_function_name(dev, func_selector)); + + return 0; +} + +static int sandbox_pinconf_set(struct udevice *dev, unsigned pin_selector, + unsigned param, unsigned argument) +{ + debug("sandbox pinconf: pin = %d (%s), param = %d, arg = %d\n", + pin_selector, sandbox_get_pin_name(dev, pin_selector), + param, argument); + + return 0; +} + +static int sandbox_pinconf_group_set(struct udevice *dev, + unsigned group_selector, + unsigned param, unsigned argument) +{ + debug("sandbox pinconf: group = %d (%s), param = %d, arg = %d\n", + group_selector, sandbox_get_group_name(dev, group_selector), + param, argument); + + return 0; +} + +const struct pinctrl_ops sandbox_pinctrl_ops = { + .get_pins_count = sandbox_get_pins_count, + .get_pin_name = sandbox_get_pin_name, + .get_groups_count = sandbox_get_groups_count, + .get_group_name = sandbox_get_group_name, + .get_functions_count = sandbox_get_functions_count, + .get_function_name = sandbox_get_function_name, + .pinmux_set = sandbox_pinmux_set, + .pinmux_group_set = sandbox_pinmux_group_set, + .pinconf_num_params = ARRAY_SIZE(sandbox_conf_params), + .pinconf_params = sandbox_conf_params, + .pinconf_set = sandbox_pinconf_set, + .pinconf_group_set = sandbox_pinconf_group_set, + .set_state = pinctrl_generic_set_state, +}; + +static const struct udevice_id sandbox_pinctrl_match[] = { + { .compatible = "sandbox,pinctrl" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(sandbox_pinctrl) = { + .name = "sandbox_pinctrl", + .id = UCLASS_PINCTRL, + .of_match = sandbox_pinctrl_match, + .ops = &sandbox_pinctrl_ops, +}; diff --git a/drivers/pinctrl/pinctrl-uclass.c b/drivers/pinctrl/pinctrl-uclass.c new file mode 100644 index 0000000000..d96c201e83 --- /dev/null +++ b/drivers/pinctrl/pinctrl-uclass.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <libfdt.h> +#include <linux/err.h> +#include <linux/list.h> +#include <dm/device.h> +#include <dm/lists.h> +#include <dm/pinctrl.h> +#include <dm/uclass.h> + +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/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index 2b6d1e4638..6275a11a0c 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -364,7 +364,7 @@ int ns16550_serial_ofdata_to_platdata(struct udevice *dev) fdt_addr_t addr; /* try Processor Local Bus device first */ - addr = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg"); + addr = dev_get_addr(dev); #ifdef CONFIG_PCI if (addr == FDT_ADDR_T_NONE) { /* then try pci device */ diff --git a/drivers/serial/serial_arc.c b/drivers/serial/serial_arc.c index 54e596c4ed..7dbb49f814 100644 --- a/drivers/serial/serial_arc.c +++ b/drivers/serial/serial_arc.c @@ -133,8 +133,7 @@ static int arc_serial_ofdata_to_platdata(struct udevice *dev) struct arc_serial_platdata *plat = dev_get_platdata(dev); DECLARE_GLOBAL_DATA_PTR; - plat->reg = (struct arc_serial_regs *)fdtdec_get_addr(gd->fdt_blob, - dev->of_offset, "reg"); + plat->reg = (struct arc_serial_regs *)dev_get_addr(dev); plat->uartclk = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "clock-frequency", 0); diff --git a/drivers/serial/serial_pl01x.c b/drivers/serial/serial_pl01x.c index 917b6034d3..ecf3bc0240 100644 --- a/drivers/serial/serial_pl01x.c +++ b/drivers/serial/serial_pl01x.c @@ -365,7 +365,7 @@ static int pl01x_serial_ofdata_to_platdata(struct udevice *dev) struct pl01x_serial_platdata *plat = dev_get_platdata(dev); fdt_addr_t addr; - addr = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg"); + addr = dev_get_addr(dev); if (addr == FDT_ADDR_T_NONE) return -EINVAL; diff --git a/drivers/serial/serial_s5p.c b/drivers/serial/serial_s5p.c index 21cb566c29..3f0b588254 100644 --- a/drivers/serial/serial_s5p.c +++ b/drivers/serial/serial_s5p.c @@ -169,7 +169,7 @@ static int s5p_serial_ofdata_to_platdata(struct udevice *dev) struct s5p_serial_platdata *plat = dev->platdata; fdt_addr_t addr; - addr = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg"); + addr = dev_get_addr(dev); if (addr == FDT_ADDR_T_NONE) return -EINVAL; diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c index 8f5c0fc802..86ee90f4be 100644 --- a/drivers/spi/designware_spi.c +++ b/drivers/spi/designware_spi.c @@ -134,7 +134,7 @@ static int dw_spi_ofdata_to_platdata(struct udevice *bus) const void *blob = gd->fdt_blob; int node = bus->of_offset; - plat->regs = (struct dw_spi *)fdtdec_get_addr(blob, node, "reg"); + plat->regs = (struct dw_spi *)dev_get_addr(bus); /* Use 500KHz as a suitable default */ plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", diff --git a/drivers/spi/exynos_spi.c b/drivers/spi/exynos_spi.c index 418b48120a..44948c3736 100644 --- a/drivers/spi/exynos_spi.c +++ b/drivers/spi/exynos_spi.c @@ -255,7 +255,7 @@ static int exynos_spi_ofdata_to_platdata(struct udevice *bus) const void *blob = gd->fdt_blob; int node = bus->of_offset; - plat->regs = (struct exynos_spi *)fdtdec_get_addr(blob, node, "reg"); + plat->regs = (struct exynos_spi *)dev_get_addr(bus); plat->periph_id = pinmux_decode_periph_id(blob, node); if (plat->periph_id == PERIPH_ID_NONE) { diff --git a/drivers/spi/fsl_dspi.c b/drivers/spi/fsl_dspi.c index 3881b2e8a5..887edd801a 100644 --- a/drivers/spi/fsl_dspi.c +++ b/drivers/spi/fsl_dspi.c @@ -654,7 +654,7 @@ static int fsl_dspi_ofdata_to_platdata(struct udevice *bus) plat->num_chipselect = fdtdec_get_int(blob, node, "num-cs", FSL_DSPI_MAX_CHIPSELECT); - addr = fdtdec_get_addr(blob, node, "reg"); + addr = dev_get_addr(bus); if (addr == FDT_ADDR_T_NONE) { debug("DSPI: Can't get base address or size\n"); return -ENOMEM; diff --git a/drivers/spi/tegra114_spi.c b/drivers/spi/tegra114_spi.c index d7eecd5bc6..a965f80aeb 100644 --- a/drivers/spi/tegra114_spi.c +++ b/drivers/spi/tegra114_spi.c @@ -118,7 +118,7 @@ static int tegra114_spi_ofdata_to_platdata(struct udevice *bus) const void *blob = gd->fdt_blob; int node = bus->of_offset; - plat->base = fdtdec_get_addr(blob, node, "reg"); + plat->base = dev_get_addr(bus); plat->periph_id = clock_decode_periph_id(blob, node); if (plat->periph_id == PERIPH_ID_NONE) { diff --git a/drivers/spi/tegra20_sflash.c b/drivers/spi/tegra20_sflash.c index 82c1b84f3b..afa0848dcb 100644 --- a/drivers/spi/tegra20_sflash.c +++ b/drivers/spi/tegra20_sflash.c @@ -90,7 +90,7 @@ static int tegra20_sflash_ofdata_to_platdata(struct udevice *bus) const void *blob = gd->fdt_blob; int node = bus->of_offset; - plat->base = fdtdec_get_addr(blob, node, "reg"); + plat->base = dev_get_addr(bus); plat->periph_id = clock_decode_periph_id(blob, node); if (plat->periph_id == PERIPH_ID_NONE) { diff --git a/drivers/spi/tegra20_slink.c b/drivers/spi/tegra20_slink.c index f6fb89b393..fbb665b86f 100644 --- a/drivers/spi/tegra20_slink.c +++ b/drivers/spi/tegra20_slink.c @@ -106,7 +106,7 @@ static int tegra30_spi_ofdata_to_platdata(struct udevice *bus) const void *blob = gd->fdt_blob; int node = bus->of_offset; - plat->base = fdtdec_get_addr(blob, node, "reg"); + plat->base = dev_get_addr(bus); plat->periph_id = clock_decode_periph_id(blob, node); if (plat->periph_id == PERIPH_ID_NONE) { diff --git a/drivers/spi/zynq_spi.c b/drivers/spi/zynq_spi.c index 7ae1f0ec9a..310fb69c8d 100644 --- a/drivers/spi/zynq_spi.c +++ b/drivers/spi/zynq_spi.c @@ -72,7 +72,7 @@ static int zynq_spi_ofdata_to_platdata(struct udevice *bus) const void *blob = gd->fdt_blob; int node = bus->of_offset; - plat->regs = (struct zynq_spi_regs *)fdtdec_get_addr(blob, node, "reg"); + plat->regs = (struct zynq_spi_regs *)dev_get_addr(bus); /* FIXME: Use 250MHz as a suitable default */ plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", diff --git a/drivers/tpm/Kconfig b/drivers/tpm/Kconfig index f408b8a81d..6bc8fddbd1 100644 --- a/drivers/tpm/Kconfig +++ b/drivers/tpm/Kconfig @@ -1,7 +1,76 @@ +# +# TPM subsystem configuration +# + +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 help This driver emulates a TPM, providing access to base functions such as reading and writing TPM private data. This is enough to support Chrome OS verified boot. Extend functionality is not implemented. + +config TPM_ATMEL_TWI + bool "Enable Atmel TWI TPM device driver" + depends on TPM + help + This driver supports an Atmel TPM device connected on the I2C bus. + The usual tpm operations and the 'tpm' command can be used to talk + to the device using the standard TPM Interface Specification (TIS) + protocol + +config TPM_TIS_I2C + bool "Enable support for Infineon SLB9635/45 TPMs on I2C" + depends on TPM && DM_I2C + help + This driver supports Infineon TPM devices connected on the I2C bus. + The usual tpm operations and the 'tpm' command can be used to talk + to the device using the standard TPM Interface Specification (TIS) + protocol + +config TPM_TIS_I2C_BURST_LIMITATION + bool "Enable I2C burst length limitation" + depends on TPM_TIS_I2C + help + Some broken TPMs have a limitation on the number of bytes they can + receive in one message. Enable this option to allow you to set this + option. The can allow a broken TPM to be used by splitting messages + into separate pieces. + +config TPM_TIS_I2C_BURST_LIMITATION_LEN + int "Length" + depends on TPM_TIS_I2C_BURST_LIMITATION + help + Use this to set the burst limitation length + +config TPM_TIS_LPC + bool "Enable support for Infineon SLB9635/45 TPMs on LPC" + depends on TPM && X86 + help + This driver supports Infineon TPM devices connected on the I2C bus. + The usual tpm operations and the 'tpm' command can be used to talk + to the device using the standard TPM Interface Specification (TIS) + protocol + +config TPM_AUTH_SESSIONS + bool "Enable TPM authentication session support" + depends on TPM + help + Enable support for authorised (AUTH1) commands as specified in the + TCG Main Specification 1.2. OIAP-authorised versions of the commands + TPM_LoadKey2 and TPM_GetPubKey are provided. Both features are + available using the 'tpm' command, too. + +endmenu diff --git a/drivers/tpm/Makefile b/drivers/tpm/Makefile index 150570ee7e..0d328f8d9d 100644 --- a/drivers/tpm/Makefile +++ b/drivers/tpm/Makefile @@ -3,9 +3,9 @@ # SPDX-License-Identifier: GPL-2.0+ # -# TODO: Merge tpm_tis_lpc.c with tpm.c +obj-$(CONFIG_DM_TPM) += tpm-uclass.o + obj-$(CONFIG_TPM_ATMEL_TWI) += tpm_atmel_twi.o -obj-$(CONFIG_TPM_TIS_I2C) += tpm.o obj-$(CONFIG_TPM_TIS_I2C) += tpm_tis_i2c.o obj-$(CONFIG_TPM_TIS_LPC) += tpm_tis_lpc.o obj-$(CONFIG_TPM_TIS_SANDBOX) += tpm_tis_sandbox.o diff --git a/drivers/tpm/tpm-uclass.c b/drivers/tpm/tpm-uclass.c new file mode 100644 index 0000000000..b6e1fc5e62 --- /dev/null +++ b/drivers/tpm/tpm-uclass.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <tpm.h> +#include <linux/unaligned/be_byteshift.h> +#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.c b/drivers/tpm/tpm.c deleted file mode 100644 index a650892f09..0000000000 --- a/drivers/tpm/tpm.c +++ /dev/null @@ -1,694 +0,0 @@ -/* - * Copyright (C) 2011 Infineon Technologies - * - * Authors: - * Peter Huewe <huewe.external@infineon.com> - * - * Description: - * Device driver for TCG/TCPA TPM (trusted platform module). - * Specifications at www.trustedcomputinggroup.org - * - * It is based on the Linux kernel driver tpm.c from Leendert van - * Dorn, Dave Safford, Reiner Sailer, and Kyleen Hall. - * - * Version: 2.1.1 - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2 of the - * License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA - */ - -#include <config.h> -#include <common.h> -#include <dm.h> -#include <linux/compiler.h> -#include <fdtdec.h> -#include <i2c.h> -#include <tpm.h> -#include <asm-generic/errno.h> -#include <linux/types.h> -#include <linux/unaligned/be_byteshift.h> - -#include "tpm_private.h" - -DECLARE_GLOBAL_DATA_PTR; - -/* TPM configuration */ -struct tpm { -#ifdef CONFIG_DM_I2C - struct udevice *dev; -#else - int i2c_bus; - int slave_addr; - int old_bus; -#endif - char inited; -} tpm; - -/* Global structure for tpm chip data */ -static struct tpm_chip g_chip; - -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, -}; - -/* Returns max number of milliseconds to wait */ -static unsigned long tpm_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->vendor.duration[duration_idx]; - - if (duration <= 0) - return 2 * 60 * HZ; /* Two minutes timeout */ - else - return duration; -} - -static ssize_t tpm_transmit(const unsigned char *buf, size_t bufsiz) -{ - 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 = chip->vendor.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->vendor.irq) - goto out_recv; - - start = get_timer(0); - stop = tpm_calc_ordinal_duration(chip, ordinal); - do { - debug("waiting for status... %ld %ld\n", start, stop); - u8 status = chip->vendor.status(chip); - if ((status & chip->vendor.req_complete_mask) == - chip->vendor.req_complete_val) { - debug("...got it;\n"); - goto out_recv; - } - - if (status == chip->vendor.req_canceled) { - error("Operation Canceled\n"); - rc = -ECANCELED; - goto out; - } - udelay(TPM_TIMEOUT * 1000); - } while (get_timer(start) < stop); - - chip->vendor.cancel(chip); - error("Operation Timed out\n"); - rc = -ETIME; - goto out; - -out_recv: - debug("out_recv: reading response...\n"); - rc = chip->vendor.recv(chip, (u8 *)buf, TPM_BUFSIZE); - if (rc < 0) - error("tpm_transmit: tpm_recv: error %d\n", rc); - -out: - return rc; -} - -#ifdef CONFIG_DM_I2C -static int tpm_open_dev(struct udevice *dev) -{ - int rc; - - debug("%s: start\n", __func__); - if (g_chip.is_open) - return -EBUSY; - rc = tpm_vendor_init_dev(dev); - if (rc < 0) - g_chip.is_open = 0; - return rc; -} -#else -static int tpm_open(uint32_t dev_addr) -{ - int rc; - - if (g_chip.is_open) - return -EBUSY; - rc = tpm_vendor_init(dev_addr); - if (rc < 0) - g_chip.is_open = 0; - return rc; -} -#endif -static void tpm_close(void) -{ - if (g_chip.is_open) { - tpm_vendor_cleanup(&g_chip); - g_chip.is_open = 0; - } -} - -static int tpm_select(void) -{ -#ifndef CONFIG_DM_I2C - int ret; - - tpm.old_bus = i2c_get_bus_num(); - if (tpm.old_bus != tpm.i2c_bus) { - ret = i2c_set_bus_num(tpm.i2c_bus); - if (ret) { - debug("%s: Fail to set i2c bus %d\n", __func__, - tpm.i2c_bus); - return -1; - } - } -#endif - return 0; -} - -static int tpm_deselect(void) -{ -#ifndef CONFIG_DM_I2C - int ret; - - if (tpm.old_bus != i2c_get_bus_num()) { - ret = i2c_set_bus_num(tpm.old_bus); - if (ret) { - debug("%s: Fail to restore i2c bus %d\n", - __func__, tpm.old_bus); - return -1; - } - } - tpm.old_bus = -1; -#endif - return 0; -} - -/** - * Decode TPM configuration. - * - * @param dev Returns a configuration of TPM device - * @return 0 if ok, -1 on error - */ -static int tpm_decode_config(struct tpm *dev) -{ - const void *blob = gd->fdt_blob; - int parent; - int node; - - 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; - } -#ifdef CONFIG_DM_I2C - struct udevice *bus; - int chip_addr; - int ret; - - /* - * 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 iic_tpm_read() so the offset length needs to be 0 here. - */ - ret = i2c_get_chip(bus, chip_addr, 1, &dev->dev); - if (ret) { - debug("Cannot find device for node '%s: ret=%d'\n", - fdt_get_name(blob, node, NULL), ret); - return ret; - } -#else - int i2c_bus; - - i2c_bus = i2c_get_bus_num_fdt(parent); - if (i2c_bus < 0) - return -1; - dev->i2c_bus = i2c_bus; - dev->slave_addr = fdtdec_get_addr(blob, node, "reg"); -#endif - - return 0; -} - -struct tpm_chip *tpm_register_hardware(const struct tpm_vendor_specific *entry) -{ - struct tpm_chip *chip; - - /* Driver specific per-device data */ - chip = &g_chip; - memcpy(&chip->vendor, entry, sizeof(struct tpm_vendor_specific)); - chip->is_open = 1; - - return chip; -} - -int tis_init(void) -{ - if (tpm.inited) - return 0; - - if (tpm_decode_config(&tpm)) - return -1; - - if (tpm_select()) - return -1; - -#ifndef CONFIG_DM_I2C - /* - * Probe TPM twice; the first probing might fail because TPM is asleep, - * and the probing can wake up TPM. - */ - if (i2c_probe(tpm.slave_addr) && i2c_probe(tpm.slave_addr)) { - debug("%s: fail to probe i2c addr 0x%x\n", __func__, - tpm.slave_addr); - return -1; - } -#endif - - tpm_deselect(); - debug("%s: done\n", __func__); - - tpm.inited = 1; - - return 0; -} - -int tis_open(void) -{ - int rc; - - if (!tpm.inited) - return -1; - - if (tpm_select()) - return -1; - -#ifdef CONFIG_DM_I2C - rc = tpm_open_dev(tpm.dev); -#else - rc = tpm_open(tpm.slave_addr); -#endif - - tpm_deselect(); - - return rc; -} - -int tis_close(void) -{ - if (!tpm.inited) - return -1; - - if (tpm_select()) - return -1; - - tpm_close(); - - tpm_deselect(); - - 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 (!tpm.inited) - return -1; - - if (sizeof(buf) < sbuf_size) - return -1; - - memcpy(buf, sendbuf, sbuf_size); - - if (tpm_select()) - return -1; - - len = tpm_transmit(buf, sbuf_size); - - tpm_deselect(); - - if (len < 10) { - *rbuf_len = 0; - return -1; - } - - memcpy(recvbuf, buf, len); - *rbuf_len = len; - - return 0; -} diff --git a/drivers/tpm/tpm_atmel_twi.c b/drivers/tpm/tpm_atmel_twi.c index 361a7720fa..205d7a5d97 100644 --- a/drivers/tpm/tpm_atmel_twi.c +++ b/drivers/tpm/tpm_atmel_twi.c @@ -1,18 +1,9 @@ /* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. + * Copyright (C) 2013 Guntermann & Drunck, GmbH * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Written by Dirk Eibach <eibach@gdsys.de> * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0+ */ #include <common.h> diff --git a/drivers/tpm/tpm_internal.h b/drivers/tpm/tpm_internal.h new file mode 100644 index 0000000000..cd29dba0b6 --- /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 cc740e9c21..9afe46c1e9 100644 --- a/drivers/tpm/tpm_tis_i2c.c +++ b/drivers/tpm/tpm_tis_i2c.c @@ -17,131 +17,33 @@ * * Version: 2.1.1 * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2 of the - * License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0 */ #include <common.h> #include <dm.h> #include <fdtdec.h> -#include <linux/compiler.h> #include <i2c.h> +#include <tis.h> #include <tpm.h> #include <asm-generic/errno.h> +#include <linux/compiler.h> #include <linux/types.h> #include <linux/unaligned/be_byteshift.h> -#include "tpm_private.h" +#include "tpm_tis_i2c.h" +#include "tpm_internal.h" DECLARE_GLOBAL_DATA_PTR; -/* Address of the TPM on the I2C bus */ -#define TPM_I2C_ADDR 0x20 - -/* Max buffer size supported by our tpm */ -#define TPM_DEV_BUFSIZE 1260 - -/* Max number of iterations after i2c NAK */ -#define MAX_COUNT 3 - -/* - * Max number of iterations after i2c NAK for 'long' commands - * - * We need this especially for sending TPM_READY, since the cleanup after the - * transtion to the ready state may take some time, but it is unpredictable - * how long it will take. - */ -#define MAX_COUNT_LONG 50 - -#define SLEEP_DURATION 60 /* in usec */ -#define SLEEP_DURATION_LONG 210 /* in usec */ - -#define TPM_HEADER_SIZE 10 - -/* - * Expected value for DIDVID register - * - * The only device the system knows about at this moment is Infineon slb9635. - */ -#define TPM_TIS_I2C_DID_VID 0x000b15d1L - -enum tis_access { - TPM_ACCESS_VALID = 0x80, - TPM_ACCESS_ACTIVE_LOCALITY = 0x20, - TPM_ACCESS_REQUEST_PENDING = 0x04, - TPM_ACCESS_REQUEST_USE = 0x02, -}; - -enum tis_status { - TPM_STS_VALID = 0x80, - TPM_STS_COMMAND_READY = 0x40, - TPM_STS_GO = 0x20, - TPM_STS_DATA_AVAIL = 0x10, - TPM_STS_DATA_EXPECT = 0x08, -}; - -enum tis_defaults { - TIS_SHORT_TIMEOUT = 750, /* ms */ - TIS_LONG_TIMEOUT = 2000, /* ms */ -}; - -/* expected value for DIDVID register */ -#define TPM_TIS_I2C_DID_VID_9635 0x000b15d1L -#define TPM_TIS_I2C_DID_VID_9645 0x001a15d1L - -enum i2c_chip_type { - SLB9635, - SLB9645, - UNKNOWN, -}; - static const char * const chip_name[] = { [SLB9635] = "slb9635tt", [SLB9645] = "slb9645tt", [UNKNOWN] = "unknown/fallback to slb9635", }; -#define TPM_ACCESS(l) (0x0000 | ((l) << 4)) -#define TPM_STS(l) (0x0001 | ((l) << 4)) -#define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4)) -#define TPM_DID_VID(l) (0x0006 | ((l) << 4)) - -/* Structure to store I2C TPM specific stuff */ -struct tpm_dev { -#ifdef CONFIG_DM_I2C - struct udevice *dev; -#else - uint addr; -#endif - u8 buf[TPM_DEV_BUFSIZE + sizeof(u8)]; /* Max buffer size + addr */ - enum i2c_chip_type chip_type; -}; - -static struct tpm_dev tpm_dev = { -#ifndef CONFIG_DM_I2C - .addr = TPM_I2C_ADDR -#endif -}; - -static struct tpm_dev tpm_dev; - /* - * iic_tpm_read() - read from TPM register + * tpm_tis_i2c_read() - read from TPM register * @addr: register address to read from * @buffer: provided by caller * @len: number of bytes to read @@ -154,39 +56,32 @@ static struct tpm_dev tpm_dev; * * Return -EIO on error, 0 on success. */ -static int iic_tpm_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 ((tpm_dev.chip_type == SLB9635) || (tpm_dev.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++) { -#ifdef CONFIG_DM_I2C - rc = dm_i2c_write(tpm_dev.dev, 0, (uchar *)&addrbuf, 1); -#else - rc = i2c_write(tpm_dev.addr, 0, 0, - (uchar *)&addrbuf, 1); -#endif + rc = dm_i2c_write(dev, 0, (uchar *)&addrbuf, 1); if (rc == 0) break; /* Success, break to skip sleep */ - udelay(SLEEP_DURATION); + 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 * retrieving the data */ for (count = 0; count < MAX_COUNT; count++) { - udelay(SLEEP_DURATION); -#ifdef CONFIG_DM_I2C - rc = dm_i2c_read(tpm_dev.dev, 0, buffer, len); -#else - rc = i2c_read(tpm_dev.addr, 0, 0, buffer, len); -#endif + udelay(SLEEP_DURATION_US); + rc = dm_i2c_read(dev, 0, buffer, len); if (rc == 0) break; /* success, break to skip sleep */ } @@ -199,60 +94,56 @@ static int iic_tpm_read(u8 addr, u8 *buffer, size_t len) * be safe on the safe side. */ for (count = 0; count < MAX_COUNT; count++) { -#ifdef CONFIG_DM_I2C - rc = dm_i2c_read(tpm_dev.dev, addr, buffer, len); -#else - rc = i2c_read(tpm_dev.addr, addr, 1, buffer, len); -#endif + rc = dm_i2c_read(dev, addr, buffer, len); if (rc == 0) break; /* break here to skip sleep */ - udelay(SLEEP_DURATION); + udelay(SLEEP_DURATION_US); } } /* Take care of 'guard time' */ - udelay(SLEEP_DURATION); + udelay(SLEEP_DURATION_US); if (rc) - return -rc; + return rc; return 0; } -static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len, - unsigned int sleep_time, u8 max_count) +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; - /* Prepare send buffer */ -#ifndef CONFIG_DM_I2C - tpm_dev.buf[0] = addr; - memcpy(&(tpm_dev.buf[1]), buffer, len); - buffer = tpm_dev.buf; - len++; -#endif + 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++) { -#ifdef CONFIG_DM_I2C - rc = dm_i2c_write(tpm_dev.dev, addr, buffer, len); -#else - rc = i2c_write(tpm_dev.addr, 0, 0, buffer, len); -#endif + rc = dm_i2c_write(dev, addr, buffer, len); if (rc == 0) break; /* Success, break to skip sleep */ - udelay(sleep_time); + udelay(sleep_time_us); } /* take care of 'guard time' */ - udelay(sleep_time); + udelay(sleep_time_us); if (rc) - return -rc; + return rc; return 0; } /* - * iic_tpm_write() - write to TPM register + * tpm_tis_i2c_write() - write to TPM register * @addr: register address to write to * @buffer: containing data to be written * @len: number of bytes to write @@ -263,109 +154,135 @@ static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len, * NOTE: TPM is big-endian for multi-byte values. Multi-byte * values have to be swapped. * - * NOTE: use this function instead of the iic_tpm_write_generic function. + * NOTE: use this function instead of the tpm_tis_i2c_write_generic function. * * Return -EIO on error, 0 on success */ -static int iic_tpm_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 iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION, - 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 iic_tpm_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 iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LONG, - MAX_COUNT_LONG); + return tpm_tis_i2c_write_generic(dev, addr, buffer, len, + SLEEP_DURATION_LONG_US, + MAX_COUNT_LONG); } -static int 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 = iic_tpm_read(TPM_ACCESS(loc), &buf, 1); + rc = tpm_tis_i2c_read(dev, TPM_ACCESS(loc), &buf, 1); if (rc < 0) return rc; if ((buf & mask) == mask) { - chip->vendor.locality = loc; + chip->locality = loc; return loc; } - return -1; + return -ENOENT; } -static void release_locality(struct tpm_chip *chip, int loc, int force) +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 (iic_tpm_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; - iic_tpm_write(TPM_ACCESS(loc), &buf, 1); + tpm_tis_i2c_write(dev, TPM_ACCESS(loc), &buf, 1); } } -static int 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 (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 = iic_tpm_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->vendor.timeout_a; + stop = chip->timeout_a; do { - if (check_locality(chip, loc) >= 0) + rc = tpm_tis_i2c_check_locality(dev, loc); + if (rc >= 0) { + debug("%s: Have locality\n", __func__); return loc; - udelay(TPM_TIMEOUT * 1000); + } 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 (iic_tpm_read(TPM_STS(chip->vendor.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 = iic_tpm_write_long(TPM_STS(chip->vendor.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 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]; @@ -373,53 +290,54 @@ static ssize_t get_burstcount(struct tpm_chip *chip) /* Wait for burstcount */ /* XXX: Which timeout value? Spec has 2 answers (c & d) */ start = get_timer(0); - stop = chip->vendor.timeout_d; + stop = chip->timeout_d; do { /* Note: STS is little endian */ - addr = TPM_STS(chip->vendor.locality) + 1; - if (iic_tpm_read(addr, buf, 3) < 0) + addr = TPM_STS(chip->locality) + 1; + if (tpm_tis_i2c_read(dev, addr, buf, 3) < 0) burstcnt = 0; else burstcnt = (buf[2] << 16) + (buf[1] << 8) + buf[0]; if (burstcnt) return burstcnt; - udelay(TPM_TIMEOUT * 1000); + mdelay(TPM_TIMEOUT_MS); } while (get_timer(start) < stop); return -EBUSY; } -static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, - int *status) +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; start = get_timer(0); stop = timeout; do { - udelay(TPM_TIMEOUT * 1000); - *status = tpm_tis_i2c_status(chip); + mdelay(TPM_TIMEOUT_MS); + *status = tpm_tis_i2c_status(dev); if ((*status & mask) == mask) return 0; } while (get_timer(start) < stop); - return -ETIME; + return -ETIMEDOUT; } -static int 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 = get_burstcount(chip); + burstcnt = tpm_tis_i2c_get_burstcount(dev); /* burstcount < 0 -> tpm is busy */ if (burstcnt < 0) @@ -429,8 +347,8 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) if (burstcnt > (count - size)) burstcnt = count - size; - rc = iic_tpm_read(TPM_DATA_FIFO(chip->vendor.locality), - &(buf[size]), burstcnt); + rc = tpm_tis_i2c_read(dev, TPM_DATA_FIFO(chip->locality), + &(buf[size]), burstcnt); if (rc == 0) size += burstcnt; } @@ -438,60 +356,58 @@ static int 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 = 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 += recv_data(chip, &buf[TPM_HEADER_SIZE], - expected - 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; } - wait_for_stat(chip, TPM_STS_VALID, chip->vendor.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 - */ - udelay(2000); - release_locality(chip, chip->vendor.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; @@ -502,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 (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 (wait_for_stat(chip, TPM_STS_COMMAND_READY, - chip->vendor.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 = get_burstcount(chip); + burstcnt = tpm_tis_i2c_get_burstcount(dev); /* burstcount < 0 -> tpm is busy */ if (burstcnt < 0) @@ -527,107 +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 = iic_tpm_write(TPM_DATA_FIFO(chip->vendor.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 = wait_for_stat(chip, TPM_STS_VALID, - chip->vendor.timeout_c, &status); + 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 */ - iic_tpm_write(TPM_STS(chip->vendor.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 */ - udelay(2000); - release_locality(chip, chip->vendor.locality, 0); - - return rc; -} + mdelay(2); + tpm_tis_i2c_release_locality(dev, chip->locality, 0); -static struct tpm_vendor_specific tpm_tis_i2c = { - .status = tpm_tis_i2c_status, - .recv = tpm_tis_i2c_recv, - .send = tpm_tis_i2c_send, - .cancel = tpm_tis_i2c_ready, - .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, - .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, - .req_canceled = TPM_STS_COMMAND_READY, -}; - - -static enum i2c_chip_type tpm_vendor_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_vendor_init_common(void) +static int tpm_tis_i2c_init(struct udevice *dev) { - struct tpm_chip *chip; + struct tpm_chip *chip = dev_get_priv(dev); u32 vendor; u32 expected_did_vid; + int rc; - tpm_dev.chip_type = tpm_vendor_chip_type(); - - chip = tpm_register_hardware(&tpm_tis_i2c); - if (chip < 0) - return -ENODEV; - - /* Disable interrupts (not supported) */ - chip->vendor.irq = 0; + chip->is_open = 1; - /* Default timeouts */ - chip->vendor.timeout_a = TIS_SHORT_TIMEOUT; - chip->vendor.timeout_b = TIS_LONG_TIMEOUT; - chip->vendor.timeout_c = TIS_SHORT_TIMEOUT; - chip->vendor.timeout_d = TIS_SHORT_TIMEOUT; + /* 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; - if (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 (iic_tpm_read(TPM_DID_VID(0), (uchar *)&vendor, 4) < 0) { - 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 (tpm_dev.chip_type == SLB9635) { + if (chip->chip_type == SLB9635) { vendor = be32_to_cpu(vendor); expected_did_vid = TPM_TIS_I2C_DID_VID_9635; } else { @@ -635,13 +524,14 @@ static int tpm_vendor_init_common(void) expected_did_vid = TPM_TIS_I2C_DID_VID_9645; } - if (tpm_dev.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[tpm_dev.chip_type], vendor >> 16); + chip_name[chip->chip_type], vendor >> 16); /* * A timeout query to TPM can be placed here. @@ -651,33 +541,83 @@ static int tpm_vendor_init_common(void) return 0; } -#ifdef CONFIG_DM_I2C -/* Initialisation of i2c tpm */ -int tpm_vendor_init_dev(struct udevice *dev) +static int tpm_tis_i2c_open(struct udevice *dev) { - tpm_dev.dev = dev; - return tpm_vendor_init_common(); + struct tpm_chip *chip = dev_get_priv(dev); + int rc; + + debug("%s: start\n", __func__); + if (chip->is_open) + return -EBUSY; + rc = tpm_tis_i2c_init(dev); + if (rc < 0) + chip->is_open = 0; + + return rc; } -#else -/* Initialisation of i2c tpm */ -int tpm_vendor_init(uint32_t dev_addr) + +static int tpm_tis_i2c_close(struct udevice *dev) { - uint old_addr; - int rc = 0; + struct tpm_chip *chip = dev_get_priv(dev); - old_addr = tpm_dev.addr; - if (dev_addr != 0) - tpm_dev.addr = dev_addr; + if (chip->is_open) { + tpm_tis_i2c_release_locality(dev, chip->locality, 1); + chip->is_open = 0; + chip->vend_dev = 0; + } - rc = tpm_vendor_init_common(); - if (rc) - tpm_dev.addr = old_addr; + return 0; +} - return rc; +static int tpm_tis_get_desc(struct udevice *dev, char *buf, int size) +{ + struct tpm_chip *chip = dev_get_priv(dev); + + if (size < 50) + return -ENOSPC; + + 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); } -#endif -void tpm_vendor_cleanup(struct tpm_chip *chip) +static int tpm_tis_i2c_probe(struct udevice *dev) { - release_locality(chip, chip->vendor.locality, 1); + struct tpm_chip_priv *uc_priv = dev_get_uclass_priv(dev); + struct tpm_chip *chip = dev_get_priv(dev); + + chip->chip_type = dev_get_driver_data(dev); + + /* 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; } + +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, +}; + +static const struct udevice_id tpm_tis_i2c_ids[] = { + { .compatible = "infineon,slb9635tt", .data = SLB9635 }, + { .compatible = "infineon,slb9645tt", .data = SLB9645 }, + { } +}; + +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_private.h b/drivers/tpm/tpm_tis_i2c.h index 8894c98e6a..3b510d101e 100644 --- a/drivers/tpm/tpm_private.h +++ b/drivers/tpm/tpm_tis_i2c.h @@ -13,34 +13,21 @@ * It is based on the Linux kernel driver tpm.c from Leendert van * Dorn, Dave Safford, Reiner Sailer, and Kyleen Hall. * - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2 of the - * License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0 */ -#ifndef _TPM_PRIVATE_H_ -#define _TPM_PRIVATE_H_ +#ifndef _TPM_TIS_I2C_H +#define _TPM_TIS_I2C_H #include <linux/compiler.h> #include <linux/types.h> enum tpm_timeout { - TPM_TIMEOUT = 5, /* msecs */ + TPM_TIMEOUT_MS = 5, + TIS_SHORT_TIMEOUT_MS = 750, + TIS_LONG_TIMEOUT_MS = 2000, + SLEEP_DURATION_US = 60, + SLEEP_DURATION_LONG_US = 210, }; /* Size of external transmit buffer (used in tpm_transmit)*/ @@ -50,25 +37,18 @@ enum tpm_timeout { #define TPM_RSP_SIZE_BYTE 2 #define TPM_RSP_RC_BYTE 6 -struct tpm_chip; - -struct tpm_vendor_specific { - const u8 req_complete_mask; - const u8 req_complete_val; - const u8 req_canceled; - int irq; - int (*recv) (struct tpm_chip *, u8 *, size_t); - int (*send) (struct tpm_chip *, u8 *, size_t); - void (*cancel) (struct tpm_chip *); - u8(*status) (struct tpm_chip *); - int locality; - unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* msec */ - unsigned long duration[3]; /* msec */ +enum i2c_chip_type { + SLB9635, + SLB9645, + UNKNOWN, }; struct tpm_chip { int is_open; - struct tpm_vendor_specific vendor; + int locality; + u32 vend_dev; + unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* msec */ + enum i2c_chip_type chip_type; }; struct tpm_input_header { @@ -127,14 +107,40 @@ struct tpm_cmd_t { union tpm_cmd_params params; } __packed; -struct tpm_chip *tpm_register_hardware(const struct tpm_vendor_specific *); +/* Max number of iterations after i2c NAK */ +#define MAX_COUNT 3 -int tpm_vendor_init(uint32_t dev_addr); +/* + * Max number of iterations after i2c NAK for 'long' commands + * + * We need this especially for sending TPM_READY, since the cleanup after the + * transtion to the ready state may take some time, but it is unpredictable + * how long it will take. + */ +#define MAX_COUNT_LONG 50 -struct udevice; -int tpm_vendor_init_dev(struct udevice *dev); +enum tis_access { + TPM_ACCESS_VALID = 0x80, + TPM_ACCESS_ACTIVE_LOCALITY = 0x20, + TPM_ACCESS_REQUEST_PENDING = 0x04, + TPM_ACCESS_REQUEST_USE = 0x02, +}; + +enum tis_status { + TPM_STS_VALID = 0x80, + TPM_STS_COMMAND_READY = 0x40, + TPM_STS_GO = 0x20, + TPM_STS_DATA_AVAIL = 0x10, + TPM_STS_DATA_EXPECT = 0x08, +}; -void tpm_vendor_cleanup(struct tpm_chip *chip); +/* expected value for DIDVID register */ +#define TPM_TIS_I2C_DID_VID_9635 0x000b15d1L +#define TPM_TIS_I2C_DID_VID_9645 0x001a15d1L +#define TPM_ACCESS(l) (0x0000 | ((l) << 4)) +#define TPM_STS(l) (0x0001 | ((l) << 4)) +#define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4)) +#define TPM_DID_VID(l) (0x0006 | ((l) << 4)) #endif diff --git a/drivers/tpm/tpm_tis_lpc.c b/drivers/tpm/tpm_tis_lpc.c index d09f8cee05..b41c3cec37 100644 --- a/drivers/tpm/tpm_tis_lpc.c +++ b/drivers/tpm/tpm_tis_lpc.c @@ -14,8 +14,11 @@ */ #include <common.h> -#include <asm/io.h> +#include <dm.h> +#include <mapmem.h> +#include <tis.h> #include <tpm.h> +#include <asm/io.h> #define PREFIX "lpc_tpm: " @@ -36,13 +39,15 @@ struct tpm_locality { u8 padding4[251]; }; +struct tpm_tis_lpc_priv { + struct tpm_locality *regs; +}; + /* * This pointer refers to the TPM chip, 5 of its localities are mapped as an * array. */ #define TPM_TOTAL_LOCALITIES 5 -static struct tpm_locality *lpc_tpm_dev = - (struct tpm_locality *)CONFIG_TPM_TIS_BASE_ADDRESS; /* Some registers' bit field definitions */ #define TIS_STS_VALID (1 << 7) /* 0x80 */ @@ -63,85 +68,45 @@ static struct tpm_locality *lpc_tpm_dev = #define TIS_STS_BURST_COUNT_MASK (0xffff) #define TIS_STS_BURST_COUNT_SHIFT (8) -/* - * Error value returned if a tpm register does not enter the expected state - * after continuous polling. No actual TPM register reading ever returns -1, - * so this value is a safe error indication to be mixed with possible status - * register values. - */ -#define TPM_TIMEOUT_ERR (-1) - -/* Error value returned on various TPM driver errors. */ -#define TPM_DRIVER_ERR (1) - /* 1 second is plenty for anything TPM does. */ #define MAX_DELAY_US (1000 * 1000) /* Retrieve burst count value out of the status register contents. */ static u16 burst_count(u32 status) { - return (status >> TIS_STS_BURST_COUNT_SHIFT) & TIS_STS_BURST_COUNT_MASK; + return (status >> TIS_STS_BURST_COUNT_SHIFT) & + TIS_STS_BURST_COUNT_MASK; } -/* - * Structures defined below allow creating descriptions of TPM vendor/device - * ID information for run time discovery. The only device the system knows - * about at this time is Infineon slb9635. - */ -struct device_name { - u16 dev_id; - const char * const dev_name; -}; - -struct vendor_name { - u16 vendor_id; - const char *vendor_name; - const struct device_name *dev_names; -}; - -static const struct device_name infineon_devices[] = { - {0xb, "SLB9635 TT 1.2"}, - {0} -}; - -static const struct vendor_name vendor_names[] = { - {0x15d1, "Infineon", infineon_devices}, -}; - -/* - * Cached vendor/device ID pair to indicate that the device has been already - * discovered. - */ -static u32 vendor_dev_id; - /* TPM access wrappers to support tracing */ -static u8 tpm_read_byte(const u8 *ptr) +static u8 tpm_read_byte(struct tpm_tis_lpc_priv *priv, const u8 *ptr) { u8 ret = readb(ptr); debug(PREFIX "Read reg 0x%4.4x returns 0x%2.2x\n", - (u32)(uintptr_t)ptr - (u32)(uintptr_t)lpc_tpm_dev, ret); + (u32)(uintptr_t)ptr - (u32)(uintptr_t)priv->regs, ret); return ret; } -static u32 tpm_read_word(const u32 *ptr) +static u32 tpm_read_word(struct tpm_tis_lpc_priv *priv, const u32 *ptr) { u32 ret = readl(ptr); debug(PREFIX "Read reg 0x%4.4x returns 0x%8.8x\n", - (u32)(uintptr_t)ptr - (u32)(uintptr_t)lpc_tpm_dev, ret); + (u32)(uintptr_t)ptr - (u32)(uintptr_t)priv->regs, ret); return ret; } -static void tpm_write_byte(u8 value, u8 *ptr) +static void tpm_write_byte(struct tpm_tis_lpc_priv *priv, u8 value, u8 *ptr) { debug(PREFIX "Write reg 0x%4.4x with 0x%2.2x\n", - (u32)(uintptr_t)ptr - (u32)(uintptr_t)lpc_tpm_dev, value); + (u32)(uintptr_t)ptr - (u32)(uintptr_t)priv->regs, value); writeb(value, ptr); } -static void tpm_write_word(u32 value, u32 *ptr) +static void tpm_write_word(struct tpm_tis_lpc_priv *priv, u32 value, + u32 *ptr) { debug(PREFIX "Write reg 0x%4.4x with 0x%8.8x\n", - (u32)(uintptr_t)ptr - (u32)(uintptr_t)lpc_tpm_dev, value); + (u32)(uintptr_t)ptr - (u32)(uintptr_t)priv->regs, value); writel(value, ptr); } @@ -156,67 +121,51 @@ static void tpm_write_word(u32 value, u32 *ptr) * @expected - value the field(s) are supposed to be set to * * Returns the register contents in case the expected value was found in the - * appropriate register bits, or TPM_TIMEOUT_ERR on timeout. + * appropriate register bits, or -ETIMEDOUT on timeout. */ -static u32 tis_wait_reg(u32 *reg, u8 mask, u8 expected) +static int tis_wait_reg(struct tpm_tis_lpc_priv *priv, u32 *reg, u8 mask, + u8 expected) { u32 time_us = MAX_DELAY_US; while (time_us > 0) { - u32 value = tpm_read_word(reg); + u32 value = tpm_read_word(priv, reg); if ((value & mask) == expected) return value; udelay(1); /* 1 us */ time_us--; } - return TPM_TIMEOUT_ERR; + + return -ETIMEDOUT; } /* * Probe the TPM device and try determining its manufacturer/device name. * - * Returns 0 on success (the device is found or was found during an earlier - * invocation) or TPM_DRIVER_ERR if the device is not found. + * Returns 0 on success, -ve on error */ -int tis_init(void) +static int tpm_tis_lpc_probe(struct udevice *dev) { - u32 didvid = tpm_read_word(&lpc_tpm_dev[0].did_vid); - int i; - const char *device_name = "unknown"; - const char *vendor_name = device_name; - u16 vid, did; - - if (vendor_dev_id) - return 0; /* Already probed. */ - - if (!didvid || (didvid == 0xffffffff)) { - printf("%s: No TPM device found\n", __func__); - return TPM_DRIVER_ERR; - } + struct tpm_tis_lpc_priv *priv = dev_get_priv(dev); + u32 vid, did; + fdt_addr_t addr; + u32 didvid; - vendor_dev_id = didvid; + addr = dev_get_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + priv->regs = map_sysmem(addr, 0); + didvid = tpm_read_word(priv, &priv->regs[0].did_vid); vid = didvid & 0xffff; did = (didvid >> 16) & 0xffff; - for (i = 0; i < ARRAY_SIZE(vendor_names); i++) { - int j = 0; - u16 known_did; - - if (vid == vendor_names[i].vendor_id) - vendor_name = vendor_names[i].vendor_name; - - while ((known_did = vendor_names[i].dev_names[j].dev_id) != 0) { - if (known_did == did) { - device_name = - vendor_names[i].dev_names[j].dev_name; - break; - } - j++; - } - break; + if (vid != 0x15d1 || did != 0xb) { + debug("Invalid vendor/device ID %04x/%04x\n", vid, did); + return -ENOSYS; } - printf("Found TPM %s by %s\n", device_name, vendor_name); + debug("Found TPM %s by %s\n", "SLB9635 TT 1.2", "Infineon"); + return 0; } @@ -228,23 +177,25 @@ int tis_init(void) * @data - address of the data to send, byte by byte * @len - length of the data to send * - * Returns 0 on success, TPM_DRIVER_ERR on error (in case the device does - * not accept the entire command). + * Returns 0 on success, -ve on error (in case the device does not accept + * the entire command). */ -static u32 tis_senddata(const u8 * const data, u32 len) +static int tis_senddata(struct udevice *dev, const u8 *data, size_t len) { + struct tpm_tis_lpc_priv *priv = dev_get_priv(dev); + struct tpm_locality *regs = priv->regs; u32 offset = 0; u16 burst = 0; u32 max_cycles = 0; u8 locality = 0; u32 value; - value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status, + value = tis_wait_reg(priv, ®s[locality].tpm_status, TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY); - if (value == TPM_TIMEOUT_ERR) { + if (value == -ETIMEDOUT) { printf("%s:%d - failed to get 'command_ready' status\n", __FILE__, __LINE__); - return TPM_DRIVER_ERR; + return value; } burst = burst_count(value); @@ -256,11 +207,11 @@ static u32 tis_senddata(const u8 * const data, u32 len) if (max_cycles++ == MAX_DELAY_US) { printf("%s:%d failed to feed %d bytes of %d\n", __FILE__, __LINE__, len - offset, len); - return TPM_DRIVER_ERR; + return -ETIMEDOUT; } udelay(1); - burst = burst_count(tpm_read_word(&lpc_tpm_dev - [locality].tpm_status)); + burst = burst_count(tpm_read_word(priv, + ®s[locality].tpm_status)); } max_cycles = 0; @@ -276,16 +227,16 @@ static u32 tis_senddata(const u8 * const data, u32 len) */ count = min((u32)burst, len - offset - 1); while (count--) - tpm_write_byte(data[offset++], - &lpc_tpm_dev[locality].data); + tpm_write_byte(priv, data[offset++], + ®s[locality].data); - value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status, + value = tis_wait_reg(priv, ®s[locality].tpm_status, TIS_STS_VALID, TIS_STS_VALID); - if ((value == TPM_TIMEOUT_ERR) || !(value & TIS_STS_EXPECT)) { + if ((value == -ETIMEDOUT) || !(value & TIS_STS_EXPECT)) { printf("%s:%d TPM command feed overflow\n", __FILE__, __LINE__); - return TPM_DRIVER_ERR; + return value == -ETIMEDOUT ? value : -EIO; } burst = burst_count(value); @@ -300,21 +251,21 @@ static u32 tis_senddata(const u8 * const data, u32 len) } /* Send the last byte. */ - tpm_write_byte(data[offset++], &lpc_tpm_dev[locality].data); + tpm_write_byte(priv, data[offset++], ®s[locality].data); /* * Verify that TPM does not expect any more data as part of this * command. */ - value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status, + value = tis_wait_reg(priv, ®s[locality].tpm_status, TIS_STS_VALID, TIS_STS_VALID); - if ((value == TPM_TIMEOUT_ERR) || (value & TIS_STS_EXPECT)) { + if ((value == -ETIMEDOUT) || (value & TIS_STS_EXPECT)) { printf("%s:%d unexpected TPM status 0x%x\n", __FILE__, __LINE__, value); - return TPM_DRIVER_ERR; + return value == -ETIMEDOUT ? value : -EIO; } /* OK, sitting pretty, let's start the command execution. */ - tpm_write_word(TIS_STS_TPM_GO, &lpc_tpm_dev[locality].tpm_status); + tpm_write_word(priv, TIS_STS_TPM_GO, ®s[locality].tpm_status); return 0; } @@ -328,25 +279,27 @@ static u32 tis_senddata(const u8 * const data, u32 len) * * On success stores the number of received bytes to len and returns 0. On * errors (misformatted TPM data or synchronization problems) returns - * TPM_DRIVER_ERR. + * -ve value. */ -static u32 tis_readresponse(u8 *buffer, u32 *len) +static int tis_readresponse(struct udevice *dev, u8 *buffer, size_t len) { + struct tpm_tis_lpc_priv *priv = dev_get_priv(dev); + struct tpm_locality *regs = priv->regs; u16 burst; u32 value; u32 offset = 0; u8 locality = 0; const u32 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID; - u32 expected_count = *len; + u32 expected_count = len; int max_cycles = 0; /* Wait for the TPM to process the command. */ - value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status, + value = tis_wait_reg(priv, ®s[locality].tpm_status, has_data, has_data); - if (value == TPM_TIMEOUT_ERR) { + if (value == -ETIMEDOUT) { printf("%s:%d failed processing command\n", __FILE__, __LINE__); - return TPM_DRIVER_ERR; + return value; } do { @@ -354,18 +307,17 @@ static u32 tis_readresponse(u8 *buffer, u32 *len) if (max_cycles++ == MAX_DELAY_US) { printf("%s:%d TPM stuck on read\n", __FILE__, __LINE__); - return TPM_DRIVER_ERR; + return -EIO; } udelay(1); - value = tpm_read_word(&lpc_tpm_dev - [locality].tpm_status); + value = tpm_read_word(priv, ®s[locality].tpm_status); } max_cycles = 0; while (burst-- && (offset < expected_count)) { - buffer[offset++] = tpm_read_byte(&lpc_tpm_dev - [locality].data); + buffer[offset++] = tpm_read_byte(priv, + ®s[locality].data); if (offset == 6) { /* @@ -382,22 +334,22 @@ static u32 tis_readresponse(u8 *buffer, u32 *len) expected_count = be32_to_cpu(real_length); if ((expected_count < offset) || - (expected_count > *len)) { + (expected_count > len)) { printf("%s:%d bad response size %d\n", __FILE__, __LINE__, expected_count); - return TPM_DRIVER_ERR; + return -ENOSPC; } } } /* Wait for the next portion. */ - value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status, + value = tis_wait_reg(priv, ®s[locality].tpm_status, TIS_STS_VALID, TIS_STS_VALID); - if (value == TPM_TIMEOUT_ERR) { + if (value == -ETIMEDOUT) { printf("%s:%d failed to read response\n", __FILE__, __LINE__); - return TPM_DRIVER_ERR; + return value; } if (offset == expected_count) @@ -412,68 +364,90 @@ static u32 tis_readresponse(u8 *buffer, u32 *len) if (value & TIS_STS_DATA_AVAILABLE) { printf("%s:%d wrong receive status %x\n", __FILE__, __LINE__, value); - return TPM_DRIVER_ERR; + return -EBADMSG; } /* Tell the TPM that we are done. */ - tpm_write_word(TIS_STS_COMMAND_READY, &lpc_tpm_dev - [locality].tpm_status); - *len = offset; - return 0; + tpm_write_word(priv, TIS_STS_COMMAND_READY, + ®s[locality].tpm_status); + + return offset; } -int tis_open(void) +static int tpm_tis_lpc_open(struct udevice *dev) { + struct tpm_tis_lpc_priv *priv = dev_get_priv(dev); + struct tpm_locality *regs = priv->regs; u8 locality = 0; /* we use locality zero for everything. */ - - if (tis_close()) - return TPM_DRIVER_ERR; + int ret; /* now request access to locality. */ - tpm_write_word(TIS_ACCESS_REQUEST_USE, &lpc_tpm_dev[locality].access); + tpm_write_word(priv, TIS_ACCESS_REQUEST_USE, ®s[locality].access); /* did we get a lock? */ - if (tis_wait_reg(&lpc_tpm_dev[locality].access, + ret = tis_wait_reg(priv, ®s[locality].access, TIS_ACCESS_ACTIVE_LOCALITY, - TIS_ACCESS_ACTIVE_LOCALITY) == TPM_TIMEOUT_ERR) { + TIS_ACCESS_ACTIVE_LOCALITY); + if (ret == -ETIMEDOUT) { printf("%s:%d - failed to lock locality %d\n", __FILE__, __LINE__, locality); - return TPM_DRIVER_ERR; + return ret; } - tpm_write_word(TIS_STS_COMMAND_READY, - &lpc_tpm_dev[locality].tpm_status); + tpm_write_word(priv, TIS_STS_COMMAND_READY, + ®s[locality].tpm_status); return 0; } -int tis_close(void) +static int tpm_tis_lpc_close(struct udevice *dev) { + struct tpm_tis_lpc_priv *priv = dev_get_priv(dev); + struct tpm_locality *regs = priv->regs; u8 locality = 0; - if (tpm_read_word(&lpc_tpm_dev[locality].access) & + if (tpm_read_word(priv, ®s[locality].access) & TIS_ACCESS_ACTIVE_LOCALITY) { - tpm_write_word(TIS_ACCESS_ACTIVE_LOCALITY, - &lpc_tpm_dev[locality].access); + tpm_write_word(priv, TIS_ACCESS_ACTIVE_LOCALITY, + ®s[locality].access); - if (tis_wait_reg(&lpc_tpm_dev[locality].access, - TIS_ACCESS_ACTIVE_LOCALITY, 0) == - TPM_TIMEOUT_ERR) { + if (tis_wait_reg(priv, ®s[locality].access, + TIS_ACCESS_ACTIVE_LOCALITY, 0) == -ETIMEDOUT) { printf("%s:%d - failed to release locality %d\n", __FILE__, __LINE__, locality); - return TPM_DRIVER_ERR; + return -ETIMEDOUT; } } return 0; } -int tis_sendrecv(const u8 *sendbuf, size_t send_size, - u8 *recvbuf, size_t *recv_len) +static int tpm_tis_get_desc(struct udevice *dev, char *buf, int size) { - if (tis_senddata(sendbuf, send_size)) { - printf("%s:%d failed sending data to TPM\n", - __FILE__, __LINE__); - return TPM_DRIVER_ERR; - } + if (size < 50) + return -ENOSPC; - return tis_readresponse(recvbuf, (u32 *)recv_len); + return snprintf(buf, size, "1.2 TPM (vendor %s, chip %s)", + "Infineon", "SLB9635 TT 1.2"); } + + +static const struct tpm_ops tpm_tis_lpc_ops = { + .open = tpm_tis_lpc_open, + .close = tpm_tis_lpc_close, + .get_desc = tpm_tis_get_desc, + .send = tis_senddata, + .recv = tis_readresponse, +}; + +static const struct udevice_id tpm_tis_lpc_ids[] = { + { .compatible = "infineon,slb9635lpc" }, + { } +}; + +U_BOOT_DRIVER(tpm_tis_lpc) = { + .name = "tpm_tis_lpc", + .id = UCLASS_TPM, + .of_match = tpm_tis_lpc_ids, + .ops = &tpm_tis_lpc_ops, + .probe = tpm_tis_lpc_probe, + .priv_auto_alloc_size = sizeof(struct tpm_tis_lpc_priv), +}; diff --git a/drivers/tpm/tpm_tis_sandbox.c b/drivers/tpm/tpm_tis_sandbox.c index ed4b039127..9ea98075b3 100644 --- a/drivers/tpm/tpm_tis_sandbox.c +++ b/drivers/tpm/tpm_tis_sandbox.c @@ -5,6 +5,8 @@ */ #include <common.h> +#include <dm.h> +#include <tpm.h> #include <asm/state.h> #include <asm/unaligned.h> #include <linux/crc8.h> @@ -56,7 +58,7 @@ enum { */ static struct tpm_state { uint8_t nvdata[NV_SEQ_COUNT][NV_DATA_SIZE]; -} state; +} g_state; /** * sandbox_tpm_read_state() - read the sandbox EC state from the state file @@ -82,7 +84,7 @@ static int sandbox_tpm_read_state(const void *blob, int node) sprintf(prop_name, "nvdata%d", i); prop = fdt_getprop(blob, node, prop_name, &len); if (prop && len == NV_DATA_SIZE) - memcpy(state.nvdata[i], prop, NV_DATA_SIZE); + memcpy(g_state.nvdata[i], prop, NV_DATA_SIZE); } return 0; @@ -110,7 +112,7 @@ static int sandbox_tpm_write_state(void *blob, int node) char prop_name[20]; sprintf(prop_name, "nvdata%d", i); - fdt_setprop(blob, node, prop_name, state.nvdata[i], + fdt_setprop(blob, node, prop_name, g_state.nvdata[i], NV_DATA_SIZE); } @@ -135,10 +137,11 @@ static int index_to_seq(uint32_t index) return -1; } -int tis_sendrecv(const u8 *sendbuf, size_t send_size, - u8 *recvbuf, size_t *recv_len) +static int sandbox_tpm_xfer(struct udevice *dev, const uint8_t *sendbuf, + size_t send_size, uint8_t *recvbuf, + size_t *recv_len) { - struct tpm_state *tpm = &state; + struct tpm_state *tpm = dev_get_priv(dev); uint32_t code, index, length, type; uint8_t *data; int seq; @@ -241,20 +244,50 @@ int tis_sendrecv(const u8 *sendbuf, size_t send_size, return 0; } -int tis_open(void) +static int sandbox_tpm_get_desc(struct udevice *dev, char *buf, int size) { - printf("%s\n", __func__); + if (size < 15) + return -ENOSPC; + + return snprintf(buf, size, "sandbox TPM"); +} + +static int sandbox_tpm_probe(struct udevice *dev) +{ + struct tpm_state *tpm = dev_get_priv(dev); + + memcpy(tpm, &g_state, sizeof(*tpm)); + return 0; } -int tis_close(void) +static int sandbox_tpm_open(struct udevice *dev) { - printf("%s\n", __func__); return 0; } -int tis_init(void) +static int sandbox_tpm_close(struct udevice *dev) { - printf("%s\n", __func__); return 0; } + +static const struct tpm_ops sandbox_tpm_ops = { + .open = sandbox_tpm_open, + .close = sandbox_tpm_close, + .get_desc = sandbox_tpm_get_desc, + .xfer = sandbox_tpm_xfer, +}; + +static const struct udevice_id sandbox_tpm_ids[] = { + { .compatible = "google,sandbox-tpm" }, + { } +}; + +U_BOOT_DRIVER(sandbox_tpm) = { + .name = "sandbox_tpm", + .id = UCLASS_TPM, + .of_match = sandbox_tpm_ids, + .ops = &sandbox_tpm_ops, + .probe = sandbox_tpm_probe, + .priv_auto_alloc_size = sizeof(struct tpm_state), +}; diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 4a4f5593e9..31d54ab285 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -684,11 +684,13 @@ static void config_clock(const u32 timing[]) timing[PARAM_CPCON], timing[PARAM_LFCON]); } -static int fdt_decode_usb(const void *blob, int node, struct fdt_usb *config) +static int fdt_decode_usb(struct udevice *dev, struct fdt_usb *config) { + const void *blob = gd->fdt_blob; + int node = dev->of_offset; const char *phy, *mode; - config->reg = (struct usb_ctlr *)fdtdec_get_addr(blob, node, "reg"); + config->reg = (struct usb_ctlr *)dev_get_addr(dev); mode = fdt_getprop(blob, node, "dr_mode", NULL); if (mode) { if (0 == strcmp(mode, "host")) @@ -812,7 +814,7 @@ static int ehci_usb_ofdata_to_platdata(struct udevice *dev) struct fdt_usb *priv = dev_get_priv(dev); int ret; - ret = fdt_decode_usb(gd->fdt_blob, dev->of_offset, priv); + ret = fdt_decode_usb(dev, priv); if (ret) return ret; diff --git a/drivers/usb/host/xhci-exynos5.c b/drivers/usb/host/xhci-exynos5.c index 251885b28b..28416ed106 100644 --- a/drivers/usb/host/xhci-exynos5.c +++ b/drivers/usb/host/xhci-exynos5.c @@ -61,7 +61,7 @@ static int xhci_usb_ofdata_to_platdata(struct udevice *dev) /* * Get the base address for XHCI controller from the device node */ - plat->hcd_base = fdtdec_get_addr(blob, dev->of_offset, "reg"); + plat->hcd_base = dev_get_addr(dev); if (plat->hcd_base == FDT_ADDR_T_NONE) { debug("Can't get the XHCI register base address\n"); return -ENXIO; diff --git a/drivers/video/tegra124/dp.c b/drivers/video/tegra124/dp.c index 3c0b721e3b..1bf92020ae 100644 --- a/drivers/video/tegra124/dp.c +++ b/drivers/video/tegra124/dp.c @@ -1555,9 +1555,8 @@ error_enable: static int tegra_dp_ofdata_to_platdata(struct udevice *dev) { struct tegra_dp_plat *plat = dev_get_platdata(dev); - const void *blob = gd->fdt_blob; - plat->base = fdtdec_get_addr(blob, dev->of_offset, "reg"); + plat->base = dev_get_addr(dev); return 0; } diff --git a/dts/Kconfig b/dts/Kconfig index d72a90905f..a04bb962af 100644 --- a/dts/Kconfig +++ b/dts/Kconfig @@ -58,7 +58,7 @@ config DEFAULT_DEVICE_TREE config OF_SPL_REMOVE_PROPS string "List of device tree properties to drop for SPL" - depends on OF_CONTROL && SPL + depends on SPL_OF_CONTROL default "pinctrl-0 pinctrl-names clocks clock-names interrupt-parent" help Since SPL normally runs in a reduced memory space, the device tree diff --git a/include/configs/controlcenterd.h b/include/configs/controlcenterd.h index 8f829eddb1..9a1f6d0782 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 5779cfdb66..1c955d9e37 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 e710f41f79..4866836d55 100644 --- a/include/configs/exynos5-common.h +++ b/include/configs/exynos5-common.h @@ -54,13 +54,6 @@ #define CONFIG_CMD_DTT #define CONFIG_TMU_CMD_DTT -/* TPM */ -#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 #define CONFIG_SUPPORT_EMMC_BOOT diff --git a/include/configs/x86-common.h b/include/configs/x86-common.h index 217312e5b7..3f153f24ae 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 /*----------------------------------------------------------------------- diff --git a/include/dm/device-internal.h b/include/dm/device-internal.h index 04884f1805..322d35a478 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 61610e69aa..4513d6a311 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, diff --git a/include/dm/pinctrl.h b/include/dm/pinctrl.h new file mode 100644 index 0000000000..bc6fdb4a1f --- /dev/null +++ b/include/dm/pinctrl.h @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com> + * + * 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 c744044bb8..1eeec74964 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 */ @@ -54,6 +56,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/dm/uclass.h b/include/dm/uclass.h index d56877c898..d214b88734 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 diff --git a/include/fdtdec.h b/include/fdtdec.h index 0cb6fa0075..3e237312a7 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/include/tis.h b/include/tis.h index 40a1f86762..1985d9e60e 100644 --- a/include/tis.h +++ b/include/tis.h @@ -7,6 +7,8 @@ #ifndef __TIS_H #define __TIS_H +#ifndef CONFIG_DM_TPM + #include <common.h> /* 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 88aeba28e3..086b672718 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, @@ -38,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) @@ -152,12 +172,217 @@ 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 */ +#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. * * @return 0 on success, non-0 on error. */ -uint32_t tpm_init(void); +int tpm_init(void); /** * Issue a TPM_Startup command. @@ -359,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/Kconfig b/lib/Kconfig index 884218a3c6..067307276e 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -54,6 +54,16 @@ source lib/dhry/Kconfig source lib/rsa/Kconfig +config TPM + bool "Trusted Platform Module (TPM) Support" + help + This enables support for TPMs which can be used to provide security + features for your board. The TPM can be connected via LPC or I2C + and a sandbox TPM is provided for testing purposes. Use the 'tpm' + command to interactive the TPM. Driver model support is provided + for the low-level TPM interface, but only one TPM is supported at + a time by the TPM library. + menu "Hashing Support" config SHA1 diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 81b54f88e8..29c5ccb214 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"), @@ -6,10 +6,11 @@ */ #include <common.h> -#include <stdarg.h> -#include <u-boot/sha1.h> +#include <dm.h> +#include <tis.h> #include <tpm.h> #include <asm/unaligned.h> +#include <u-boot/sha1.h> /* Internal error of TPM command library */ #define TPM_LIB_ERROR ((uint32_t)~0u) @@ -17,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, @@ -240,9 +240,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 +261,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) @@ -589,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 /** diff --git a/tools/patman/series.py b/tools/patman/series.py index a17a7d1de7..3399f2c8dd 100644 --- a/tools/patman/series.py +++ b/tools/patman/series.py @@ -12,7 +12,7 @@ import terminal # Series-xxx tags that we understand valid_series = ['to', 'cc', 'version', 'changes', 'prefix', 'notes', 'name', - 'cover-cc', 'process_log'] + 'cover_cc', 'process_log'] class Series(dict): """Holds information about a patch series, including all tags. |