Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,30 @@
# SPDX-License-Identifier: GPL-2.0-only
config TCG_TIS_ST33ZP24
tristate
help
STMicroelectronics ST33ZP24 core driver. It implements the core
TPM1.2 logic and hooks into the TPM kernel APIs. Physical layers will
register against it.
To compile this driver as a module, choose m here. The module will be called
tpm_st33zp24.
config TCG_TIS_ST33ZP24_I2C
tristate "STMicroelectronics TPM Interface Specification 1.2 Interface (I2C)"
depends on I2C
select TCG_TIS_ST33ZP24
help
This module adds support for the STMicroelectronics TPM security chip
ST33ZP24 with i2c interface.
To compile this driver as a module, choose M here; the module will be
called tpm_st33zp24_i2c.
config TCG_TIS_ST33ZP24_SPI
tristate "STMicroelectronics TPM Interface Specification 1.2 Interface (SPI)"
depends on SPI
select TCG_TIS_ST33ZP24
help
This module adds support for the STMicroelectronics TPM security chip
ST33ZP24 with spi interface.
To compile this driver as a module, choose M here; the module will be
called tpm_st33zp24_spi.
@@ -0,0 +1,13 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ST33ZP24 TPM 1.2 driver
#
tpm_st33zp24-objs = st33zp24.o
obj-$(CONFIG_TCG_TIS_ST33ZP24) += tpm_st33zp24.o
tpm_st33zp24_i2c-objs = i2c.o
obj-$(CONFIG_TCG_TIS_ST33ZP24_I2C) += tpm_st33zp24_i2c.o
tpm_st33zp24_spi-objs = spi.o
obj-$(CONFIG_TCG_TIS_ST33ZP24_SPI) += tpm_st33zp24_spi.o
+173
View File
@@ -0,0 +1,173 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24
* Copyright (C) 2009 - 2016 STMicroelectronics
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/of.h>
#include <linux/acpi.h>
#include <linux/tpm.h>
#include "../tpm.h"
#include "st33zp24.h"
#define TPM_DUMMY_BYTE 0xAA
struct st33zp24_i2c_phy {
struct i2c_client *client;
u8 buf[ST33ZP24_BUFSIZE + 1];
};
/*
* write8_reg
* Send byte to the TIS register according to the ST33ZP24 I2C protocol.
* @param: tpm_register, the tpm tis register where the data should be written
* @param: tpm_data, the tpm_data to write inside the tpm_register
* @param: tpm_size, The length of the data
* @return: Returns negative errno, or else the number of bytes written.
*/
static int write8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size)
{
struct st33zp24_i2c_phy *phy = phy_id;
phy->buf[0] = tpm_register;
memcpy(phy->buf + 1, tpm_data, tpm_size);
return i2c_master_send(phy->client, phy->buf, tpm_size + 1);
} /* write8_reg() */
/*
* read8_reg
* Recv byte from the TIS register according to the ST33ZP24 I2C protocol.
* @param: tpm_register, the tpm tis register where the data should be read
* @param: tpm_data, the TPM response
* @param: tpm_size, tpm TPM response size to read.
* @return: number of byte read successfully: should be one if success.
*/
static int read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size)
{
struct st33zp24_i2c_phy *phy = phy_id;
u8 status = 0;
u8 data;
data = TPM_DUMMY_BYTE;
status = write8_reg(phy, tpm_register, &data, 1);
if (status == 2)
status = i2c_master_recv(phy->client, tpm_data, tpm_size);
return status;
} /* read8_reg() */
/*
* st33zp24_i2c_send
* Send byte to the TIS register according to the ST33ZP24 I2C protocol.
* @param: phy_id, the phy description
* @param: tpm_register, the tpm tis register where the data should be written
* @param: tpm_data, the tpm_data to write inside the tpm_register
* @param: tpm_size, the length of the data
* @return: number of byte written successfully: should be one if success.
*/
static int st33zp24_i2c_send(void *phy_id, u8 tpm_register, u8 *tpm_data,
int tpm_size)
{
return write8_reg(phy_id, tpm_register | TPM_WRITE_DIRECTION, tpm_data,
tpm_size);
}
/*
* st33zp24_i2c_recv
* Recv byte from the TIS register according to the ST33ZP24 I2C protocol.
* @param: phy_id, the phy description
* @param: tpm_register, the tpm tis register where the data should be read
* @param: tpm_data, the TPM response
* @param: tpm_size, tpm TPM response size to read.
* @return: number of byte read successfully: should be one if success.
*/
static int st33zp24_i2c_recv(void *phy_id, u8 tpm_register, u8 *tpm_data,
int tpm_size)
{
return read8_reg(phy_id, tpm_register, tpm_data, tpm_size);
}
static const struct st33zp24_phy_ops i2c_phy_ops = {
.send = st33zp24_i2c_send,
.recv = st33zp24_i2c_recv,
};
/*
* st33zp24_i2c_probe initialize the TPM device
* @param: client, the i2c_client description (TPM I2C description).
* @param: id, the i2c_device_id struct.
* @return: 0 in case of success.
* -1 in other case.
*/
static int st33zp24_i2c_probe(struct i2c_client *client)
{
struct st33zp24_i2c_phy *phy;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_info(&client->dev, "client not i2c capable\n");
return -ENODEV;
}
phy = devm_kzalloc(&client->dev, sizeof(struct st33zp24_i2c_phy),
GFP_KERNEL);
if (!phy)
return -ENOMEM;
phy->client = client;
return st33zp24_probe(phy, &i2c_phy_ops, &client->dev, client->irq);
}
/*
* st33zp24_i2c_remove remove the TPM device
* @param: client, the i2c_client description (TPM I2C description).
* @return: 0 in case of success.
*/
static void st33zp24_i2c_remove(struct i2c_client *client)
{
struct tpm_chip *chip = i2c_get_clientdata(client);
st33zp24_remove(chip);
}
static const struct i2c_device_id st33zp24_i2c_id[] = {
{ TPM_ST33_I2C },
{}
};
MODULE_DEVICE_TABLE(i2c, st33zp24_i2c_id);
static const struct of_device_id of_st33zp24_i2c_match[] __maybe_unused = {
{ .compatible = "st,st33zp24-i2c", },
{}
};
MODULE_DEVICE_TABLE(of, of_st33zp24_i2c_match);
static const struct acpi_device_id st33zp24_i2c_acpi_match[] __maybe_unused = {
{"SMO3324"},
{}
};
MODULE_DEVICE_TABLE(acpi, st33zp24_i2c_acpi_match);
static SIMPLE_DEV_PM_OPS(st33zp24_i2c_ops, st33zp24_pm_suspend,
st33zp24_pm_resume);
static struct i2c_driver st33zp24_i2c_driver = {
.driver = {
.name = TPM_ST33_I2C,
.pm = &st33zp24_i2c_ops,
.of_match_table = of_match_ptr(of_st33zp24_i2c_match),
.acpi_match_table = ACPI_PTR(st33zp24_i2c_acpi_match),
},
.probe = st33zp24_i2c_probe,
.remove = st33zp24_i2c_remove,
.id_table = st33zp24_i2c_id
};
module_i2c_driver(st33zp24_i2c_driver);
MODULE_AUTHOR("TPM support <TPMsupport@list.st.com>");
MODULE_DESCRIPTION("STM TPM 1.2 I2C ST33 Driver");
MODULE_VERSION("1.3.0");
MODULE_LICENSE("GPL");
+290
View File
@@ -0,0 +1,290 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* STMicroelectronics TPM SPI Linux driver for TPM ST33ZP24
* Copyright (C) 2009 - 2016 STMicroelectronics
*/
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/of.h>
#include <linux/acpi.h>
#include <linux/tpm.h>
#include "../tpm.h"
#include "st33zp24.h"
#define TPM_DATA_FIFO 0x24
#define TPM_INTF_CAPABILITY 0x14
#define TPM_DUMMY_BYTE 0x00
#define MAX_SPI_LATENCY 15
#define LOCALITY0 0
#define ST33ZP24_OK 0x5A
#define ST33ZP24_UNDEFINED_ERR 0x80
#define ST33ZP24_BADLOCALITY 0x81
#define ST33ZP24_TISREGISTER_UNKNOWN 0x82
#define ST33ZP24_LOCALITY_NOT_ACTIVATED 0x83
#define ST33ZP24_HASH_END_BEFORE_HASH_START 0x84
#define ST33ZP24_BAD_COMMAND_ORDER 0x85
#define ST33ZP24_INCORECT_RECEIVED_LENGTH 0x86
#define ST33ZP24_TPM_FIFO_OVERFLOW 0x89
#define ST33ZP24_UNEXPECTED_READ_FIFO 0x8A
#define ST33ZP24_UNEXPECTED_WRITE_FIFO 0x8B
#define ST33ZP24_CMDRDY_SET_WHEN_PROCESSING_HASH_END 0x90
#define ST33ZP24_DUMMY_BYTES 0x00
/*
* TPM command can be up to 2048 byte, A TPM response can be up to
* 1024 byte.
* Between command and response, there are latency byte (up to 15
* usually on st33zp24 2 are enough).
*
* Overall when sending a command and expecting an answer we need if
* worst case:
* 2048 (for the TPM command) + 1024 (for the TPM answer). We need
* some latency byte before the answer is available (max 15).
* We have 2048 + 1024 + 15.
*/
#define ST33ZP24_SPI_BUFFER_SIZE (ST33ZP24_BUFSIZE + (ST33ZP24_BUFSIZE / 2) +\
MAX_SPI_LATENCY)
struct st33zp24_spi_phy {
struct spi_device *spi_device;
u8 tx_buf[ST33ZP24_SPI_BUFFER_SIZE];
u8 rx_buf[ST33ZP24_SPI_BUFFER_SIZE];
int latency;
};
static int st33zp24_status_to_errno(u8 code)
{
switch (code) {
case ST33ZP24_OK:
return 0;
case ST33ZP24_UNDEFINED_ERR:
case ST33ZP24_BADLOCALITY:
case ST33ZP24_TISREGISTER_UNKNOWN:
case ST33ZP24_LOCALITY_NOT_ACTIVATED:
case ST33ZP24_HASH_END_BEFORE_HASH_START:
case ST33ZP24_BAD_COMMAND_ORDER:
case ST33ZP24_UNEXPECTED_READ_FIFO:
case ST33ZP24_UNEXPECTED_WRITE_FIFO:
case ST33ZP24_CMDRDY_SET_WHEN_PROCESSING_HASH_END:
return -EPROTO;
case ST33ZP24_INCORECT_RECEIVED_LENGTH:
case ST33ZP24_TPM_FIFO_OVERFLOW:
return -EMSGSIZE;
case ST33ZP24_DUMMY_BYTES:
return -ENOSYS;
}
return code;
}
/*
* st33zp24_spi_send
* Send byte to the TIS register according to the ST33ZP24 SPI protocol.
* @param: phy_id, the phy description
* @param: tpm_register, the tpm tis register where the data should be written
* @param: tpm_data, the tpm_data to write inside the tpm_register
* @param: tpm_size, The length of the data
* @return: should be zero if success else a negative error code.
*/
static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data,
int tpm_size)
{
int total_length = 0, ret = 0;
struct st33zp24_spi_phy *phy = phy_id;
struct spi_device *dev = phy->spi_device;
struct spi_transfer spi_xfer = {
.tx_buf = phy->tx_buf,
.rx_buf = phy->rx_buf,
};
/* Pre-Header */
phy->tx_buf[total_length++] = TPM_WRITE_DIRECTION | LOCALITY0;
phy->tx_buf[total_length++] = tpm_register;
if (tpm_size > 0 && tpm_register == TPM_DATA_FIFO) {
phy->tx_buf[total_length++] = tpm_size >> 8;
phy->tx_buf[total_length++] = tpm_size;
}
memcpy(&phy->tx_buf[total_length], tpm_data, tpm_size);
total_length += tpm_size;
memset(&phy->tx_buf[total_length], TPM_DUMMY_BYTE, phy->latency);
spi_xfer.len = total_length + phy->latency;
ret = spi_sync_transfer(dev, &spi_xfer, 1);
if (ret == 0)
ret = phy->rx_buf[total_length + phy->latency - 1];
return st33zp24_status_to_errno(ret);
} /* st33zp24_spi_send() */
/*
* st33zp24_spi_read8_recv
* Recv byte from the TIS register according to the ST33ZP24 SPI protocol.
* @param: phy_id, the phy description
* @param: tpm_register, the tpm tis register where the data should be read
* @param: tpm_data, the TPM response
* @param: tpm_size, tpm TPM response size to read.
* @return: should be zero if success else a negative error code.
*/
static int st33zp24_spi_read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data,
int tpm_size)
{
int total_length = 0, ret;
struct st33zp24_spi_phy *phy = phy_id;
struct spi_device *dev = phy->spi_device;
struct spi_transfer spi_xfer = {
.tx_buf = phy->tx_buf,
.rx_buf = phy->rx_buf,
};
/* Pre-Header */
phy->tx_buf[total_length++] = LOCALITY0;
phy->tx_buf[total_length++] = tpm_register;
memset(&phy->tx_buf[total_length], TPM_DUMMY_BYTE,
phy->latency + tpm_size);
spi_xfer.len = total_length + phy->latency + tpm_size;
/* header + status byte + size of the data + status byte */
ret = spi_sync_transfer(dev, &spi_xfer, 1);
if (tpm_size > 0 && ret == 0) {
ret = phy->rx_buf[total_length + phy->latency - 1];
memcpy(tpm_data, phy->rx_buf + total_length + phy->latency,
tpm_size);
}
return ret;
} /* st33zp24_spi_read8_reg() */
/*
* st33zp24_spi_recv
* Recv byte from the TIS register according to the ST33ZP24 SPI protocol.
* @param: phy_id, the phy description
* @param: tpm_register, the tpm tis register where the data should be read
* @param: tpm_data, the TPM response
* @param: tpm_size, tpm TPM response size to read.
* @return: number of byte read successfully: should be one if success.
*/
static int st33zp24_spi_recv(void *phy_id, u8 tpm_register, u8 *tpm_data,
int tpm_size)
{
int ret;
ret = st33zp24_spi_read8_reg(phy_id, tpm_register, tpm_data, tpm_size);
if (!st33zp24_status_to_errno(ret))
return tpm_size;
return ret;
} /* st33zp24_spi_recv() */
static int st33zp24_spi_evaluate_latency(void *phy_id)
{
struct st33zp24_spi_phy *phy = phy_id;
int latency = 1, status = 0;
u8 data = 0;
while (!status && latency < MAX_SPI_LATENCY) {
phy->latency = latency;
status = st33zp24_spi_read8_reg(phy_id, TPM_INTF_CAPABILITY,
&data, 1);
latency++;
}
if (status < 0)
return status;
if (latency == MAX_SPI_LATENCY)
return -ENODEV;
return latency - 1;
} /* evaluate_latency() */
static const struct st33zp24_phy_ops spi_phy_ops = {
.send = st33zp24_spi_send,
.recv = st33zp24_spi_recv,
};
/*
* st33zp24_spi_probe initialize the TPM device
* @param: dev, the spi_device description (TPM SPI description).
* @return: 0 in case of success.
* or a negative value describing the error.
*/
static int st33zp24_spi_probe(struct spi_device *dev)
{
struct st33zp24_spi_phy *phy;
phy = devm_kzalloc(&dev->dev, sizeof(struct st33zp24_spi_phy),
GFP_KERNEL);
if (!phy)
return -ENOMEM;
phy->spi_device = dev;
phy->latency = st33zp24_spi_evaluate_latency(phy);
if (phy->latency <= 0)
return -ENODEV;
return st33zp24_probe(phy, &spi_phy_ops, &dev->dev, dev->irq);
}
/*
* st33zp24_spi_remove remove the TPM device
* @param: client, the spi_device description (TPM SPI description).
* @return: 0 in case of success.
*/
static void st33zp24_spi_remove(struct spi_device *dev)
{
struct tpm_chip *chip = spi_get_drvdata(dev);
st33zp24_remove(chip);
}
static const struct spi_device_id st33zp24_spi_id[] = {
{TPM_ST33_SPI, 0},
{}
};
MODULE_DEVICE_TABLE(spi, st33zp24_spi_id);
static const struct of_device_id of_st33zp24_spi_match[] __maybe_unused = {
{ .compatible = "st,st33zp24-spi", },
{}
};
MODULE_DEVICE_TABLE(of, of_st33zp24_spi_match);
static const struct acpi_device_id st33zp24_spi_acpi_match[] __maybe_unused = {
{"SMO3324"},
{}
};
MODULE_DEVICE_TABLE(acpi, st33zp24_spi_acpi_match);
static SIMPLE_DEV_PM_OPS(st33zp24_spi_ops, st33zp24_pm_suspend,
st33zp24_pm_resume);
static struct spi_driver st33zp24_spi_driver = {
.driver = {
.name = "st33zp24-spi",
.pm = &st33zp24_spi_ops,
.of_match_table = of_match_ptr(of_st33zp24_spi_match),
.acpi_match_table = ACPI_PTR(st33zp24_spi_acpi_match),
},
.probe = st33zp24_spi_probe,
.remove = st33zp24_spi_remove,
.id_table = st33zp24_spi_id,
};
module_spi_driver(st33zp24_spi_driver);
MODULE_AUTHOR("TPM support <TPMsupport@list.st.com>");
MODULE_DESCRIPTION("STM TPM 1.2 SPI ST33 Driver");
MODULE_VERSION("1.3.0");
MODULE_LICENSE("GPL");
@@ -0,0 +1,590 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* STMicroelectronics TPM Linux driver for TPM ST33ZP24
* Copyright (C) 2009 - 2016 STMicroelectronics
*/
#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/freezer.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/gpio/consumer.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/slab.h>
#include "../tpm.h"
#include "st33zp24.h"
#define TPM_ACCESS 0x0
#define TPM_STS 0x18
#define TPM_DATA_FIFO 0x24
#define TPM_INTF_CAPABILITY 0x14
#define TPM_INT_STATUS 0x10
#define TPM_INT_ENABLE 0x08
#define LOCALITY0 0
enum st33zp24_access {
TPM_ACCESS_VALID = 0x80,
TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
TPM_ACCESS_REQUEST_PENDING = 0x04,
TPM_ACCESS_REQUEST_USE = 0x02,
};
enum st33zp24_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 st33zp24_int_flags {
TPM_GLOBAL_INT_ENABLE = 0x80,
TPM_INTF_CMD_READY_INT = 0x080,
TPM_INTF_FIFO_AVALAIBLE_INT = 0x040,
TPM_INTF_WAKE_UP_READY_INT = 0x020,
TPM_INTF_LOCALITY_CHANGE_INT = 0x004,
TPM_INTF_STS_VALID_INT = 0x002,
TPM_INTF_DATA_AVAIL_INT = 0x001,
};
enum tis_defaults {
TIS_SHORT_TIMEOUT = 750,
TIS_LONG_TIMEOUT = 2000,
};
/*
* clear the pending interrupt.
*/
static u8 clear_interruption(struct st33zp24_dev *tpm_dev)
{
u8 interrupt;
tpm_dev->ops->recv(tpm_dev->phy_id, TPM_INT_STATUS, &interrupt, 1);
tpm_dev->ops->send(tpm_dev->phy_id, TPM_INT_STATUS, &interrupt, 1);
return interrupt;
}
/*
* cancel the current command execution or set STS to COMMAND READY.
*/
static void st33zp24_cancel(struct tpm_chip *chip)
{
struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
u8 data;
data = TPM_STS_COMMAND_READY;
tpm_dev->ops->send(tpm_dev->phy_id, TPM_STS, &data, 1);
}
/*
* return the TPM_STS register
*/
static u8 st33zp24_status(struct tpm_chip *chip)
{
struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
u8 data;
tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS, &data, 1);
return data;
}
/*
* if the locality is active
*/
static bool check_locality(struct tpm_chip *chip)
{
struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
u8 data;
u8 status;
status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_ACCESS, &data, 1);
if (status && (data &
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
return true;
return false;
}
static int request_locality(struct tpm_chip *chip)
{
struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
unsigned long stop;
long ret;
u8 data;
if (check_locality(chip))
return tpm_dev->locality;
data = TPM_ACCESS_REQUEST_USE;
ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_ACCESS, &data, 1);
if (ret < 0)
return ret;
stop = jiffies + chip->timeout_a;
/* Request locality is usually effective after the request */
do {
if (check_locality(chip))
return tpm_dev->locality;
msleep(TPM_TIMEOUT);
} while (time_before(jiffies, stop));
/* could not get locality */
return -EACCES;
}
static void release_locality(struct tpm_chip *chip)
{
struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
u8 data;
data = TPM_ACCESS_ACTIVE_LOCALITY;
tpm_dev->ops->send(tpm_dev->phy_id, TPM_ACCESS, &data, 1);
}
/*
* get_burstcount return the burstcount value
*/
static int get_burstcount(struct tpm_chip *chip)
{
struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
unsigned long stop;
int burstcnt, status;
u8 temp;
stop = jiffies + chip->timeout_d;
do {
status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS + 1,
&temp, 1);
if (status < 0)
return -EBUSY;
burstcnt = temp;
status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS + 2,
&temp, 1);
if (status < 0)
return -EBUSY;
burstcnt |= temp << 8;
if (burstcnt)
return burstcnt;
msleep(TPM_TIMEOUT);
} while (time_before(jiffies, stop));
return -EBUSY;
}
static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask,
bool check_cancel, bool *canceled)
{
u8 status = chip->ops->status(chip);
*canceled = false;
if ((status & mask) == mask)
return true;
if (check_cancel && chip->ops->req_canceled(chip, status)) {
*canceled = true;
return true;
}
return false;
}
/*
* wait for a TPM_STS value
*/
static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
wait_queue_head_t *queue, bool check_cancel)
{
struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
unsigned long stop;
int ret = 0;
bool canceled = false;
bool condition;
u32 cur_intrs;
u8 status;
/* check current status */
status = st33zp24_status(chip);
if ((status & mask) == mask)
return 0;
stop = jiffies + timeout;
if (chip->flags & TPM_CHIP_FLAG_IRQ) {
cur_intrs = tpm_dev->intrs;
clear_interruption(tpm_dev);
enable_irq(tpm_dev->irq);
do {
if (ret == -ERESTARTSYS && freezing(current))
clear_thread_flag(TIF_SIGPENDING);
timeout = stop - jiffies;
if ((long) timeout <= 0)
return -1;
ret = wait_event_interruptible_timeout(*queue,
cur_intrs != tpm_dev->intrs,
timeout);
clear_interruption(tpm_dev);
condition = wait_for_tpm_stat_cond(chip, mask,
check_cancel, &canceled);
if (ret >= 0 && condition) {
if (canceled)
return -ECANCELED;
return 0;
}
} while (ret == -ERESTARTSYS && freezing(current));
disable_irq_nosync(tpm_dev->irq);
} else {
do {
msleep(TPM_TIMEOUT);
status = chip->ops->status(chip);
if ((status & mask) == mask)
return 0;
} while (time_before(jiffies, stop));
}
return -ETIME;
}
static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
{
struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
int size = 0, burstcnt, len, ret;
while (size < count &&
wait_for_stat(chip,
TPM_STS_DATA_AVAIL | TPM_STS_VALID,
chip->timeout_c,
&tpm_dev->read_queue, true) == 0) {
burstcnt = get_burstcount(chip);
if (burstcnt < 0)
return burstcnt;
len = min_t(int, burstcnt, count - size);
ret = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_DATA_FIFO,
buf + size, len);
if (ret < 0)
return ret;
size += len;
}
return size;
}
static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id)
{
struct tpm_chip *chip = dev_id;
struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
tpm_dev->intrs++;
wake_up_interruptible(&tpm_dev->read_queue);
disable_irq_nosync(tpm_dev->irq);
return IRQ_HANDLED;
}
/*
* send TPM commands through the I2C bus.
*/
static int st33zp24_send(struct tpm_chip *chip, unsigned char *buf,
size_t bufsiz, size_t len)
{
struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
u32 status, i, size, ordinal;
int burstcnt = 0;
int ret;
u8 data;
if (len < TPM_HEADER_SIZE)
return -EBUSY;
ret = request_locality(chip);
if (ret < 0)
return ret;
status = st33zp24_status(chip);
if ((status & TPM_STS_COMMAND_READY) == 0) {
st33zp24_cancel(chip);
if (wait_for_stat
(chip, TPM_STS_COMMAND_READY, chip->timeout_b,
&tpm_dev->read_queue, false) < 0) {
ret = -ETIME;
goto out_err;
}
}
for (i = 0; i < len - 1;) {
burstcnt = get_burstcount(chip);
if (burstcnt < 0) {
ret = burstcnt;
goto out_err;
}
size = min_t(int, len - i - 1, burstcnt);
ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_DATA_FIFO,
buf + i, size);
if (ret < 0)
goto out_err;
i += size;
}
status = st33zp24_status(chip);
if ((status & TPM_STS_DATA_EXPECT) == 0) {
ret = -EIO;
goto out_err;
}
ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_DATA_FIFO,
buf + len - 1, 1);
if (ret < 0)
goto out_err;
status = st33zp24_status(chip);
if ((status & TPM_STS_DATA_EXPECT) != 0) {
ret = -EIO;
goto out_err;
}
data = TPM_STS_GO;
ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_STS, &data, 1);
if (ret < 0)
goto out_err;
if (chip->flags & TPM_CHIP_FLAG_IRQ) {
ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
ret = wait_for_stat(chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
tpm_calc_ordinal_duration(chip, ordinal),
&tpm_dev->read_queue, false);
if (ret < 0)
goto out_err;
}
return 0;
out_err:
st33zp24_cancel(chip);
release_locality(chip);
return ret;
}
static int st33zp24_recv(struct tpm_chip *chip, unsigned char *buf,
size_t count)
{
int size = 0;
u32 expected;
if (!chip)
return -EBUSY;
if (count < TPM_HEADER_SIZE) {
size = -EIO;
goto out;
}
size = recv_data(chip, buf, TPM_HEADER_SIZE);
if (size < TPM_HEADER_SIZE) {
dev_err(&chip->dev, "Unable to read header\n");
goto out;
}
expected = be32_to_cpu(*(__be32 *)(buf + 2));
if (expected > count || expected < TPM_HEADER_SIZE) {
size = -EIO;
goto out;
}
size += recv_data(chip, &buf[TPM_HEADER_SIZE],
expected - TPM_HEADER_SIZE);
if (size < expected) {
dev_err(&chip->dev, "Unable to read remainder of result\n");
size = -ETIME;
}
out:
st33zp24_cancel(chip);
release_locality(chip);
return size;
}
static bool st33zp24_req_canceled(struct tpm_chip *chip, u8 status)
{
return (status == TPM_STS_COMMAND_READY);
}
static const struct tpm_class_ops st33zp24_tpm = {
.flags = TPM_OPS_AUTO_STARTUP,
.send = st33zp24_send,
.recv = st33zp24_recv,
.cancel = st33zp24_cancel,
.status = st33zp24_status,
.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
.req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
.req_canceled = st33zp24_req_canceled,
};
static const struct acpi_gpio_params lpcpd_gpios = { 1, 0, false };
static const struct acpi_gpio_mapping acpi_st33zp24_gpios[] = {
{ "lpcpd-gpios", &lpcpd_gpios, 1 },
{ },
};
/*
* initialize the TPM device
*/
int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops,
struct device *dev, int irq)
{
int ret;
u8 intmask = 0;
struct tpm_chip *chip;
struct st33zp24_dev *tpm_dev;
chip = tpmm_chip_alloc(dev, &st33zp24_tpm);
if (IS_ERR(chip))
return PTR_ERR(chip);
tpm_dev = devm_kzalloc(dev, sizeof(struct st33zp24_dev),
GFP_KERNEL);
if (!tpm_dev)
return -ENOMEM;
tpm_dev->phy_id = phy_id;
tpm_dev->ops = ops;
dev_set_drvdata(&chip->dev, tpm_dev);
chip->timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
chip->timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
chip->timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
chip->timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
tpm_dev->locality = LOCALITY0;
if (ACPI_COMPANION(dev)) {
ret = devm_acpi_dev_add_driver_gpios(dev, acpi_st33zp24_gpios);
if (ret)
return ret;
}
/*
* Get LPCPD GPIO. If lpcpd pin is not specified. This is not an
* issue as power management can be also managed by TPM specific
* commands.
*/
tpm_dev->io_lpcpd = devm_gpiod_get_optional(dev, "lpcpd",
GPIOD_OUT_HIGH);
ret = PTR_ERR_OR_ZERO(tpm_dev->io_lpcpd);
if (ret) {
dev_err(dev, "failed to request lpcpd gpio: %d\n", ret);
return ret;
}
if (irq) {
/* INTERRUPT Setup */
init_waitqueue_head(&tpm_dev->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(dev, irq, tpm_ioserirq_handler,
IRQF_TRIGGER_HIGH, "TPM SERIRQ management",
chip);
if (ret < 0) {
dev_err(&chip->dev, "TPM SERIRQ signals %d not available\n",
irq);
goto _tpm_clean_answer;
}
intmask |= TPM_INTF_CMD_READY_INT
| TPM_INTF_STS_VALID_INT
| TPM_INTF_DATA_AVAIL_INT;
ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_INT_ENABLE,
&intmask, 1);
if (ret < 0)
goto _tpm_clean_answer;
intmask = TPM_GLOBAL_INT_ENABLE;
ret = tpm_dev->ops->send(tpm_dev->phy_id, (TPM_INT_ENABLE + 3),
&intmask, 1);
if (ret < 0)
goto _tpm_clean_answer;
tpm_dev->irq = irq;
chip->flags |= TPM_CHIP_FLAG_IRQ;
disable_irq_nosync(tpm_dev->irq);
}
return tpm_chip_register(chip);
_tpm_clean_answer:
dev_info(&chip->dev, "TPM initialization fail\n");
return ret;
}
EXPORT_SYMBOL(st33zp24_probe);
void st33zp24_remove(struct tpm_chip *chip)
{
tpm_chip_unregister(chip);
}
EXPORT_SYMBOL(st33zp24_remove);
#ifdef CONFIG_PM_SLEEP
int st33zp24_pm_suspend(struct device *dev)
{
struct tpm_chip *chip = dev_get_drvdata(dev);
struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
int ret = 0;
if (tpm_dev->io_lpcpd)
gpiod_set_value_cansleep(tpm_dev->io_lpcpd, 0);
else
ret = tpm_pm_suspend(dev);
return ret;
}
EXPORT_SYMBOL(st33zp24_pm_suspend);
int st33zp24_pm_resume(struct device *dev)
{
struct tpm_chip *chip = dev_get_drvdata(dev);
struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
int ret = 0;
if (tpm_dev->io_lpcpd) {
gpiod_set_value_cansleep(tpm_dev->io_lpcpd, 1);
ret = wait_for_stat(chip,
TPM_STS_VALID, chip->timeout_b,
&tpm_dev->read_queue, false);
} else {
ret = tpm_pm_resume(dev);
if (!ret)
tpm1_do_selftest(chip);
}
return ret;
}
EXPORT_SYMBOL(st33zp24_pm_resume);
#endif
MODULE_AUTHOR("TPM support <TPMsupport@list.st.com>");
MODULE_DESCRIPTION("ST33ZP24 TPM 1.2 driver");
MODULE_VERSION("1.3.0");
MODULE_LICENSE("GPL");
@@ -0,0 +1,41 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* STMicroelectronics TPM Linux driver for TPM ST33ZP24
* Copyright (C) 2009 - 2016 STMicroelectronics
*/
#ifndef __LOCAL_ST33ZP24_H__
#define __LOCAL_ST33ZP24_H__
#define TPM_ST33_I2C "st33zp24-i2c"
#define TPM_ST33_SPI "st33zp24-spi"
#define TPM_WRITE_DIRECTION 0x80
#define ST33ZP24_BUFSIZE 2048
struct st33zp24_dev {
struct tpm_chip *chip;
void *phy_id;
const struct st33zp24_phy_ops *ops;
int locality;
int irq;
u32 intrs;
struct gpio_desc *io_lpcpd;
wait_queue_head_t read_queue;
};
struct st33zp24_phy_ops {
int (*send)(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size);
int (*recv)(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size);
};
#ifdef CONFIG_PM_SLEEP
int st33zp24_pm_suspend(struct device *dev);
int st33zp24_pm_resume(struct device *dev);
#endif
int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops,
struct device *dev, int irq);
void st33zp24_remove(struct tpm_chip *chip);
#endif /* __LOCAL_ST33ZP24_H__ */