1*d5ae56a4SMasahisa Kojima // SPDX-License-Identifier: GPL-2.0 2*d5ae56a4SMasahisa Kojima /* 3*d5ae56a4SMasahisa Kojima * Copyright (C) 2020 Linaro Ltd. 4*d5ae56a4SMasahisa Kojima * 5*d5ae56a4SMasahisa Kojima * This device driver implements MMIO TPM on SynQuacer Platform. 6*d5ae56a4SMasahisa Kojima */ 7*d5ae56a4SMasahisa Kojima #include <linux/acpi.h> 8*d5ae56a4SMasahisa Kojima #include <linux/init.h> 9*d5ae56a4SMasahisa Kojima #include <linux/module.h> 10*d5ae56a4SMasahisa Kojima #include <linux/slab.h> 11*d5ae56a4SMasahisa Kojima #include <linux/of.h> 12*d5ae56a4SMasahisa Kojima #include <linux/of_device.h> 13*d5ae56a4SMasahisa Kojima #include <linux/kernel.h> 14*d5ae56a4SMasahisa Kojima #include "tpm.h" 15*d5ae56a4SMasahisa Kojima #include "tpm_tis_core.h" 16*d5ae56a4SMasahisa Kojima 17*d5ae56a4SMasahisa Kojima /* 18*d5ae56a4SMasahisa Kojima * irq > 0 means: use irq $irq; 19*d5ae56a4SMasahisa Kojima * irq = 0 means: autoprobe for an irq; 20*d5ae56a4SMasahisa Kojima * irq = -1 means: no irq support 21*d5ae56a4SMasahisa Kojima */ 22*d5ae56a4SMasahisa Kojima struct tpm_tis_synquacer_info { 23*d5ae56a4SMasahisa Kojima struct resource res; 24*d5ae56a4SMasahisa Kojima int irq; 25*d5ae56a4SMasahisa Kojima }; 26*d5ae56a4SMasahisa Kojima 27*d5ae56a4SMasahisa Kojima struct tpm_tis_synquacer_phy { 28*d5ae56a4SMasahisa Kojima struct tpm_tis_data priv; 29*d5ae56a4SMasahisa Kojima void __iomem *iobase; 30*d5ae56a4SMasahisa Kojima }; 31*d5ae56a4SMasahisa Kojima 32*d5ae56a4SMasahisa Kojima static inline struct tpm_tis_synquacer_phy *to_tpm_tis_tcg_phy(struct tpm_tis_data *data) 33*d5ae56a4SMasahisa Kojima { 34*d5ae56a4SMasahisa Kojima return container_of(data, struct tpm_tis_synquacer_phy, priv); 35*d5ae56a4SMasahisa Kojima } 36*d5ae56a4SMasahisa Kojima 37*d5ae56a4SMasahisa Kojima static int tpm_tis_synquacer_read_bytes(struct tpm_tis_data *data, u32 addr, 38*d5ae56a4SMasahisa Kojima u16 len, u8 *result) 39*d5ae56a4SMasahisa Kojima { 40*d5ae56a4SMasahisa Kojima struct tpm_tis_synquacer_phy *phy = to_tpm_tis_tcg_phy(data); 41*d5ae56a4SMasahisa Kojima 42*d5ae56a4SMasahisa Kojima while (len--) 43*d5ae56a4SMasahisa Kojima *result++ = ioread8(phy->iobase + addr); 44*d5ae56a4SMasahisa Kojima 45*d5ae56a4SMasahisa Kojima return 0; 46*d5ae56a4SMasahisa Kojima } 47*d5ae56a4SMasahisa Kojima 48*d5ae56a4SMasahisa Kojima static int tpm_tis_synquacer_write_bytes(struct tpm_tis_data *data, u32 addr, 49*d5ae56a4SMasahisa Kojima u16 len, const u8 *value) 50*d5ae56a4SMasahisa Kojima { 51*d5ae56a4SMasahisa Kojima struct tpm_tis_synquacer_phy *phy = to_tpm_tis_tcg_phy(data); 52*d5ae56a4SMasahisa Kojima 53*d5ae56a4SMasahisa Kojima while (len--) 54*d5ae56a4SMasahisa Kojima iowrite8(*value++, phy->iobase + addr); 55*d5ae56a4SMasahisa Kojima 56*d5ae56a4SMasahisa Kojima return 0; 57*d5ae56a4SMasahisa Kojima } 58*d5ae56a4SMasahisa Kojima 59*d5ae56a4SMasahisa Kojima static int tpm_tis_synquacer_read16_bw(struct tpm_tis_data *data, 60*d5ae56a4SMasahisa Kojima u32 addr, u16 *result) 61*d5ae56a4SMasahisa Kojima { 62*d5ae56a4SMasahisa Kojima struct tpm_tis_synquacer_phy *phy = to_tpm_tis_tcg_phy(data); 63*d5ae56a4SMasahisa Kojima 64*d5ae56a4SMasahisa Kojima /* 65*d5ae56a4SMasahisa Kojima * Due to the limitation of SPI controller on SynQuacer, 66*d5ae56a4SMasahisa Kojima * 16/32 bits access must be done in byte-wise and descending order. 67*d5ae56a4SMasahisa Kojima */ 68*d5ae56a4SMasahisa Kojima *result = (ioread8(phy->iobase + addr + 1) << 8) | 69*d5ae56a4SMasahisa Kojima (ioread8(phy->iobase + addr)); 70*d5ae56a4SMasahisa Kojima 71*d5ae56a4SMasahisa Kojima return 0; 72*d5ae56a4SMasahisa Kojima } 73*d5ae56a4SMasahisa Kojima 74*d5ae56a4SMasahisa Kojima static int tpm_tis_synquacer_read32_bw(struct tpm_tis_data *data, 75*d5ae56a4SMasahisa Kojima u32 addr, u32 *result) 76*d5ae56a4SMasahisa Kojima { 77*d5ae56a4SMasahisa Kojima struct tpm_tis_synquacer_phy *phy = to_tpm_tis_tcg_phy(data); 78*d5ae56a4SMasahisa Kojima 79*d5ae56a4SMasahisa Kojima /* 80*d5ae56a4SMasahisa Kojima * Due to the limitation of SPI controller on SynQuacer, 81*d5ae56a4SMasahisa Kojima * 16/32 bits access must be done in byte-wise and descending order. 82*d5ae56a4SMasahisa Kojima */ 83*d5ae56a4SMasahisa Kojima *result = (ioread8(phy->iobase + addr + 3) << 24) | 84*d5ae56a4SMasahisa Kojima (ioread8(phy->iobase + addr + 2) << 16) | 85*d5ae56a4SMasahisa Kojima (ioread8(phy->iobase + addr + 1) << 8) | 86*d5ae56a4SMasahisa Kojima (ioread8(phy->iobase + addr)); 87*d5ae56a4SMasahisa Kojima 88*d5ae56a4SMasahisa Kojima return 0; 89*d5ae56a4SMasahisa Kojima } 90*d5ae56a4SMasahisa Kojima 91*d5ae56a4SMasahisa Kojima static int tpm_tis_synquacer_write32_bw(struct tpm_tis_data *data, 92*d5ae56a4SMasahisa Kojima u32 addr, u32 value) 93*d5ae56a4SMasahisa Kojima { 94*d5ae56a4SMasahisa Kojima struct tpm_tis_synquacer_phy *phy = to_tpm_tis_tcg_phy(data); 95*d5ae56a4SMasahisa Kojima 96*d5ae56a4SMasahisa Kojima /* 97*d5ae56a4SMasahisa Kojima * Due to the limitation of SPI controller on SynQuacer, 98*d5ae56a4SMasahisa Kojima * 16/32 bits access must be done in byte-wise and descending order. 99*d5ae56a4SMasahisa Kojima */ 100*d5ae56a4SMasahisa Kojima iowrite8(value >> 24, phy->iobase + addr + 3); 101*d5ae56a4SMasahisa Kojima iowrite8(value >> 16, phy->iobase + addr + 2); 102*d5ae56a4SMasahisa Kojima iowrite8(value >> 8, phy->iobase + addr + 1); 103*d5ae56a4SMasahisa Kojima iowrite8(value, phy->iobase + addr); 104*d5ae56a4SMasahisa Kojima 105*d5ae56a4SMasahisa Kojima return 0; 106*d5ae56a4SMasahisa Kojima } 107*d5ae56a4SMasahisa Kojima 108*d5ae56a4SMasahisa Kojima static const struct tpm_tis_phy_ops tpm_tcg_bw = { 109*d5ae56a4SMasahisa Kojima .read_bytes = tpm_tis_synquacer_read_bytes, 110*d5ae56a4SMasahisa Kojima .write_bytes = tpm_tis_synquacer_write_bytes, 111*d5ae56a4SMasahisa Kojima .read16 = tpm_tis_synquacer_read16_bw, 112*d5ae56a4SMasahisa Kojima .read32 = tpm_tis_synquacer_read32_bw, 113*d5ae56a4SMasahisa Kojima .write32 = tpm_tis_synquacer_write32_bw, 114*d5ae56a4SMasahisa Kojima }; 115*d5ae56a4SMasahisa Kojima 116*d5ae56a4SMasahisa Kojima static int tpm_tis_synquacer_init(struct device *dev, 117*d5ae56a4SMasahisa Kojima struct tpm_tis_synquacer_info *tpm_info) 118*d5ae56a4SMasahisa Kojima { 119*d5ae56a4SMasahisa Kojima struct tpm_tis_synquacer_phy *phy; 120*d5ae56a4SMasahisa Kojima 121*d5ae56a4SMasahisa Kojima phy = devm_kzalloc(dev, sizeof(struct tpm_tis_synquacer_phy), GFP_KERNEL); 122*d5ae56a4SMasahisa Kojima if (phy == NULL) 123*d5ae56a4SMasahisa Kojima return -ENOMEM; 124*d5ae56a4SMasahisa Kojima 125*d5ae56a4SMasahisa Kojima phy->iobase = devm_ioremap_resource(dev, &tpm_info->res); 126*d5ae56a4SMasahisa Kojima if (IS_ERR(phy->iobase)) 127*d5ae56a4SMasahisa Kojima return PTR_ERR(phy->iobase); 128*d5ae56a4SMasahisa Kojima 129*d5ae56a4SMasahisa Kojima return tpm_tis_core_init(dev, &phy->priv, tpm_info->irq, &tpm_tcg_bw, 130*d5ae56a4SMasahisa Kojima ACPI_HANDLE(dev)); 131*d5ae56a4SMasahisa Kojima } 132*d5ae56a4SMasahisa Kojima 133*d5ae56a4SMasahisa Kojima static SIMPLE_DEV_PM_OPS(tpm_tis_synquacer_pm, tpm_pm_suspend, tpm_tis_resume); 134*d5ae56a4SMasahisa Kojima 135*d5ae56a4SMasahisa Kojima static int tpm_tis_synquacer_probe(struct platform_device *pdev) 136*d5ae56a4SMasahisa Kojima { 137*d5ae56a4SMasahisa Kojima struct tpm_tis_synquacer_info tpm_info = {}; 138*d5ae56a4SMasahisa Kojima struct resource *res; 139*d5ae56a4SMasahisa Kojima 140*d5ae56a4SMasahisa Kojima res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 141*d5ae56a4SMasahisa Kojima if (res == NULL) { 142*d5ae56a4SMasahisa Kojima dev_err(&pdev->dev, "no memory resource defined\n"); 143*d5ae56a4SMasahisa Kojima return -ENODEV; 144*d5ae56a4SMasahisa Kojima } 145*d5ae56a4SMasahisa Kojima tpm_info.res = *res; 146*d5ae56a4SMasahisa Kojima 147*d5ae56a4SMasahisa Kojima tpm_info.irq = -1; 148*d5ae56a4SMasahisa Kojima 149*d5ae56a4SMasahisa Kojima return tpm_tis_synquacer_init(&pdev->dev, &tpm_info); 150*d5ae56a4SMasahisa Kojima } 151*d5ae56a4SMasahisa Kojima 152*d5ae56a4SMasahisa Kojima static int tpm_tis_synquacer_remove(struct platform_device *pdev) 153*d5ae56a4SMasahisa Kojima { 154*d5ae56a4SMasahisa Kojima struct tpm_chip *chip = dev_get_drvdata(&pdev->dev); 155*d5ae56a4SMasahisa Kojima 156*d5ae56a4SMasahisa Kojima tpm_chip_unregister(chip); 157*d5ae56a4SMasahisa Kojima tpm_tis_remove(chip); 158*d5ae56a4SMasahisa Kojima 159*d5ae56a4SMasahisa Kojima return 0; 160*d5ae56a4SMasahisa Kojima } 161*d5ae56a4SMasahisa Kojima 162*d5ae56a4SMasahisa Kojima #ifdef CONFIG_OF 163*d5ae56a4SMasahisa Kojima static const struct of_device_id tis_synquacer_of_platform_match[] = { 164*d5ae56a4SMasahisa Kojima {.compatible = "socionext,synquacer-tpm-mmio"}, 165*d5ae56a4SMasahisa Kojima {}, 166*d5ae56a4SMasahisa Kojima }; 167*d5ae56a4SMasahisa Kojima MODULE_DEVICE_TABLE(of, tis_synquacer_of_platform_match); 168*d5ae56a4SMasahisa Kojima #endif 169*d5ae56a4SMasahisa Kojima 170*d5ae56a4SMasahisa Kojima #ifdef CONFIG_ACPI 171*d5ae56a4SMasahisa Kojima static const struct acpi_device_id tpm_synquacer_acpi_tbl[] = { 172*d5ae56a4SMasahisa Kojima { "SCX0009" }, 173*d5ae56a4SMasahisa Kojima {}, 174*d5ae56a4SMasahisa Kojima }; 175*d5ae56a4SMasahisa Kojima MODULE_DEVICE_TABLE(acpi, tpm_synquacer_acpi_tbl); 176*d5ae56a4SMasahisa Kojima #endif 177*d5ae56a4SMasahisa Kojima 178*d5ae56a4SMasahisa Kojima static struct platform_driver tis_synquacer_drv = { 179*d5ae56a4SMasahisa Kojima .probe = tpm_tis_synquacer_probe, 180*d5ae56a4SMasahisa Kojima .remove = tpm_tis_synquacer_remove, 181*d5ae56a4SMasahisa Kojima .driver = { 182*d5ae56a4SMasahisa Kojima .name = "tpm_tis_synquacer", 183*d5ae56a4SMasahisa Kojima .pm = &tpm_tis_synquacer_pm, 184*d5ae56a4SMasahisa Kojima .of_match_table = of_match_ptr(tis_synquacer_of_platform_match), 185*d5ae56a4SMasahisa Kojima .acpi_match_table = ACPI_PTR(tpm_synquacer_acpi_tbl), 186*d5ae56a4SMasahisa Kojima }, 187*d5ae56a4SMasahisa Kojima }; 188*d5ae56a4SMasahisa Kojima 189*d5ae56a4SMasahisa Kojima static int __init tpm_tis_synquacer_module_init(void) 190*d5ae56a4SMasahisa Kojima { 191*d5ae56a4SMasahisa Kojima int rc; 192*d5ae56a4SMasahisa Kojima 193*d5ae56a4SMasahisa Kojima rc = platform_driver_register(&tis_synquacer_drv); 194*d5ae56a4SMasahisa Kojima if (rc) 195*d5ae56a4SMasahisa Kojima return rc; 196*d5ae56a4SMasahisa Kojima 197*d5ae56a4SMasahisa Kojima return 0; 198*d5ae56a4SMasahisa Kojima } 199*d5ae56a4SMasahisa Kojima 200*d5ae56a4SMasahisa Kojima static void __exit tpm_tis_synquacer_module_exit(void) 201*d5ae56a4SMasahisa Kojima { 202*d5ae56a4SMasahisa Kojima platform_driver_unregister(&tis_synquacer_drv); 203*d5ae56a4SMasahisa Kojima } 204*d5ae56a4SMasahisa Kojima 205*d5ae56a4SMasahisa Kojima module_init(tpm_tis_synquacer_module_init); 206*d5ae56a4SMasahisa Kojima module_exit(tpm_tis_synquacer_module_exit); 207*d5ae56a4SMasahisa Kojima MODULE_DESCRIPTION("TPM MMIO Driver for Socionext SynQuacer platform"); 208*d5ae56a4SMasahisa Kojima MODULE_LICENSE("GPL"); 209