Fix TPM driver to work with multiple vendor TPMs
Port u-boot patch for low-level driver: - Fix bug in traversal of vendor name list. - Sending "command ready" needs additional logic to handle TPMs that need that bit set twice: once to empty the read FIFOs and once to actualy set command ready. Change-Id: I57c280266b2e966c5b90e4f9e968426a33b93cf1 Signed-off-by: Duncan Laurie <dlaurie@chromium.org> Reviewed-on: http://review.coreboot.org/972 Tested-by: build bot (Jenkins)
This commit is contained in:
		
				
					committed by
					
						
						Stefan Reinauer
					
				
			
			
				
	
			
			
			
						parent
						
							95be1d6f46
						
					
				
				
					commit
					c908fc762c
				
			@@ -127,13 +127,31 @@ struct vendor_name {
 | 
			
		||||
	const struct device_name* dev_names;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct device_name atmel_devices[] = {
 | 
			
		||||
	{0x3204, "AT97SC3204"},
 | 
			
		||||
	{0xffff}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct device_name infineon_devices[] = {
 | 
			
		||||
	{0xb, "SLB9635 TT 1.2"},
 | 
			
		||||
	{0}
 | 
			
		||||
	{0x000b, "SLB9635 TT 1.2"},
 | 
			
		||||
	{0xffff}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct device_name nuvoton_devices[] = {
 | 
			
		||||
	{0x00fe, "NPCT420AA V2"},
 | 
			
		||||
	{0xffff}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct device_name stmicro_devices[] = {
 | 
			
		||||
	{0x0000, "ST33ZP24" },
 | 
			
		||||
	{0xffff}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct vendor_name vendor_names[] = {
 | 
			
		||||
	{0x1114, "Atmel", atmel_devices},
 | 
			
		||||
	{0x15d1, "Infineon", infineon_devices},
 | 
			
		||||
	{0x1050, "Nuvoton", nuvoton_devices},
 | 
			
		||||
	{0x104a, "ST Microelectronics", stmicro_devices},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -207,6 +225,44 @@ static u32 tis_wait_reg(u8 reg, u8 locality, u8 mask, u8 expected)
 | 
			
		||||
	return TPM_TIMEOUT_ERR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * PC Client Specific TPM Interface Specification section 11.2.12:
 | 
			
		||||
 *
 | 
			
		||||
 *  Software must be prepared to send two writes of a "1" to command ready
 | 
			
		||||
 *  field: the first to indicate successful read of all the data, thus
 | 
			
		||||
 *  clearing the data from the ReadFIFO and freeing the TPM's resources,
 | 
			
		||||
 *  and the second to indicate to the TPM it is about to send a new command.
 | 
			
		||||
 *
 | 
			
		||||
 * In practice not all TPMs behave the same so it is necessary to be
 | 
			
		||||
 * flexible when trying to set command ready.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success if the TPM is ready for transactions.
 | 
			
		||||
 * Returns TPM_TIMEOUT_ERR if the command ready bit does not get set.
 | 
			
		||||
 */
 | 
			
		||||
static int tis_command_ready(u8 locality)
 | 
			
		||||
{
 | 
			
		||||
	u32 status;
 | 
			
		||||
 | 
			
		||||
	/* 1st attempt to set command ready */
 | 
			
		||||
	tpm_write(TIS_STS_COMMAND_READY, locality, TIS_REG_STS);
 | 
			
		||||
 | 
			
		||||
	/* Wait for response */
 | 
			
		||||
	status = tpm_read(locality, TIS_REG_STS);
 | 
			
		||||
 | 
			
		||||
	/* Check if command ready is set yet */
 | 
			
		||||
	if (status & TIS_STS_COMMAND_READY)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	/* 2nd attempt to set command ready */
 | 
			
		||||
	tpm_write(TIS_STS_COMMAND_READY, locality, TIS_REG_STS);
 | 
			
		||||
 | 
			
		||||
	/* Wait for command ready to get set */
 | 
			
		||||
	status = tis_wait_reg(TIS_REG_STS, locality,
 | 
			
		||||
			      TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY);
 | 
			
		||||
 | 
			
		||||
	return (status == TPM_TIMEOUT_ERR) ? TPM_TIMEOUT_ERR : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Probe the TPM device and try determining its manufacturer/device name.
 | 
			
		||||
 *
 | 
			
		||||
@@ -215,15 +271,17 @@ static u32 tis_wait_reg(u8 reg, u8 locality, u8 mask, u8 expected)
 | 
			
		||||
 */
 | 
			
		||||
static u32 tis_probe(void)
 | 
			
		||||
{
 | 
			
		||||
	u32 didvid = tpm_read(0, TIS_REG_DID_VID);
 | 
			
		||||
	int i;
 | 
			
		||||
	const char *device_name = "unknown";
 | 
			
		||||
	const char *vendor_name = device_name;
 | 
			
		||||
	const struct device_name *dev;
 | 
			
		||||
	u32 didvid;
 | 
			
		||||
	u16 vid, did;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	if (vendor_dev_id)
 | 
			
		||||
		return 0;  /* Already probed. */
 | 
			
		||||
 | 
			
		||||
	didvid = tpm_read(0, TIS_REG_DID_VID);
 | 
			
		||||
	if (!didvid || (didvid == 0xffffffff)) {
 | 
			
		||||
		printf("%s: No TPM device found\n", __FUNCTION__);
 | 
			
		||||
		return TPM_DRIVER_ERR;
 | 
			
		||||
@@ -238,11 +296,13 @@ static u32 tis_probe(void)
 | 
			
		||||
		u16 known_did;
 | 
			
		||||
		if (vid == vendor_names[i].vendor_id) {
 | 
			
		||||
			vendor_name = vendor_names[i].vendor_name;
 | 
			
		||||
		} else {
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		while ((known_did = vendor_names[i].dev_names[j].dev_id) != 0) {
 | 
			
		||||
		dev = &vendor_names[i].dev_names[j];
 | 
			
		||||
		while ((known_did = dev->dev_id) != 0xffff) {
 | 
			
		||||
			if (known_did == did) {
 | 
			
		||||
				device_name =
 | 
			
		||||
					vendor_names[i].dev_names[j].dev_name;
 | 
			
		||||
				device_name = dev->dev_name;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			j++;
 | 
			
		||||
@@ -250,7 +310,7 @@ static u32 tis_probe(void)
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	/* this will have to be converted into debug printout */
 | 
			
		||||
	TPM_DEBUG("Found TPM %s by %s\n", device_name, vendor_name);
 | 
			
		||||
	printf("Found TPM %s by %s\n", device_name, vendor_name);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -446,7 +506,8 @@ static u32 tis_readresponse(u8 *buffer, size_t *len)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Tell the TPM that we are done. */
 | 
			
		||||
	tpm_write(TIS_STS_COMMAND_READY, locality, TIS_REG_STS);
 | 
			
		||||
	if (tis_command_ready(locality) == TPM_TIMEOUT_ERR)
 | 
			
		||||
		return TPM_DRIVER_ERR;
 | 
			
		||||
 | 
			
		||||
	*len = offset;
 | 
			
		||||
	return 0;
 | 
			
		||||
@@ -492,7 +553,11 @@ int tis_open(void)
 | 
			
		||||
		return TPM_DRIVER_ERR;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tpm_write(TIS_STS_COMMAND_READY, locality, TIS_REG_STS);
 | 
			
		||||
	/* Certain TPMs seem to need some delay here or they hang... */
 | 
			
		||||
	udelay(10);
 | 
			
		||||
 | 
			
		||||
	if (tis_command_ready(locality) == TPM_TIMEOUT_ERR)
 | 
			
		||||
		return TPM_DRIVER_ERR;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user