18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ATMEL I2C TPM AT97SC3204T
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2012 V Lab Technologies
68c2ecf20Sopenharmony_ci *  Teddy Reed <teddy@prosauce.org>
78c2ecf20Sopenharmony_ci * Copyright (C) 2013, Obsidian Research Corp.
88c2ecf20Sopenharmony_ci *  Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
98c2ecf20Sopenharmony_ci * Device driver for ATMEL I2C TPMs.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Teddy Reed determined the basic I2C command flow, unlike other I2C TPM
128c2ecf20Sopenharmony_ci * devices the raw TCG formatted TPM command data is written via I2C and then
138c2ecf20Sopenharmony_ci * raw TCG formatted TPM command data is returned via I2C.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * TGC status/locality/etc functions seen in the LPC implementation do not
168c2ecf20Sopenharmony_ci * seem to be present.
178c2ecf20Sopenharmony_ci */
188c2ecf20Sopenharmony_ci#include <linux/init.h>
198c2ecf20Sopenharmony_ci#include <linux/module.h>
208c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci#include <linux/i2c.h>
238c2ecf20Sopenharmony_ci#include "tpm.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define I2C_DRIVER_NAME "tpm_i2c_atmel"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define TPM_I2C_SHORT_TIMEOUT  750     /* ms */
288c2ecf20Sopenharmony_ci#define TPM_I2C_LONG_TIMEOUT   2000    /* 2 sec */
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define ATMEL_STS_OK 1
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct priv_data {
338c2ecf20Sopenharmony_ci	size_t len;
348c2ecf20Sopenharmony_ci	/* This is the amount we read on the first try. 25 was chosen to fit a
358c2ecf20Sopenharmony_ci	 * fair number of read responses in the buffer so a 2nd retry can be
368c2ecf20Sopenharmony_ci	 * avoided in small message cases. */
378c2ecf20Sopenharmony_ci	u8 buffer[sizeof(struct tpm_header) + 25];
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	struct priv_data *priv = dev_get_drvdata(&chip->dev);
438c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(chip->dev.parent);
448c2ecf20Sopenharmony_ci	s32 status;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	priv->len = 0;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	if (len <= 2)
498c2ecf20Sopenharmony_ci		return -EIO;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	status = i2c_master_send(client, buf, len);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	dev_dbg(&chip->dev,
548c2ecf20Sopenharmony_ci		"%s(buf=%*ph len=%0zx) -> sts=%d\n", __func__,
558c2ecf20Sopenharmony_ci		(int)min_t(size_t, 64, len), buf, len, status);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	if (status < 0)
588c2ecf20Sopenharmony_ci		return status;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	/* The upper layer does not support incomplete sends. */
618c2ecf20Sopenharmony_ci	if (status != len)
628c2ecf20Sopenharmony_ci		return -E2BIG;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	return 0;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	struct priv_data *priv = dev_get_drvdata(&chip->dev);
708c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(chip->dev.parent);
718c2ecf20Sopenharmony_ci	struct tpm_header *hdr = (struct tpm_header *)priv->buffer;
728c2ecf20Sopenharmony_ci	u32 expected_len;
738c2ecf20Sopenharmony_ci	int rc;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (priv->len == 0)
768c2ecf20Sopenharmony_ci		return -EIO;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	/* Get the message size from the message header, if we didn't get the
798c2ecf20Sopenharmony_ci	 * whole message in read_status then we need to re-read the
808c2ecf20Sopenharmony_ci	 * message. */
818c2ecf20Sopenharmony_ci	expected_len = be32_to_cpu(hdr->length);
828c2ecf20Sopenharmony_ci	if (expected_len > count)
838c2ecf20Sopenharmony_ci		return -ENOMEM;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	if (priv->len >= expected_len) {
868c2ecf20Sopenharmony_ci		dev_dbg(&chip->dev,
878c2ecf20Sopenharmony_ci			"%s early(buf=%*ph count=%0zx) -> ret=%d\n", __func__,
888c2ecf20Sopenharmony_ci			(int)min_t(size_t, 64, expected_len), buf, count,
898c2ecf20Sopenharmony_ci			expected_len);
908c2ecf20Sopenharmony_ci		memcpy(buf, priv->buffer, expected_len);
918c2ecf20Sopenharmony_ci		return expected_len;
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	rc = i2c_master_recv(client, buf, expected_len);
958c2ecf20Sopenharmony_ci	dev_dbg(&chip->dev,
968c2ecf20Sopenharmony_ci		"%s reread(buf=%*ph count=%0zx) -> ret=%d\n", __func__,
978c2ecf20Sopenharmony_ci		(int)min_t(size_t, 64, expected_len), buf, count,
988c2ecf20Sopenharmony_ci		expected_len);
998c2ecf20Sopenharmony_ci	return rc;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic void i2c_atmel_cancel(struct tpm_chip *chip)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	dev_err(&chip->dev, "TPM operation cancellation was requested, but is not supported");
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic u8 i2c_atmel_read_status(struct tpm_chip *chip)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	struct priv_data *priv = dev_get_drvdata(&chip->dev);
1108c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(chip->dev.parent);
1118c2ecf20Sopenharmony_ci	int rc;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	/* The TPM fails the I2C read until it is ready, so we do the entire
1148c2ecf20Sopenharmony_ci	 * transfer here and buffer it locally. This way the common code can
1158c2ecf20Sopenharmony_ci	 * properly handle the timeouts. */
1168c2ecf20Sopenharmony_ci	priv->len = 0;
1178c2ecf20Sopenharmony_ci	memset(priv->buffer, 0, sizeof(priv->buffer));
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	/* Once the TPM has completed the command the command remains readable
1218c2ecf20Sopenharmony_ci	 * until another command is issued. */
1228c2ecf20Sopenharmony_ci	rc = i2c_master_recv(client, priv->buffer, sizeof(priv->buffer));
1238c2ecf20Sopenharmony_ci	dev_dbg(&chip->dev,
1248c2ecf20Sopenharmony_ci		"%s: sts=%d", __func__, rc);
1258c2ecf20Sopenharmony_ci	if (rc <= 0)
1268c2ecf20Sopenharmony_ci		return 0;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	priv->len = rc;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	return ATMEL_STS_OK;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic bool i2c_atmel_req_canceled(struct tpm_chip *chip, u8 status)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	return false;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic const struct tpm_class_ops i2c_atmel = {
1398c2ecf20Sopenharmony_ci	.flags = TPM_OPS_AUTO_STARTUP,
1408c2ecf20Sopenharmony_ci	.status = i2c_atmel_read_status,
1418c2ecf20Sopenharmony_ci	.recv = i2c_atmel_recv,
1428c2ecf20Sopenharmony_ci	.send = i2c_atmel_send,
1438c2ecf20Sopenharmony_ci	.cancel = i2c_atmel_cancel,
1448c2ecf20Sopenharmony_ci	.req_complete_mask = ATMEL_STS_OK,
1458c2ecf20Sopenharmony_ci	.req_complete_val = ATMEL_STS_OK,
1468c2ecf20Sopenharmony_ci	.req_canceled = i2c_atmel_req_canceled,
1478c2ecf20Sopenharmony_ci};
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic int i2c_atmel_probe(struct i2c_client *client,
1508c2ecf20Sopenharmony_ci			   const struct i2c_device_id *id)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	struct tpm_chip *chip;
1538c2ecf20Sopenharmony_ci	struct device *dev = &client->dev;
1548c2ecf20Sopenharmony_ci	struct priv_data *priv;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
1578c2ecf20Sopenharmony_ci		return -ENODEV;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	chip = tpmm_chip_alloc(dev, &i2c_atmel);
1608c2ecf20Sopenharmony_ci	if (IS_ERR(chip))
1618c2ecf20Sopenharmony_ci		return PTR_ERR(chip);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(struct priv_data), GFP_KERNEL);
1648c2ecf20Sopenharmony_ci	if (!priv)
1658c2ecf20Sopenharmony_ci		return -ENOMEM;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	/* Default timeouts */
1688c2ecf20Sopenharmony_ci	chip->timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
1698c2ecf20Sopenharmony_ci	chip->timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT);
1708c2ecf20Sopenharmony_ci	chip->timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
1718c2ecf20Sopenharmony_ci	chip->timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	dev_set_drvdata(&chip->dev, priv);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	/* There is no known way to probe for this device, and all version
1768c2ecf20Sopenharmony_ci	 * information seems to be read via TPM commands. Thus we rely on the
1778c2ecf20Sopenharmony_ci	 * TPM startup process in the common code to detect the device. */
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	return tpm_chip_register(chip);
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic int i2c_atmel_remove(struct i2c_client *client)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	struct device *dev = &(client->dev);
1858c2ecf20Sopenharmony_ci	struct tpm_chip *chip = dev_get_drvdata(dev);
1868c2ecf20Sopenharmony_ci	tpm_chip_unregister(chip);
1878c2ecf20Sopenharmony_ci	return 0;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic const struct i2c_device_id i2c_atmel_id[] = {
1918c2ecf20Sopenharmony_ci	{I2C_DRIVER_NAME, 0},
1928c2ecf20Sopenharmony_ci	{}
1938c2ecf20Sopenharmony_ci};
1948c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, i2c_atmel_id);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
1978c2ecf20Sopenharmony_cistatic const struct of_device_id i2c_atmel_of_match[] = {
1988c2ecf20Sopenharmony_ci	{.compatible = "atmel,at97sc3204t"},
1998c2ecf20Sopenharmony_ci	{},
2008c2ecf20Sopenharmony_ci};
2018c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, i2c_atmel_of_match);
2028c2ecf20Sopenharmony_ci#endif
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(i2c_atmel_pm_ops, tpm_pm_suspend, tpm_pm_resume);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic struct i2c_driver i2c_atmel_driver = {
2078c2ecf20Sopenharmony_ci	.id_table = i2c_atmel_id,
2088c2ecf20Sopenharmony_ci	.probe = i2c_atmel_probe,
2098c2ecf20Sopenharmony_ci	.remove = i2c_atmel_remove,
2108c2ecf20Sopenharmony_ci	.driver = {
2118c2ecf20Sopenharmony_ci		.name = I2C_DRIVER_NAME,
2128c2ecf20Sopenharmony_ci		.pm = &i2c_atmel_pm_ops,
2138c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(i2c_atmel_of_match),
2148c2ecf20Sopenharmony_ci	},
2158c2ecf20Sopenharmony_ci};
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cimodule_i2c_driver(i2c_atmel_driver);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jason Gunthorpe <jgunthorpe@obsidianresearch.com>");
2208c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Atmel TPM I2C Driver");
2218c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
222