18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2020 Linaro Ltd. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This device driver implements MMIO TPM on SynQuacer Platform. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/acpi.h> 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/of.h> 128c2ecf20Sopenharmony_ci#include <linux/of_device.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include "tpm.h" 158c2ecf20Sopenharmony_ci#include "tpm_tis_core.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* 188c2ecf20Sopenharmony_ci * irq > 0 means: use irq $irq; 198c2ecf20Sopenharmony_ci * irq = 0 means: autoprobe for an irq; 208c2ecf20Sopenharmony_ci * irq = -1 means: no irq support 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_cistruct tpm_tis_synquacer_info { 238c2ecf20Sopenharmony_ci struct resource res; 248c2ecf20Sopenharmony_ci int irq; 258c2ecf20Sopenharmony_ci}; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct tpm_tis_synquacer_phy { 288c2ecf20Sopenharmony_ci struct tpm_tis_data priv; 298c2ecf20Sopenharmony_ci void __iomem *iobase; 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic inline struct tpm_tis_synquacer_phy *to_tpm_tis_tcg_phy(struct tpm_tis_data *data) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci return container_of(data, struct tpm_tis_synquacer_phy, priv); 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic int tpm_tis_synquacer_read_bytes(struct tpm_tis_data *data, u32 addr, 388c2ecf20Sopenharmony_ci u16 len, u8 *result) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct tpm_tis_synquacer_phy *phy = to_tpm_tis_tcg_phy(data); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci while (len--) 438c2ecf20Sopenharmony_ci *result++ = ioread8(phy->iobase + addr); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci return 0; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int tpm_tis_synquacer_write_bytes(struct tpm_tis_data *data, u32 addr, 498c2ecf20Sopenharmony_ci u16 len, const u8 *value) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct tpm_tis_synquacer_phy *phy = to_tpm_tis_tcg_phy(data); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci while (len--) 548c2ecf20Sopenharmony_ci iowrite8(*value++, phy->iobase + addr); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci return 0; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic int tpm_tis_synquacer_read16_bw(struct tpm_tis_data *data, 608c2ecf20Sopenharmony_ci u32 addr, u16 *result) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct tpm_tis_synquacer_phy *phy = to_tpm_tis_tcg_phy(data); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci /* 658c2ecf20Sopenharmony_ci * Due to the limitation of SPI controller on SynQuacer, 668c2ecf20Sopenharmony_ci * 16/32 bits access must be done in byte-wise and descending order. 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_ci *result = (ioread8(phy->iobase + addr + 1) << 8) | 698c2ecf20Sopenharmony_ci (ioread8(phy->iobase + addr)); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci return 0; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic int tpm_tis_synquacer_read32_bw(struct tpm_tis_data *data, 758c2ecf20Sopenharmony_ci u32 addr, u32 *result) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct tpm_tis_synquacer_phy *phy = to_tpm_tis_tcg_phy(data); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* 808c2ecf20Sopenharmony_ci * Due to the limitation of SPI controller on SynQuacer, 818c2ecf20Sopenharmony_ci * 16/32 bits access must be done in byte-wise and descending order. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci *result = (ioread8(phy->iobase + addr + 3) << 24) | 848c2ecf20Sopenharmony_ci (ioread8(phy->iobase + addr + 2) << 16) | 858c2ecf20Sopenharmony_ci (ioread8(phy->iobase + addr + 1) << 8) | 868c2ecf20Sopenharmony_ci (ioread8(phy->iobase + addr)); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return 0; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int tpm_tis_synquacer_write32_bw(struct tpm_tis_data *data, 928c2ecf20Sopenharmony_ci u32 addr, u32 value) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct tpm_tis_synquacer_phy *phy = to_tpm_tis_tcg_phy(data); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* 978c2ecf20Sopenharmony_ci * Due to the limitation of SPI controller on SynQuacer, 988c2ecf20Sopenharmony_ci * 16/32 bits access must be done in byte-wise and descending order. 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_ci iowrite8(value >> 24, phy->iobase + addr + 3); 1018c2ecf20Sopenharmony_ci iowrite8(value >> 16, phy->iobase + addr + 2); 1028c2ecf20Sopenharmony_ci iowrite8(value >> 8, phy->iobase + addr + 1); 1038c2ecf20Sopenharmony_ci iowrite8(value, phy->iobase + addr); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci return 0; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic const struct tpm_tis_phy_ops tpm_tcg_bw = { 1098c2ecf20Sopenharmony_ci .read_bytes = tpm_tis_synquacer_read_bytes, 1108c2ecf20Sopenharmony_ci .write_bytes = tpm_tis_synquacer_write_bytes, 1118c2ecf20Sopenharmony_ci .read16 = tpm_tis_synquacer_read16_bw, 1128c2ecf20Sopenharmony_ci .read32 = tpm_tis_synquacer_read32_bw, 1138c2ecf20Sopenharmony_ci .write32 = tpm_tis_synquacer_write32_bw, 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic int tpm_tis_synquacer_init(struct device *dev, 1178c2ecf20Sopenharmony_ci struct tpm_tis_synquacer_info *tpm_info) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct tpm_tis_synquacer_phy *phy; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci phy = devm_kzalloc(dev, sizeof(struct tpm_tis_synquacer_phy), GFP_KERNEL); 1228c2ecf20Sopenharmony_ci if (phy == NULL) 1238c2ecf20Sopenharmony_ci return -ENOMEM; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci phy->iobase = devm_ioremap_resource(dev, &tpm_info->res); 1268c2ecf20Sopenharmony_ci if (IS_ERR(phy->iobase)) 1278c2ecf20Sopenharmony_ci return PTR_ERR(phy->iobase); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return tpm_tis_core_init(dev, &phy->priv, tpm_info->irq, &tpm_tcg_bw, 1308c2ecf20Sopenharmony_ci ACPI_HANDLE(dev)); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(tpm_tis_synquacer_pm, tpm_pm_suspend, tpm_tis_resume); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic int tpm_tis_synquacer_probe(struct platform_device *pdev) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct tpm_tis_synquacer_info tpm_info = {}; 1388c2ecf20Sopenharmony_ci struct resource *res; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1418c2ecf20Sopenharmony_ci if (res == NULL) { 1428c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no memory resource defined\n"); 1438c2ecf20Sopenharmony_ci return -ENODEV; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci tpm_info.res = *res; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci tpm_info.irq = -1; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return tpm_tis_synquacer_init(&pdev->dev, &tpm_info); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic int tpm_tis_synquacer_remove(struct platform_device *pdev) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct tpm_chip *chip = dev_get_drvdata(&pdev->dev); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci tpm_chip_unregister(chip); 1578c2ecf20Sopenharmony_ci tpm_tis_remove(chip); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return 0; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 1638c2ecf20Sopenharmony_cistatic const struct of_device_id tis_synquacer_of_platform_match[] = { 1648c2ecf20Sopenharmony_ci {.compatible = "socionext,synquacer-tpm-mmio"}, 1658c2ecf20Sopenharmony_ci {}, 1668c2ecf20Sopenharmony_ci}; 1678c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tis_synquacer_of_platform_match); 1688c2ecf20Sopenharmony_ci#endif 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI 1718c2ecf20Sopenharmony_cistatic const struct acpi_device_id tpm_synquacer_acpi_tbl[] = { 1728c2ecf20Sopenharmony_ci { "SCX0009" }, 1738c2ecf20Sopenharmony_ci {}, 1748c2ecf20Sopenharmony_ci}; 1758c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, tpm_synquacer_acpi_tbl); 1768c2ecf20Sopenharmony_ci#endif 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic struct platform_driver tis_synquacer_drv = { 1798c2ecf20Sopenharmony_ci .probe = tpm_tis_synquacer_probe, 1808c2ecf20Sopenharmony_ci .remove = tpm_tis_synquacer_remove, 1818c2ecf20Sopenharmony_ci .driver = { 1828c2ecf20Sopenharmony_ci .name = "tpm_tis_synquacer", 1838c2ecf20Sopenharmony_ci .pm = &tpm_tis_synquacer_pm, 1848c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(tis_synquacer_of_platform_match), 1858c2ecf20Sopenharmony_ci .acpi_match_table = ACPI_PTR(tpm_synquacer_acpi_tbl), 1868c2ecf20Sopenharmony_ci }, 1878c2ecf20Sopenharmony_ci}; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int __init tpm_tis_synquacer_module_init(void) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci int rc; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci rc = platform_driver_register(&tis_synquacer_drv); 1948c2ecf20Sopenharmony_ci if (rc) 1958c2ecf20Sopenharmony_ci return rc; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci return 0; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic void __exit tpm_tis_synquacer_module_exit(void) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci platform_driver_unregister(&tis_synquacer_drv); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cimodule_init(tpm_tis_synquacer_module_init); 2068c2ecf20Sopenharmony_cimodule_exit(tpm_tis_synquacer_module_exit); 2078c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TPM MMIO Driver for Socionext SynQuacer platform"); 2088c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 209