mirror of
				https://source.denx.de/u-boot/u-boot.git
				synced 2025-10-31 00:11:51 +01:00 
			
		
		
		
	Add the tpm2_tis_i2c driver that should support any TPMv2 compliant I2C chips, such as the NPCT75X chip. [Ilias rename priv_auto_alloc_size to priv_auto] Signed-off-by: Eddie James <eajames@linux.ibm.com> Reviewed-by: Joel Stanley <joel@jms.id.au> Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org> Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
		
			
				
	
	
		
			172 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			172 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| /*
 | |
|  * Copyright 2022 IBM Corp.
 | |
|  */
 | |
| 
 | |
| #include <common.h>
 | |
| #include <dm.h>
 | |
| #include <fdtdec.h>
 | |
| #include <i2c.h>
 | |
| #include <log.h>
 | |
| #include <tpm-v2.h>
 | |
| #include <linux/bitops.h>
 | |
| #include <linux/delay.h>
 | |
| #include <linux/errno.h>
 | |
| #include <linux/compiler.h>
 | |
| #include <linux/types.h>
 | |
| #include <linux/unaligned/be_byteshift.h>
 | |
| #include <asm-generic/gpio.h>
 | |
| 
 | |
| #include "tpm_tis.h"
 | |
| #include "tpm_internal.h"
 | |
| 
 | |
| struct tpm_tis_chip_data {
 | |
| 	unsigned int pcr_count;
 | |
| 	unsigned int pcr_select_min;
 | |
| };
 | |
| 
 | |
| static uint tpm_tis_i2c_address_to_register(u32 addr)
 | |
| {
 | |
| 	addr &= 0xFFF;
 | |
| 
 | |
| 	/*
 | |
| 	 * Adapt register addresses that have changed compared to older TIS
 | |
| 	 * version.
 | |
| 	 */
 | |
| 	switch (addr) {
 | |
| 	case TPM_ACCESS(0):
 | |
| 		return 0x04;
 | |
| 	case TPM_DID_VID(0):
 | |
| 		return 0x48;
 | |
| 	case TPM_RID(0):
 | |
| 		return 0x4C;
 | |
| 	default:
 | |
| 		return addr;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int tpm_tis_i2c_read(struct udevice *dev, u32 addr, u16 len, u8 *in)
 | |
| {
 | |
| 	int rc;
 | |
| 	int count = 0;
 | |
| 	uint reg = tpm_tis_i2c_address_to_register(addr);
 | |
| 
 | |
| 	do {
 | |
| 		rc = dm_i2c_read(dev, reg, in, len);
 | |
| 		udelay(SLEEP_DURATION_US);
 | |
| 	} while (rc && count++ < MAX_COUNT);
 | |
| 
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| static int tpm_tis_i2c_write(struct udevice *dev, u32 addr, u16 len,
 | |
| 			     const u8 *out)
 | |
| {
 | |
| 	int rc;
 | |
| 	int count = 0;
 | |
| 	uint reg = tpm_tis_i2c_address_to_register(addr);
 | |
| 
 | |
| 	do {
 | |
| 		rc = dm_i2c_write(dev, reg, out, len);
 | |
| 		udelay(SLEEP_DURATION_US);
 | |
| 	} while (rc && count++ < MAX_COUNT);
 | |
| 
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| static int tpm_tis_i2c_read32(struct udevice *dev, u32 addr, u32 *result)
 | |
| {
 | |
| 	__le32 result_le;
 | |
| 	int rc;
 | |
| 
 | |
| 	rc = tpm_tis_i2c_read(dev, addr, sizeof(u32), (u8 *)&result_le);
 | |
| 	if (!rc)
 | |
| 		*result = le32_to_cpu(result_le);
 | |
| 
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| static int tpm_tis_i2c_write32(struct udevice *dev, u32 addr, u32 value)
 | |
| {
 | |
| 	__le32 value_le = cpu_to_le32(value);
 | |
| 
 | |
| 	return tpm_tis_i2c_write(dev, addr, sizeof(value), (u8 *)&value_le);
 | |
| }
 | |
| 
 | |
| static struct tpm_tis_phy_ops phy_ops = {
 | |
| 	.read_bytes = tpm_tis_i2c_read,
 | |
| 	.write_bytes = tpm_tis_i2c_write,
 | |
| 	.read32 = tpm_tis_i2c_read32,
 | |
| 	.write32 = tpm_tis_i2c_write32,
 | |
| };
 | |
| 
 | |
| static int tpm_tis_i2c_probe(struct udevice *udev)
 | |
| {
 | |
| 	struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);
 | |
| 	struct tpm_chip_priv *priv = dev_get_uclass_priv(udev);
 | |
| 	int rc;
 | |
| 	u8 loc = 0;
 | |
| 
 | |
| 	tpm_tis_ops_register(udev, &phy_ops);
 | |
| 
 | |
| 	/*
 | |
| 	 * Force locality 0. The core driver doesn't actually write the
 | |
| 	 * locality register and instead just reads/writes various access
 | |
| 	 * bits of the selected locality.
 | |
| 	 */
 | |
| 	rc = dm_i2c_write(udev, 0, &loc, 1);
 | |
| 	if (rc)
 | |
| 		return rc;
 | |
| 
 | |
| 	rc = tpm_tis_init(udev);
 | |
| 	if (rc)
 | |
| 		return rc;
 | |
| 
 | |
| 	priv->pcr_count = drv_data->pcr_count;
 | |
| 	priv->pcr_select_min = drv_data->pcr_select_min;
 | |
| 	priv->version = TPM_V2;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int tpm_tis_i2c_remove(struct udevice *udev)
 | |
| {
 | |
| 	return tpm_tis_cleanup(udev);
 | |
| }
 | |
| 
 | |
| static const struct tpm_ops tpm_tis_i2c_ops = {
 | |
| 	.open = tpm_tis_open,
 | |
| 	.close = tpm_tis_close,
 | |
| 	.get_desc = tpm_tis_get_desc,
 | |
| 	.send = tpm_tis_send,
 | |
| 	.recv = tpm_tis_recv,
 | |
| 	.cleanup = tpm_tis_cleanup,
 | |
| };
 | |
| 
 | |
| static const struct tpm_tis_chip_data tpm_tis_std_chip_data = {
 | |
| 	.pcr_count = 24,
 | |
| 	.pcr_select_min = 3,
 | |
| };
 | |
| 
 | |
| static const struct udevice_id tpm_tis_i2c_ids[] = {
 | |
| 	{
 | |
| 		.compatible = "nuvoton,npct75x",
 | |
| 		.data = (ulong)&tpm_tis_std_chip_data,
 | |
| 	},
 | |
| 	{
 | |
| 		.compatible = "tcg,tpm-tis-i2c",
 | |
| 		.data = (ulong)&tpm_tis_std_chip_data,
 | |
| 	},
 | |
| 	{ }
 | |
| };
 | |
| 
 | |
| 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,
 | |
| 	.remove = tpm_tis_i2c_remove,
 | |
| 	.priv_auto = sizeof(struct tpm_chip),
 | |
| };
 |