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
};
};
+ 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 {
<&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>;
i2c@12E10000 { /* i2c9 */
clock-frequency = <400000>;
- tpm@20 {
- compatible = "infineon,slb9645tt";
- reg = <0x20>;
+ tpm@20 {
+ compatible = "infineon,slb9645tt";
+ reg = <0x20>;
};
};
i2c@12E10000 { /* i2c9 */
clock-frequency = <400000>;
- tpm@20 {
- compatible = "infineon,slb9645tt";
- reg = <0x20>;
+ tpm@20 {
+ compatible = "infineon,slb9645tt";
+ reg = <0x20>;
};
};
reg = <0 0>;
compatible = "sandbox,i2c";
clock-frequency = <400000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c0>;
eeprom@2c {
reg = <0x2c>;
compatible = "i2c-eeprom";
};
};
+ 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>;
sides = <4>;
};
+ tpm {
+ compatible = "google,sandbox-tpm";
+ };
+
triangle {
compatible = "demo-shape";
colour = "cyan";
uart0: serial {
compatible = "sandbox,serial";
sandbox,text-colour = "cyan";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_serial0>;
};
usb@0 {
};
};
+ tpm {
+ reg = <0xfed40000 0x5000>;
+ compatible = "infineon,slb9635lpc";
+ };
+
microcode {
update@0 {
#include "microcode/m12306a9_0000001b.dtsi"
};
};
+ tpm {
+ reg = <0xfed40000 0x5000>;
+ compatible = "infineon,slb9635lpc";
+ };
+
};
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);
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
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
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
/**
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)
" 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)
#include <common.h>
#include <command.h>
+#include <dm.h>
#include <malloc.h>
#include <tpm.h>
#include <asm/unaligned.h>
size_t count, length;
int i;
+ if (!bytes)
+ return NULL;
length = strlen(bytes);
count = length / 2;
}
/**
- * 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;
+ }
}
/**
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,
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,
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,
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,
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,
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,
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,
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,
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,
print_byte_string(cap, count);
}
- return convert_return_code(rc);
+ return report_return_code(rc);
}
#define TPM_COMMAND_NO_ARG(cmd) \
{ \
if (argc != 1) \
return CMD_RET_USAGE; \
- return convert_return_code(cmd()); \
+ return report_return_code(cmd()); \
}
TPM_COMMAND_NO_ARG(tpm_init)
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[])
{
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,
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,
}
free(data);
- return convert_return_code(err);
+ return report_return_code(err);
}
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
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,
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,
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)
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,
"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"
--- /dev/null
+/*
+ * 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");
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
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
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
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
# 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
# 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
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
# 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) # "
# 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
CONFIG_SOUND_WM8994=y
CONFIG_USB=y
CONFIG_DM_USB=y
+CONFIG_TPM=y
CONFIG_ERRNO_STR=y
CONFIG_SYS_PROMPT="Peach-Pi # "
# 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
CONFIG_SOUND_WM8994=y
CONFIG_USB=y
CONFIG_DM_USB=y
+CONFIG_TPM=y
CONFIG_ERRNO_STR=y
CONFIG_SYS_PROMPT="Peach-Pit # "
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
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
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
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
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
CONFIG_SOUND_WM8994=y
CONFIG_USB=y
CONFIG_DM_USB=y
+CONFIG_TPM=y
CONFIG_ERRNO_STR=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
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
CONFIG_SOUND_WM8994=y
CONFIG_USB=y
CONFIG_DM_USB=y
+CONFIG_TPM=y
CONFIG_ERRNO_STR=y
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"
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
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
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
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
#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>
struct uclass *uc;
int size, ret = 0;
- *devp = NULL;
+ if (devp)
+ *devp = NULL;
if (!name)
return -EINVAL;
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;
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;
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);
}
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++) {
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;
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++) {
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);
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
--- /dev/null
+#
+# 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
--- /dev/null
+obj-y += pinctrl-uclass.o
+obj-$(CONFIG_$(SPL_)PINCTRL_GENERIC) += pinctrl-generic.o
+
+obj-$(CONFIG_PINCTRL_SANDBOX) += pinctrl-sandbox.o
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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,
+};
--- /dev/null
+/*
+ * 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",
+};
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 */
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);
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;
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;
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",
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) {
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;
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) {
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) {
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) {
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",
+#
+# 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
# 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
--- /dev/null
+/*
+ * 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),
+};
+++ /dev/null
-/*
- * 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;
-}
/*
- * 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>
--- /dev/null
+/*
+ * 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
*
* 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
*
* 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 */
}
* 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
* 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];
/* 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)
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;
}
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;
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)
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 {
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.
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),
+};
* 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)*/
#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 {
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
*/
#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: "
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 */
#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);
}
* @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;
}
* @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);
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;
*/
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);
}
/* 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;
}
*
* 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 {
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) {
/*
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)
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),
+};
*/
#include <common.h>
+#include <dm.h>
+#include <tpm.h>
#include <asm/state.h>
#include <asm/unaligned.h>
#include <linux/crc8.h>
*/
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
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;
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);
}
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;
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),
+};
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"))
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;
/*
* 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;
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;
}
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
#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
#undef CONFIG_CMD_SF_TEST
-#undef CONFIG_TPM
-#undef CONFIG_TPM_TIS_LPC
#undef CONFIG_TPM_TIS_BASE_ADDRESS
#undef CONFIG_CMD_IMLS
#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
#endif
/* Generic TPM interfaced through LPC bus */
-#define CONFIG_TPM
-#define CONFIG_TPM_TIS_LPC
#define CONFIG_TPM_TIS_BASE_ADDRESS 0xfed40000
/*-----------------------------------------------------------------------
* 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,
* @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,
* @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);
* @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,
--- /dev/null
+/*
+ * 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 */
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 */
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 */
* 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
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 */
#ifndef __TIS_H
#define __TIS_H
+#ifndef CONFIG_DM_TPM
+
#include <common.h>
/* Low-level interface to access TPM */
*/
int tis_sendrecv(const uint8_t *sendbuf, size_t send_size, uint8_t *recvbuf,
size_t *recv_len);
+#endif
#endif /* __TIS_H */
* 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,
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)
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.
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 */
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
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"),
*/
#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)
/* 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,
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;
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)
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
/**
# 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.