diff options
Diffstat (limited to 'drivers/char/tpm/tpm_i2c_infineon.c')
-rw-r--r-- | drivers/char/tpm/tpm_i2c_infineon.c | 180 |
1 files changed, 137 insertions, 43 deletions
diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index 8fe7ac3d095b..37d5dcc10ea7 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Infineon Technologies + * Copyright (C) 2012,2013 Infineon Technologies * * Authors: * Peter Huewe <peter.huewe@infineon.com> @@ -56,13 +56,21 @@ #define TPM_TIMEOUT_US_HI (TPM_TIMEOUT_US_LOW + 2000) /* expected value for DIDVID register */ -#define TPM_TIS_I2C_DID_VID 0x000b15d1L +#define TPM_TIS_I2C_DID_VID_9635 0xd1150b00L +#define TPM_TIS_I2C_DID_VID_9645 0x001a15d1L + +enum i2c_chip_type { + SLB9635, + SLB9645, + UNKNOWN, +}; /* Structure to store I2C TPM specific stuff */ struct tpm_inf_dev { struct i2c_client *client; u8 buf[TPM_BUFSIZE + sizeof(u8)]; /* max. buffer size + addr */ struct tpm_chip *chip; + enum i2c_chip_type chip_type; }; static struct tpm_inf_dev tpm_dev; @@ -90,10 +98,20 @@ static struct i2c_driver tpm_tis_i2c_driver; static int iic_tpm_read(u8 addr, u8 *buffer, size_t len) { - struct i2c_msg msg1 = { tpm_dev.client->addr, 0, 1, &addr }; - struct i2c_msg msg2 = { tpm_dev.client->addr, I2C_M_RD, len, buffer }; + struct i2c_msg msg1 = { + .addr = tpm_dev.client->addr, + .len = 1, + .buf = &addr + }; + struct i2c_msg msg2 = { + .addr = tpm_dev.client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = buffer + }; + struct i2c_msg msgs[] = {msg1, msg2}; - int rc; + int rc = 0; int count; /* Lock the adapter for the duration of the whole sequence. */ @@ -101,30 +119,53 @@ static int iic_tpm_read(u8 addr, u8 *buffer, size_t len) return -EOPNOTSUPP; i2c_lock_adapter(tpm_dev.client->adapter); - for (count = 0; count < MAX_COUNT; count++) { - rc = __i2c_transfer(tpm_dev.client->adapter, &msg1, 1); - if (rc > 0) - break; /* break here to skip sleep */ - - usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); - } - - if (rc <= 0) - goto out; - - /* 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++) { - usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); - rc = __i2c_transfer(tpm_dev.client->adapter, &msg2, 1); - if (rc > 0) - break; + if (tpm_dev.chip_type == SLB9645) { + /* use a combined read for newer chips + * unfortunately the smbus functions are not suitable due to + * the 32 byte limit of the smbus. + * retries should usually not be needed, but are kept just to + * be on the safe side. + */ + for (count = 0; count < MAX_COUNT; count++) { + rc = __i2c_transfer(tpm_dev.client->adapter, msgs, 2); + if (rc > 0) + break; /* break here to skip sleep */ + usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); + } + } else { + /* slb9635 protocol should work in all cases */ + for (count = 0; count < MAX_COUNT; count++) { + rc = __i2c_transfer(tpm_dev.client->adapter, &msg1, 1); + if (rc > 0) + break; /* break here to skip sleep */ + + usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); + } + if (rc <= 0) + goto out; + + /* 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++) { + usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); + rc = __i2c_transfer(tpm_dev.client->adapter, &msg2, 1); + if (rc > 0) + break; + } } out: i2c_unlock_adapter(tpm_dev.client->adapter); + /* take care of 'guard time' */ + usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); + + /* __i2c_transfer returns the number of successfully transferred + * messages. + * So rc should be greater than 0 here otherwise we have an error. + */ if (rc <= 0) return -EIO; @@ -138,7 +179,11 @@ static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len, int rc = -EIO; int count; - struct i2c_msg msg1 = { tpm_dev.client->addr, 0, len + 1, tpm_dev.buf }; + struct i2c_msg msg1 = { + .addr = tpm_dev.client->addr, + .len = len + 1, + .buf = tpm_dev.buf + }; if (len > TPM_BUFSIZE) return -EINVAL; @@ -154,16 +199,24 @@ static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len, /* * NOTE: We have to use these special mechanisms here and unfortunately * cannot rely on the standard behavior of i2c_transfer. + * Even for newer chips the smbus functions are not + * suitable due to the 32 byte limit of the smbus. */ for (count = 0; count < max_count; count++) { rc = __i2c_transfer(tpm_dev.client->adapter, &msg1, 1); if (rc > 0) break; - usleep_range(sleep_low, sleep_hi); } i2c_unlock_adapter(tpm_dev.client->adapter); + /* take care of 'guard time' */ + usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); + + /* __i2c_transfer returns the number of successfully transferred + * messages. + * So rc should be greater than 0 here otherwise we have an error. + */ if (rc <= 0) return -EIO; @@ -283,11 +336,18 @@ static int request_locality(struct tpm_chip *chip, int loc) static u8 tpm_tis_i2c_status(struct tpm_chip *chip) { /* 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) - return 0; - else - return buf; + u8 buf = 0xFF; + u8 i = 0; + + do { + if (iic_tpm_read(TPM_STS(chip->vendor.locality), &buf, 1) < 0) + return 0; + + i++; + /* if locallity is set STS should not be 0xFF */ + } while ((buf == 0xFF) && i < 10); + + return buf; } static void tpm_tis_i2c_ready(struct tpm_chip *chip) @@ -328,7 +388,7 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, /* check current status */ *status = tpm_tis_i2c_status(chip); - if ((*status & mask) == mask) + if ((*status != 0xFF) && (*status & mask) == mask) return 0; stop = jiffies + timeout; @@ -372,7 +432,6 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) /* avoid endless loop in case of broken HW */ if (retries > MAX_COUNT_LONG) return -EIO; - } return size; } @@ -480,7 +539,6 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) rc = -EIO; goto out_err; } - } /* write last byte */ @@ -568,6 +626,7 @@ static int tpm_tis_i2c_init(struct device *dev) chip = tpm_register_hardware(dev, &tpm_tis_i2c); if (!chip) { + dev_err(dev, "could not register hardware\n"); rc = -ENODEV; goto out_err; } @@ -582,20 +641,24 @@ static int tpm_tis_i2c_init(struct device *dev) chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); if (request_locality(chip, 0) != 0) { + dev_err(dev, "could not request locality\n"); rc = -ENODEV; goto out_vendor; } /* read four bytes from DID_VID register */ if (iic_tpm_read(TPM_DID_VID(0), (u8 *)&vendor, 4) < 0) { + dev_err(dev, "could not read vendor id\n"); rc = -EIO; goto out_release; } - /* create DID_VID register value, after swapping to little-endian */ - vendor = be32_to_cpu((__be32) vendor); - - if (vendor != TPM_TIS_I2C_DID_VID) { + if (vendor == TPM_TIS_I2C_DID_VID_9645) { + tpm_dev.chip_type = SLB9645; + } else if (vendor == TPM_TIS_I2C_DID_VID_9635) { + tpm_dev.chip_type = SLB9635; + } else { + dev_err(dev, "vendor id did not match! ID was %08x\n", vendor); rc = -ENODEV; goto out_release; } @@ -631,22 +694,53 @@ out_err: static const struct i2c_device_id tpm_tis_i2c_table[] = { {"tpm_i2c_infineon", 0}, + {"slb9635tt", 0}, + {"slb9645tt", 1}, {}, }; MODULE_DEVICE_TABLE(i2c, tpm_tis_i2c_table); + +#ifdef CONFIG_OF +static const struct of_device_id tpm_tis_i2c_of_match[] = { + { + .name = "tpm_i2c_infineon", + .type = "tpm", + .compatible = "infineon,tpm_i2c_infineon", + .data = (void *)0 + }, + { + .name = "slb9635tt", + .type = "tpm", + .compatible = "infineon,slb9635tt", + .data = (void *)0 + }, + { + .name = "slb9645tt", + .type = "tpm", + .compatible = "infineon,slb9645tt", + .data = (void *)1 + }, + {}, +}; +MODULE_DEVICE_TABLE(of, tpm_tis_i2c_of_match); +#endif + static SIMPLE_DEV_PM_OPS(tpm_tis_i2c_ops, tpm_pm_suspend, tpm_pm_resume); static int tpm_tis_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { int rc; - if (tpm_dev.client != NULL) + struct device *dev = &(client->dev); + + if (tpm_dev.client != NULL) { + dev_err(dev, "This driver only supports one client at a time\n"); return -EBUSY; /* We only support one client */ + } if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - dev_err(&client->dev, - "no algorithms associated to the i2c bus\n"); + dev_err(dev, "no algorithms associated to the i2c bus\n"); return -ENODEV; } @@ -682,7 +776,6 @@ static int tpm_tis_i2c_remove(struct i2c_client *client) } static struct i2c_driver tpm_tis_i2c_driver = { - .id_table = tpm_tis_i2c_table, .probe = tpm_tis_i2c_probe, .remove = tpm_tis_i2c_remove, @@ -690,11 +783,12 @@ static struct i2c_driver tpm_tis_i2c_driver = { .name = "tpm_i2c_infineon", .owner = THIS_MODULE, .pm = &tpm_tis_i2c_ops, + .of_match_table = of_match_ptr(tpm_tis_i2c_of_match), }, }; module_i2c_driver(tpm_tis_i2c_driver); MODULE_AUTHOR("Peter Huewe <peter.huewe@infineon.com>"); MODULE_DESCRIPTION("TPM TIS I2C Infineon Driver"); -MODULE_VERSION("2.1.5"); +MODULE_VERSION("2.2.0"); MODULE_LICENSE("GPL"); |