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, ®ion_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