168 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2020 Linaro Ltd.4 *5 * This device driver implements MMIO TPM on SynQuacer Platform.6 */7#include <linux/acpi.h>8#include <linux/init.h>9#include <linux/module.h>10#include <linux/slab.h>11#include <linux/of.h>12#include <linux/kernel.h>13#include "tpm.h"14#include "tpm_tis_core.h"15 16/*17 * irq > 0 means: use irq $irq;18 * irq = 0 means: autoprobe for an irq;19 * irq = -1 means: no irq support20 */21struct tpm_tis_synquacer_info {22 struct resource res;23 int irq;24};25 26struct tpm_tis_synquacer_phy {27 struct tpm_tis_data priv;28 void __iomem *iobase;29};30 31static inline struct tpm_tis_synquacer_phy *to_tpm_tis_tcg_phy(struct tpm_tis_data *data)32{33 return container_of(data, struct tpm_tis_synquacer_phy, priv);34}35 36static int tpm_tis_synquacer_read_bytes(struct tpm_tis_data *data, u32 addr,37 u16 len, u8 *result,38 enum tpm_tis_io_mode io_mode)39{40 struct tpm_tis_synquacer_phy *phy = to_tpm_tis_tcg_phy(data);41 switch (io_mode) {42 case TPM_TIS_PHYS_8:43 while (len--)44 *result++ = ioread8(phy->iobase + addr);45 break;46 case TPM_TIS_PHYS_16:47 result[1] = ioread8(phy->iobase + addr + 1);48 result[0] = ioread8(phy->iobase + addr);49 break;50 case TPM_TIS_PHYS_32:51 result[3] = ioread8(phy->iobase + addr + 3);52 result[2] = ioread8(phy->iobase + addr + 2);53 result[1] = ioread8(phy->iobase + addr + 1);54 result[0] = ioread8(phy->iobase + addr);55 break;56 }57 58 return 0;59}60 61static int tpm_tis_synquacer_write_bytes(struct tpm_tis_data *data, u32 addr,62 u16 len, const u8 *value,63 enum tpm_tis_io_mode io_mode)64{65 struct tpm_tis_synquacer_phy *phy = to_tpm_tis_tcg_phy(data);66 switch (io_mode) {67 case TPM_TIS_PHYS_8:68 while (len--)69 iowrite8(*value++, phy->iobase + addr);70 break;71 case TPM_TIS_PHYS_16:72 return -EINVAL;73 case TPM_TIS_PHYS_32:74 /*75 * Due to the limitation of SPI controller on SynQuacer,76 * 16/32 bits access must be done in byte-wise and descending order.77 */78 iowrite8(value[3], phy->iobase + addr + 3);79 iowrite8(value[2], phy->iobase + addr + 2);80 iowrite8(value[1], phy->iobase + addr + 1);81 iowrite8(value[0], phy->iobase + addr);82 break;83 }84 85 return 0;86}87 88static const struct tpm_tis_phy_ops tpm_tcg_bw = {89 .read_bytes = tpm_tis_synquacer_read_bytes,90 .write_bytes = tpm_tis_synquacer_write_bytes,91};92 93static int tpm_tis_synquacer_init(struct device *dev,94 struct tpm_tis_synquacer_info *tpm_info)95{96 struct tpm_tis_synquacer_phy *phy;97 98 phy = devm_kzalloc(dev, sizeof(struct tpm_tis_synquacer_phy), GFP_KERNEL);99 if (phy == NULL)100 return -ENOMEM;101 102 phy->iobase = devm_ioremap_resource(dev, &tpm_info->res);103 if (IS_ERR(phy->iobase))104 return PTR_ERR(phy->iobase);105 106 return tpm_tis_core_init(dev, &phy->priv, tpm_info->irq, &tpm_tcg_bw,107 ACPI_HANDLE(dev));108}109 110static SIMPLE_DEV_PM_OPS(tpm_tis_synquacer_pm, tpm_pm_suspend, tpm_tis_resume);111 112static int tpm_tis_synquacer_probe(struct platform_device *pdev)113{114 struct tpm_tis_synquacer_info tpm_info = {};115 struct resource *res;116 117 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);118 if (res == NULL) {119 dev_err(&pdev->dev, "no memory resource defined\n");120 return -ENODEV;121 }122 tpm_info.res = *res;123 124 tpm_info.irq = -1;125 126 return tpm_tis_synquacer_init(&pdev->dev, &tpm_info);127}128 129static void tpm_tis_synquacer_remove(struct platform_device *pdev)130{131 struct tpm_chip *chip = dev_get_drvdata(&pdev->dev);132 133 tpm_chip_unregister(chip);134 tpm_tis_remove(chip);135}136 137#ifdef CONFIG_OF138static const struct of_device_id tis_synquacer_of_platform_match[] = {139 {.compatible = "socionext,synquacer-tpm-mmio"},140 {},141};142MODULE_DEVICE_TABLE(of, tis_synquacer_of_platform_match);143#endif144 145#ifdef CONFIG_ACPI146static const struct acpi_device_id tpm_synquacer_acpi_tbl[] = {147 { "SCX0009" },148 {},149};150MODULE_DEVICE_TABLE(acpi, tpm_synquacer_acpi_tbl);151#endif152 153static struct platform_driver tis_synquacer_drv = {154 .probe = tpm_tis_synquacer_probe,155 .remove_new = tpm_tis_synquacer_remove,156 .driver = {157 .name = "tpm_tis_synquacer",158 .pm = &tpm_tis_synquacer_pm,159 .of_match_table = of_match_ptr(tis_synquacer_of_platform_match),160 .acpi_match_table = ACPI_PTR(tpm_synquacer_acpi_tbl),161 },162};163 164module_platform_driver(tis_synquacer_drv);165 166MODULE_DESCRIPTION("TPM MMIO Driver for Socionext SynQuacer platform");167MODULE_LICENSE("GPL");168