/* * request_locality request the TPM locality * @param: chip, the chip description * @return: the active locality or EACCESS. */ static int request_locality(struct tpm_chip *chip) { unsigned long stop; long ret; struct tpm_stm_dev *tpm_dev; u8 data; if (check_locality(chip) == chip->vendor.locality) return chip->vendor.locality; tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); data = TPM_ACCESS_REQUEST_USE; ret = I2C_WRITE_DATA(tpm_dev, TPM_ACCESS, &data, 1); if (ret < 0) goto end; stop = jiffies + chip->vendor.timeout_a; /* Request locality is usually effective after the request */ do { if (check_locality(chip) >= 0) return chip->vendor.locality; msleep(TPM_TIMEOUT); } while (time_before(jiffies, stop)); ret = -EACCES; end: return ret; } /* request_locality() */
/* * request_locality request the TPM locality * @param: chip, the chip description * @return: the active locality or EACCESS. */ static int request_locality(struct tpm_chip *chip) { unsigned long stop; long rc; struct i2c_client *client; u8 data; client = (struct i2c_client *) TPM_VPRIV(chip); if (check_locality(chip) == chip->vendor.locality) return chip->vendor.locality; data = TPM_ACCESS_REQUEST_USE; rc = I2C_WRITE_DATA(client, TPM_ACCESS, &data, 1); if (rc < 0) goto end; if (chip->vendor.irq) { rc = wait_for_serirq_timeout(chip, (check_locality (chip) >= 0), chip->vendor.timeout_a); if (rc > 0) return chip->vendor.locality; } else{ stop = jiffies + chip->vendor.timeout_a; do { if (check_locality(chip) >= 0) return chip->vendor.locality; msleep(TPM_TIMEOUT); } while (time_before(jiffies, stop)); } rc = -EACCES; end: return rc; } /* request_locality() */
/* * clear_interruption * clear the TPM interrupt register. * @param: tpm, the chip description */ static void clear_interruption(struct i2c_client *client) { u8 interrupt; I2C_READ_DATA(client, TPM_INT_STATUS, &interrupt, 1); I2C_WRITE_DATA(client, TPM_INT_STATUS, &interrupt, 1); I2C_READ_DATA(client, TPM_INT_STATUS, &interrupt, 1); } /* clear_interruption() */
void ti_iic_do_write(struct ti_iic_softc *sc, uint32_t stat) { int len; DPRINTF(("ti_iic_do_write stat %#x\n", stat)); if (stat & I2C_IRQSTATUS_XDR) { len = I2C_READ_REG(sc, AM335X_I2C_BUFSTAT); len = I2C_BUFSTAT_TXSTAT(len); DPRINTF(("ti_iic_do_write xmit drain len %d left %d\n", len, I2C_READ_REG(sc, AM335X_I2C_CNT))); } else if (stat & I2C_IRQSTATUS_XRDY) { len = sc->sc_txthres + 1; DPRINTF(("ti_iic_do_write xmit len %d left %d\n", len, I2C_READ_REG(sc, AM335X_I2C_CNT))); } for (; sc->sc_bufidx < sc->sc_buflen && len > 0; sc->sc_bufidx++, len--) { DPRINTF(("ti_iic_do_write send b[%d]=0x%x\n", sc->sc_bufidx, sc->sc_buf[sc->sc_bufidx])); I2C_WRITE_DATA(sc, sc->sc_buf[sc->sc_bufidx]); } DPRINTF(("ti_iic_do_write done\n")); }
/* * clear_interruption * clear the TPM interrupt register. * @param: tpm, the chip description * @return: the TPM_INT_STATUS value */ static u8 clear_interruption(struct tpm_stm_dev *tpm_dev) { u8 interrupt; I2C_READ_DATA(tpm_dev, TPM_INT_STATUS, &interrupt, 1); I2C_WRITE_DATA(tpm_dev, TPM_INT_STATUS, &interrupt, 1); return interrupt; } /* clear_interruption() */
/* * release_locality release the active locality * @param: chip, the tpm chip description. */ static void release_locality(struct tpm_chip *chip) { struct i2c_client *client; u8 data; client = (struct i2c_client *) TPM_VPRIV(chip); data = TPM_ACCESS_ACTIVE_LOCALITY; I2C_WRITE_DATA(client, TPM_ACCESS, &data, 1); }
/* * release_locality release the active locality * @param: chip, the tpm chip description. */ static void release_locality(struct tpm_chip *chip) { struct tpm_stm_dev *tpm_dev; u8 data; tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); data = TPM_ACCESS_ACTIVE_LOCALITY; I2C_WRITE_DATA(tpm_dev, TPM_ACCESS, &data, 1); }
/* * tpm_stm_i2c_cancel, cancel is not implemented. * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h */ static void tpm_stm_i2c_cancel(struct tpm_chip *chip) { struct tpm_stm_dev *tpm_dev; u8 data; tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); data = TPM_STS_COMMAND_READY; I2C_WRITE_DATA(tpm_dev, TPM_STS, &data, 1); } /* tpm_stm_i2c_cancel() */
/* * tpm_stm_i2c_cancel, cancel is not implemented. * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h */ static void tpm_stm_i2c_cancel(struct tpm_chip *chip) { struct i2c_client *client; u8 data; client = (struct i2c_client *) TPM_VPRIV(chip); data = TPM_STS_COMMAND_READY; I2C_WRITE_DATA(client, TPM_STS, &data, 1); if (chip->vendor.irq) wait_for_serirq_timeout(chip, 1, chip->vendor.timeout_a); } /* tpm_stm_i2c_cancel() */
/* * tpm_st33_i2c_probe initialize the TPM device * @param: client, the i2c_client drescription (TPM I2C description). * @param: id, the i2c_device_id struct. * @return: 0 in case of success. * -1 in other case. */ static int tpm_st33_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { int err; u8 intmask; struct tpm_chip *chip; struct st33zp24_platform_data *platform_data; if (client == NULL) { pr_info("%s: i2c client is NULL. Device not accessible.\n", __func__); err = -ENODEV; goto end; } if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { dev_info(&client->dev, "client not i2c capable\n"); err = -ENODEV; goto end; } chip = tpm_register_hardware(&client->dev, &st_i2c_tpm); if (!chip) { dev_info(&client->dev, "fail chip\n"); err = -ENODEV; goto end; } platform_data = client->dev.platform_data; if (!platform_data) { dev_info(&client->dev, "chip not available\n"); err = -ENODEV; goto _tpm_clean_answer; } platform_data->tpm_i2c_buffer[0] = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL); if (platform_data->tpm_i2c_buffer[0] == NULL) { err = -ENOMEM; goto _tpm_clean_answer; } platform_data->tpm_i2c_buffer[1] = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL); if (platform_data->tpm_i2c_buffer[1] == NULL) { err = -ENOMEM; goto _tpm_clean_response1; } TPM_VPRIV(chip) = client; chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); chip->vendor.locality = LOCALITY0; if (power_mgt) { err = gpio_request(platform_data->io_lpcpd, "TPM IO_LPCPD"); if (err) goto _gpio_init1; gpio_set_value(platform_data->io_lpcpd, 1); } if (interrupts) { init_completion(&platform_data->irq_detection); if (request_locality(chip) != LOCALITY0) { err = -ENODEV; goto _tpm_clean_response2; } err = gpio_request(platform_data->io_serirq, "TPM IO_SERIRQ"); if (err) goto _gpio_init2; clear_interruption(client); err = request_irq(gpio_to_irq(platform_data->io_serirq), &tpm_ioserirq_handler, IRQF_TRIGGER_HIGH, "TPM SERIRQ management", chip); if (err < 0) { dev_err(chip->dev , "TPM SERIRQ signals %d not available\n", gpio_to_irq(platform_data->io_serirq)); goto _irq_set; } err = I2C_READ_DATA(client, TPM_INT_ENABLE, &intmask, 1); if (err < 0) goto _irq_set; intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_FIFO_AVALAIBLE_INT | TPM_INTF_WAKE_UP_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_STS_VALID_INT | TPM_INTF_DATA_AVAIL_INT; err = I2C_WRITE_DATA(client, TPM_INT_ENABLE, &intmask, 1); if (err < 0) goto _irq_set; intmask = TPM_GLOBAL_INT_ENABLE; err = I2C_WRITE_DATA(client, (TPM_INT_ENABLE + 3), &intmask, 1); if (err < 0) goto _irq_set; err = I2C_READ_DATA(client, TPM_INT_STATUS, &intmask, 1); if (err < 0) goto _irq_set; chip->vendor.irq = interrupts; tpm_gen_interrupt(chip); } tpm_get_timeouts(chip); i2c_set_clientdata(client, chip); dev_info(chip->dev, "TPM I2C Initialized\n"); return 0; _irq_set: free_irq(gpio_to_irq(platform_data->io_serirq), (void *) chip); _gpio_init2: if (interrupts) gpio_free(platform_data->io_serirq); _gpio_init1: if (power_mgt) gpio_free(platform_data->io_lpcpd); _tpm_clean_response2: kzfree(platform_data->tpm_i2c_buffer[1]); platform_data->tpm_i2c_buffer[1] = NULL; _tpm_clean_response1: kzfree(platform_data->tpm_i2c_buffer[0]); platform_data->tpm_i2c_buffer[0] = NULL; _tpm_clean_answer: tpm_remove_hardware(chip->dev); end: pr_info("TPM I2C initialisation fail\n"); return err; }
/* * tpm_stm_i2c_send send TPM commands through the I2C bus. * * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h * @param: buf, the buffer to send. * @param: count, the number of bytes to send. * @return: In case of success the number of bytes sent. * In other case, a < 0 value describing the issue. */ static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf, size_t len) { u32 status, burstcnt = 0, i, size; int ret; u8 data; struct i2c_client *client; if (chip == NULL) return -EBUSY; if (len < TPM_HEADER_SIZE) return -EBUSY; client = (struct i2c_client *)TPM_VPRIV(chip); client->flags = 0; ret = request_locality(chip); if (ret < 0) return ret; status = tpm_stm_i2c_status(chip); if ((status & TPM_STS_COMMAND_READY) == 0) { tpm_stm_i2c_cancel(chip); if (wait_for_stat (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b, &chip->vendor.int_queue) < 0) { ret = -ETIME; goto out_err; } } for (i = 0 ; i < len - 1 ;) { burstcnt = get_burstcount(chip); size = min_t(int, len - i - 1, burstcnt); ret = I2C_WRITE_DATA(client, TPM_DATA_FIFO, buf, size); if (ret < 0) goto out_err; i += size; } status = tpm_stm_i2c_status(chip); if ((status & TPM_STS_DATA_EXPECT) == 0) { ret = -EIO; goto out_err; } ret = I2C_WRITE_DATA(client, TPM_DATA_FIFO, buf + len - 1, 1); if (ret < 0) goto out_err; status = tpm_stm_i2c_status(chip); if ((status & TPM_STS_DATA_EXPECT) != 0) { ret = -EIO; goto out_err; } data = TPM_STS_GO; I2C_WRITE_DATA(client, TPM_STS, &data, 1); return len; out_err: tpm_stm_i2c_cancel(chip); release_locality(chip); return ret; }
/* * tpm_stm_i2c_probe initialize the TPM device * @param: client, the i2c_client drescription (TPM I2C description). * @param: id, the i2c_device_id struct. * @return: 0 in case of success. * -1 in other case. */ static int tpm_stm_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { int ret; u8 intmask = 0; struct tpm_chip *chip; struct st33zp24_platform_data *platform_data; struct tpm_stm_dev *tpm_dev; if (!client) { pr_info("%s: i2c client is NULL. Device not accessible.\n", __func__); return -ENODEV; } if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { dev_info(&client->dev, "client not i2c capable\n"); return -ENODEV; } tpm_dev = devm_kzalloc(&client->dev, sizeof(struct tpm_stm_dev), GFP_KERNEL); if (!tpm_dev) return -ENOMEM; chip = tpmm_chip_alloc(&client->dev, &st_i2c_tpm); if (IS_ERR(chip)) return PTR_ERR(chip); TPM_VPRIV(chip) = tpm_dev; tpm_dev->client = client; platform_data = client->dev.platform_data; if (!platform_data && client->dev.of_node) { ret = tpm_stm_i2c_of_request_resources(chip); if (ret) goto _tpm_clean_answer; } else if (platform_data) { ret = tpm_stm_i2c_request_resources(client, chip); if (ret) goto _tpm_clean_answer; } chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); chip->vendor.locality = LOCALITY0; if (client->irq) { /* INTERRUPT Setup */ init_waitqueue_head(&chip->vendor.read_queue); tpm_dev->intrs = 0; if (request_locality(chip) != LOCALITY0) { ret = -ENODEV; goto _tpm_clean_answer; } clear_interruption(tpm_dev); ret = devm_request_irq(&client->dev, client->irq, tpm_ioserirq_handler, IRQF_TRIGGER_HIGH, "TPM SERIRQ management", chip); if (ret < 0) { dev_err(chip->pdev, "TPM SERIRQ signals %d not available\n", client->irq); goto _tpm_clean_answer; } intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_STS_VALID_INT | TPM_INTF_DATA_AVAIL_INT; ret = I2C_WRITE_DATA(tpm_dev, TPM_INT_ENABLE, &intmask, 1); if (ret < 0) goto _tpm_clean_answer; intmask = TPM_GLOBAL_INT_ENABLE; ret = I2C_WRITE_DATA(tpm_dev, (TPM_INT_ENABLE + 3), &intmask, 1); if (ret < 0) goto _tpm_clean_answer; chip->vendor.irq = client->irq; disable_irq_nosync(chip->vendor.irq); tpm_gen_interrupt(chip); } tpm_get_timeouts(chip); tpm_do_selftest(chip); return tpm_chip_register(chip); _tpm_clean_answer: dev_info(chip->pdev, "TPM I2C initialisation fail\n"); return ret; }