18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2004 IBM Corporation
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Authors:
68c2ecf20Sopenharmony_ci * Leendert van Doorn <leendert@watson.ibm.com>
78c2ecf20Sopenharmony_ci * Dave Safford <safford@watson.ibm.com>
88c2ecf20Sopenharmony_ci * Reiner Sailer <sailer@watson.ibm.com>
98c2ecf20Sopenharmony_ci * Kylene Hall <kjhall@us.ibm.com>
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Maintained by: <tpmdd-devel@lists.sourceforge.net>
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * Device driver for TCG/TCPA TPM (trusted platform module).
148c2ecf20Sopenharmony_ci * Specifications at www.trustedcomputinggroup.org
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "tpm.h"
188c2ecf20Sopenharmony_ci#include "tpm_atmel.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* write status bits */
218c2ecf20Sopenharmony_cienum tpm_atmel_write_status {
228c2ecf20Sopenharmony_ci	ATML_STATUS_ABORT = 0x01,
238c2ecf20Sopenharmony_ci	ATML_STATUS_LASTBYTE = 0x04
248c2ecf20Sopenharmony_ci};
258c2ecf20Sopenharmony_ci/* read status bits */
268c2ecf20Sopenharmony_cienum tpm_atmel_read_status {
278c2ecf20Sopenharmony_ci	ATML_STATUS_BUSY = 0x01,
288c2ecf20Sopenharmony_ci	ATML_STATUS_DATA_AVAIL = 0x02,
298c2ecf20Sopenharmony_ci	ATML_STATUS_REWRITE = 0x04,
308c2ecf20Sopenharmony_ci	ATML_STATUS_READY = 0x08
318c2ecf20Sopenharmony_ci};
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
368c2ecf20Sopenharmony_ci	u8 status, *hdr = buf;
378c2ecf20Sopenharmony_ci	u32 size;
388c2ecf20Sopenharmony_ci	int i;
398c2ecf20Sopenharmony_ci	__be32 *native_size;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	/* start reading header */
428c2ecf20Sopenharmony_ci	if (count < 6)
438c2ecf20Sopenharmony_ci		return -EIO;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	for (i = 0; i < 6; i++) {
468c2ecf20Sopenharmony_ci		status = ioread8(priv->iobase + 1);
478c2ecf20Sopenharmony_ci		if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
488c2ecf20Sopenharmony_ci			dev_err(&chip->dev, "error reading header\n");
498c2ecf20Sopenharmony_ci			return -EIO;
508c2ecf20Sopenharmony_ci		}
518c2ecf20Sopenharmony_ci		*buf++ = ioread8(priv->iobase);
528c2ecf20Sopenharmony_ci	}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	/* size of the data received */
558c2ecf20Sopenharmony_ci	native_size = (__force __be32 *) (hdr + 2);
568c2ecf20Sopenharmony_ci	size = be32_to_cpu(*native_size);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	if (count < size) {
598c2ecf20Sopenharmony_ci		dev_err(&chip->dev,
608c2ecf20Sopenharmony_ci			"Recv size(%d) less than available space\n", size);
618c2ecf20Sopenharmony_ci		for (; i < size; i++) {	/* clear the waiting data anyway */
628c2ecf20Sopenharmony_ci			status = ioread8(priv->iobase + 1);
638c2ecf20Sopenharmony_ci			if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
648c2ecf20Sopenharmony_ci				dev_err(&chip->dev, "error reading data\n");
658c2ecf20Sopenharmony_ci				return -EIO;
668c2ecf20Sopenharmony_ci			}
678c2ecf20Sopenharmony_ci		}
688c2ecf20Sopenharmony_ci		return -EIO;
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	/* read all the data available */
728c2ecf20Sopenharmony_ci	for (; i < size; i++) {
738c2ecf20Sopenharmony_ci		status = ioread8(priv->iobase + 1);
748c2ecf20Sopenharmony_ci		if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
758c2ecf20Sopenharmony_ci			dev_err(&chip->dev, "error reading data\n");
768c2ecf20Sopenharmony_ci			return -EIO;
778c2ecf20Sopenharmony_ci		}
788c2ecf20Sopenharmony_ci		*buf++ = ioread8(priv->iobase);
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	/* make sure data available is gone */
828c2ecf20Sopenharmony_ci	status = ioread8(priv->iobase + 1);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	if (status & ATML_STATUS_DATA_AVAIL) {
858c2ecf20Sopenharmony_ci		dev_err(&chip->dev, "data available is stuck\n");
868c2ecf20Sopenharmony_ci		return -EIO;
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	return size;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
958c2ecf20Sopenharmony_ci	int i;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	dev_dbg(&chip->dev, "tpm_atml_send:\n");
988c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
998c2ecf20Sopenharmony_ci		dev_dbg(&chip->dev, "%d 0x%x(%d)\n",  i, buf[i], buf[i]);
1008c2ecf20Sopenharmony_ci		iowrite8(buf[i], priv->iobase);
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	return 0;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic void tpm_atml_cancel(struct tpm_chip *chip)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	iowrite8(ATML_STATUS_ABORT, priv->iobase + 1);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic u8 tpm_atml_status(struct tpm_chip *chip)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	return ioread8(priv->iobase + 1);
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic bool tpm_atml_req_canceled(struct tpm_chip *chip, u8 status)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	return (status == ATML_STATUS_READY);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic const struct tpm_class_ops tpm_atmel = {
1268c2ecf20Sopenharmony_ci	.recv = tpm_atml_recv,
1278c2ecf20Sopenharmony_ci	.send = tpm_atml_send,
1288c2ecf20Sopenharmony_ci	.cancel = tpm_atml_cancel,
1298c2ecf20Sopenharmony_ci	.status = tpm_atml_status,
1308c2ecf20Sopenharmony_ci	.req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL,
1318c2ecf20Sopenharmony_ci	.req_complete_val = ATML_STATUS_DATA_AVAIL,
1328c2ecf20Sopenharmony_ci	.req_canceled = tpm_atml_req_canceled,
1338c2ecf20Sopenharmony_ci};
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic struct platform_device *pdev;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic void atml_plat_remove(void)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	struct tpm_chip *chip = dev_get_drvdata(&pdev->dev);
1408c2ecf20Sopenharmony_ci	struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	tpm_chip_unregister(chip);
1438c2ecf20Sopenharmony_ci	if (priv->have_region)
1448c2ecf20Sopenharmony_ci		atmel_release_region(priv->base, priv->region_size);
1458c2ecf20Sopenharmony_ci	atmel_put_base_addr(priv->iobase);
1468c2ecf20Sopenharmony_ci	platform_device_unregister(pdev);
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(tpm_atml_pm, tpm_pm_suspend, tpm_pm_resume);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic struct platform_driver atml_drv = {
1528c2ecf20Sopenharmony_ci	.driver = {
1538c2ecf20Sopenharmony_ci		.name = "tpm_atmel",
1548c2ecf20Sopenharmony_ci		.pm		= &tpm_atml_pm,
1558c2ecf20Sopenharmony_ci	},
1568c2ecf20Sopenharmony_ci};
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic int __init init_atmel(void)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	int rc = 0;
1618c2ecf20Sopenharmony_ci	void __iomem *iobase = NULL;
1628c2ecf20Sopenharmony_ci	int have_region, region_size;
1638c2ecf20Sopenharmony_ci	unsigned long base;
1648c2ecf20Sopenharmony_ci	struct  tpm_chip *chip;
1658c2ecf20Sopenharmony_ci	struct tpm_atmel_priv *priv;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	rc = platform_driver_register(&atml_drv);
1688c2ecf20Sopenharmony_ci	if (rc)
1698c2ecf20Sopenharmony_ci		return rc;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if ((iobase = atmel_get_base_addr(&base, &region_size)) == NULL) {
1728c2ecf20Sopenharmony_ci		rc = -ENODEV;
1738c2ecf20Sopenharmony_ci		goto err_unreg_drv;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	have_region =
1778c2ecf20Sopenharmony_ci	    (atmel_request_region
1788c2ecf20Sopenharmony_ci	     (base, region_size, "tpm_atmel0") == NULL) ? 0 : 1;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	pdev = platform_device_register_simple("tpm_atmel", -1, NULL, 0);
1818c2ecf20Sopenharmony_ci	if (IS_ERR(pdev)) {
1828c2ecf20Sopenharmony_ci		rc = PTR_ERR(pdev);
1838c2ecf20Sopenharmony_ci		goto err_rel_reg;
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
1878c2ecf20Sopenharmony_ci	if (!priv) {
1888c2ecf20Sopenharmony_ci		rc = -ENOMEM;
1898c2ecf20Sopenharmony_ci		goto err_unreg_dev;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	priv->iobase = iobase;
1938c2ecf20Sopenharmony_ci	priv->base = base;
1948c2ecf20Sopenharmony_ci	priv->have_region = have_region;
1958c2ecf20Sopenharmony_ci	priv->region_size = region_size;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	chip = tpmm_chip_alloc(&pdev->dev, &tpm_atmel);
1988c2ecf20Sopenharmony_ci	if (IS_ERR(chip)) {
1998c2ecf20Sopenharmony_ci		rc = PTR_ERR(chip);
2008c2ecf20Sopenharmony_ci		goto err_unreg_dev;
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	dev_set_drvdata(&chip->dev, priv);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	rc = tpm_chip_register(chip);
2068c2ecf20Sopenharmony_ci	if (rc)
2078c2ecf20Sopenharmony_ci		goto err_unreg_dev;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	return 0;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cierr_unreg_dev:
2128c2ecf20Sopenharmony_ci	platform_device_unregister(pdev);
2138c2ecf20Sopenharmony_cierr_rel_reg:
2148c2ecf20Sopenharmony_ci	atmel_put_base_addr(iobase);
2158c2ecf20Sopenharmony_ci	if (have_region)
2168c2ecf20Sopenharmony_ci		atmel_release_region(base,
2178c2ecf20Sopenharmony_ci				     region_size);
2188c2ecf20Sopenharmony_cierr_unreg_drv:
2198c2ecf20Sopenharmony_ci	platform_driver_unregister(&atml_drv);
2208c2ecf20Sopenharmony_ci	return rc;
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic void __exit cleanup_atmel(void)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	platform_driver_unregister(&atml_drv);
2268c2ecf20Sopenharmony_ci	atml_plat_remove();
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cimodule_init(init_atmel);
2308c2ecf20Sopenharmony_cimodule_exit(cleanup_atmel);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
2338c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TPM Driver");
2348c2ecf20Sopenharmony_ciMODULE_VERSION("2.0");
2358c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
236