| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * driver for mmio TCG/TIS TPM (trusted platform module). |
| * |
| * Specifications at www.trustedcomputinggroup.org |
| */ |
| |
| #include <common.h> |
| #include <dm.h> |
| #include <log.h> |
| #include <tpm-v2.h> |
| #include <linux/bitops.h> |
| #include <linux/compiler.h> |
| #include <linux/delay.h> |
| #include <linux/errno.h> |
| #include <linux/types.h> |
| #include <linux/io.h> |
| #include <linux/unaligned/be_byteshift.h> |
| #include "tpm_tis.h" |
| #include "tpm_internal.h" |
| |
| /** |
| * struct tpm_tis_chip_data - Information about an MMIO TPM |
| * @pcr_count: Number of PCR per bank |
| * @pcr_select_min: Minimum size in bytes of the pcrSelect array |
| * @iobase: Base address |
| */ |
| struct tpm_tis_chip_data { |
| unsigned int pcr_count; |
| unsigned int pcr_select_min; |
| void __iomem *iobase; |
| }; |
| |
| static int mmio_read_bytes(struct udevice *dev, u32 addr, u16 len, |
| u8 *result) |
| { |
| struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(dev); |
| |
| while (len--) |
| *result++ = ioread8(drv_data->iobase + addr); |
| |
| return 0; |
| } |
| |
| static int mmio_write_bytes(struct udevice *dev, u32 addr, u16 len, |
| const u8 *value) |
| { |
| struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(dev); |
| |
| while (len--) |
| iowrite8(*value++, drv_data->iobase + addr); |
| |
| return 0; |
| } |
| |
| static int mmio_read32(struct udevice *dev, u32 addr, u32 *result) |
| { |
| struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(dev); |
| |
| *result = ioread32(drv_data->iobase + addr); |
| |
| return 0; |
| } |
| |
| static int mmio_write32(struct udevice *dev, u32 addr, u32 value) |
| { |
| struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(dev); |
| |
| iowrite32(value, drv_data->iobase + addr); |
| |
| return 0; |
| } |
| |
| static struct tpm_tis_phy_ops phy_ops = { |
| .read_bytes = mmio_read_bytes, |
| .write_bytes = mmio_write_bytes, |
| .read32 = mmio_read32, |
| .write32 = mmio_write32, |
| }; |
| |
| static int tpm_tis_probe(struct udevice *dev) |
| { |
| struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(dev); |
| struct tpm_chip_priv *priv = dev_get_uclass_priv(dev); |
| int ret = 0; |
| fdt_addr_t ioaddr; |
| u64 sz; |
| |
| ioaddr = dev_read_addr(dev); |
| if (ioaddr == FDT_ADDR_T_NONE) |
| return log_msg_ret("ioaddr", -EINVAL); |
| |
| ret = dev_read_u64(dev, "reg", &sz); |
| if (ret) |
| return -EINVAL; |
| |
| drv_data->iobase = ioremap(ioaddr, sz); |
| tpm_tis_ops_register(dev, &phy_ops); |
| ret = tpm_tis_init(dev); |
| if (ret) |
| goto iounmap; |
| |
| priv->pcr_count = drv_data->pcr_count; |
| priv->pcr_select_min = drv_data->pcr_select_min; |
| /* |
| * Although the driver probably works with a TPMv1 our Kconfig |
| * limits the driver to TPMv2 only |
| */ |
| priv->version = TPM_V2; |
| |
| return ret; |
| iounmap: |
| iounmap(drv_data->iobase); |
| |
| return -EINVAL; |
| } |
| |
| static int tpm_tis_remove(struct udevice *dev) |
| { |
| struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(dev); |
| int ret; |
| |
| ret = tpm_tis_cleanup(dev); |
| |
| iounmap(drv_data->iobase); |
| |
| return ret; |
| } |
| |
| static const struct tpm_ops tpm_tis_ops = { |
| .open = tpm_tis_open, |
| .close = tpm_tis_close, |
| .get_desc = tpm_tis_get_desc, |
| .send = tpm_tis_send, |
| .recv = tpm_tis_recv, |
| .cleanup = tpm_tis_cleanup, |
| }; |
| |
| static const struct tpm_tis_chip_data tpm_tis_std_chip_data = { |
| .pcr_count = 24, |
| .pcr_select_min = 3, |
| }; |
| |
| static const struct udevice_id tpm_tis_ids[] = { |
| { |
| .compatible = "tcg,tpm-tis-mmio", |
| .data = (ulong)&tpm_tis_std_chip_data, |
| }, |
| { } |
| }; |
| |
| U_BOOT_DRIVER(tpm_tis_mmio) = { |
| .name = "tpm_tis_mmio", |
| .id = UCLASS_TPM, |
| .of_match = tpm_tis_ids, |
| .ops = &tpm_tis_ops, |
| .probe = tpm_tis_probe, |
| .remove = tpm_tis_remove, |
| .priv_auto = sizeof(struct tpm_chip), |
| }; |