linux/drivers/char/tpm/tpm_tis.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2005, 2006 IBM Corporation
   3 * Copyright (C) 2014, 2015 Intel Corporation
   4 *
   5 * Authors:
   6 * Leendert van Doorn <leendert@watson.ibm.com>
   7 * Kylene Hall <kjhall@us.ibm.com>
   8 *
   9 * Maintained by: <tpmdd-devel@lists.sourceforge.net>
  10 *
  11 * Device driver for TCG/TCPA TPM (trusted platform module).
  12 * Specifications at www.trustedcomputinggroup.org
  13 *
  14 * This device driver implements the TPM interface as defined in
  15 * the TCG TPM Interface Spec version 1.2, revision 1.0.
  16 *
  17 * This program is free software; you can redistribute it and/or
  18 * modify it under the terms of the GNU General Public License as
  19 * published by the Free Software Foundation, version 2 of the
  20 * License.
  21 */
  22#include <linux/init.h>
  23#include <linux/module.h>
  24#include <linux/moduleparam.h>
  25#include <linux/pnp.h>
  26#include <linux/slab.h>
  27#include <linux/interrupt.h>
  28#include <linux/wait.h>
  29#include <linux/acpi.h>
  30#include <linux/freezer.h>
  31#include <linux/of.h>
  32#include <linux/of_device.h>
  33#include <linux/kernel.h>
  34#include "tpm.h"
  35#include "tpm_tis_core.h"
  36
  37struct tpm_info {
  38        struct resource res;
  39        /* irq > 0 means: use irq $irq;
  40         * irq = 0 means: autoprobe for an irq;
  41         * irq = -1 means: no irq support
  42         */
  43        int irq;
  44};
  45
  46struct tpm_tis_tcg_phy {
  47        struct tpm_tis_data priv;
  48        void __iomem *iobase;
  49};
  50
  51static inline struct tpm_tis_tcg_phy *to_tpm_tis_tcg_phy(struct tpm_tis_data *data)
  52{
  53        return container_of(data, struct tpm_tis_tcg_phy, priv);
  54}
  55
  56static bool interrupts = true;
  57module_param(interrupts, bool, 0444);
  58MODULE_PARM_DESC(interrupts, "Enable interrupts");
  59
  60static bool itpm;
  61module_param(itpm, bool, 0444);
  62MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)");
  63
  64static bool force;
  65#ifdef CONFIG_X86
  66module_param(force, bool, 0444);
  67MODULE_PARM_DESC(force, "Force device probe rather than using ACPI entry");
  68#endif
  69
  70#if defined(CONFIG_PNP) && defined(CONFIG_ACPI)
  71static int has_hid(struct acpi_device *dev, const char *hid)
  72{
  73        struct acpi_hardware_id *id;
  74
  75        list_for_each_entry(id, &dev->pnp.ids, list)
  76                if (!strcmp(hid, id->id))
  77                        return 1;
  78
  79        return 0;
  80}
  81
  82static inline int is_itpm(struct acpi_device *dev)
  83{
  84        if (!dev)
  85                return 0;
  86        return has_hid(dev, "INTC0102");
  87}
  88#else
  89static inline int is_itpm(struct acpi_device *dev)
  90{
  91        return 0;
  92}
  93#endif
  94
  95#if defined(CONFIG_ACPI)
  96#define DEVICE_IS_TPM2 1
  97
  98static const struct acpi_device_id tpm_acpi_tbl[] = {
  99        {"MSFT0101", DEVICE_IS_TPM2},
 100        {},
 101};
 102MODULE_DEVICE_TABLE(acpi, tpm_acpi_tbl);
 103
 104static int check_acpi_tpm2(struct device *dev)
 105{
 106        const struct acpi_device_id *aid = acpi_match_device(tpm_acpi_tbl, dev);
 107        struct acpi_table_tpm2 *tbl;
 108        acpi_status st;
 109
 110        if (!aid || aid->driver_data != DEVICE_IS_TPM2)
 111                return 0;
 112
 113        /* If the ACPI TPM2 signature is matched then a global ACPI_SIG_TPM2
 114         * table is mandatory
 115         */
 116        st =
 117            acpi_get_table(ACPI_SIG_TPM2, 1, (struct acpi_table_header **)&tbl);
 118        if (ACPI_FAILURE(st) || tbl->header.length < sizeof(*tbl)) {
 119                dev_err(dev, FW_BUG "failed to get TPM2 ACPI table\n");
 120                return -EINVAL;
 121        }
 122
 123        /* The tpm2_crb driver handles this device */
 124        if (tbl->start_method != ACPI_TPM2_MEMORY_MAPPED)
 125                return -ENODEV;
 126
 127        return 0;
 128}
 129#else
 130static int check_acpi_tpm2(struct device *dev)
 131{
 132        return 0;
 133}
 134#endif
 135
 136static int tpm_tcg_read_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
 137                              u8 *result)
 138{
 139        struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
 140
 141        while (len--)
 142                *result++ = ioread8(phy->iobase + addr);
 143
 144        return 0;
 145}
 146
 147static int tpm_tcg_write_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
 148                               const u8 *value)
 149{
 150        struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
 151
 152        while (len--)
 153                iowrite8(*value++, phy->iobase + addr);
 154
 155        return 0;
 156}
 157
 158static int tpm_tcg_read16(struct tpm_tis_data *data, u32 addr, u16 *result)
 159{
 160        struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
 161
 162        *result = ioread16(phy->iobase + addr);
 163
 164        return 0;
 165}
 166
 167static int tpm_tcg_read32(struct tpm_tis_data *data, u32 addr, u32 *result)
 168{
 169        struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
 170
 171        *result = ioread32(phy->iobase + addr);
 172
 173        return 0;
 174}
 175
 176static int tpm_tcg_write32(struct tpm_tis_data *data, u32 addr, u32 value)
 177{
 178        struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
 179
 180        iowrite32(value, phy->iobase + addr);
 181
 182        return 0;
 183}
 184
 185static const struct tpm_tis_phy_ops tpm_tcg = {
 186        .read_bytes = tpm_tcg_read_bytes,
 187        .write_bytes = tpm_tcg_write_bytes,
 188        .read16 = tpm_tcg_read16,
 189        .read32 = tpm_tcg_read32,
 190        .write32 = tpm_tcg_write32,
 191};
 192
 193static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info)
 194{
 195        struct tpm_tis_tcg_phy *phy;
 196        int irq = -1;
 197        int rc;
 198
 199        rc = check_acpi_tpm2(dev);
 200        if (rc)
 201                return rc;
 202
 203        phy = devm_kzalloc(dev, sizeof(struct tpm_tis_tcg_phy), GFP_KERNEL);
 204        if (phy == NULL)
 205                return -ENOMEM;
 206
 207        phy->iobase = devm_ioremap_resource(dev, &tpm_info->res);
 208        if (IS_ERR(phy->iobase))
 209                return PTR_ERR(phy->iobase);
 210
 211        if (interrupts)
 212                irq = tpm_info->irq;
 213
 214        if (itpm || is_itpm(ACPI_COMPANION(dev)))
 215                phy->priv.flags |= TPM_TIS_ITPM_WORKAROUND;
 216
 217        return tpm_tis_core_init(dev, &phy->priv, irq, &tpm_tcg,
 218                                 ACPI_HANDLE(dev));
 219}
 220
 221static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume);
 222
 223static int tpm_tis_pnp_init(struct pnp_dev *pnp_dev,
 224                            const struct pnp_device_id *pnp_id)
 225{
 226        struct tpm_info tpm_info = {};
 227        struct resource *res;
 228
 229        res = pnp_get_resource(pnp_dev, IORESOURCE_MEM, 0);
 230        if (!res)
 231                return -ENODEV;
 232        tpm_info.res = *res;
 233
 234        if (pnp_irq_valid(pnp_dev, 0))
 235                tpm_info.irq = pnp_irq(pnp_dev, 0);
 236        else
 237                tpm_info.irq = -1;
 238
 239        return tpm_tis_init(&pnp_dev->dev, &tpm_info);
 240}
 241
 242static struct pnp_device_id tpm_pnp_tbl[] = {
 243        {"PNP0C31", 0},         /* TPM */
 244        {"ATM1200", 0},         /* Atmel */
 245        {"IFX0102", 0},         /* Infineon */
 246        {"BCM0101", 0},         /* Broadcom */
 247        {"BCM0102", 0},         /* Broadcom */
 248        {"NSC1200", 0},         /* National */
 249        {"ICO0102", 0},         /* Intel */
 250        /* Add new here */
 251        {"", 0},                /* User Specified */
 252        {"", 0}                 /* Terminator */
 253};
 254MODULE_DEVICE_TABLE(pnp, tpm_pnp_tbl);
 255
 256static void tpm_tis_pnp_remove(struct pnp_dev *dev)
 257{
 258        struct tpm_chip *chip = pnp_get_drvdata(dev);
 259
 260        tpm_chip_unregister(chip);
 261        tpm_tis_remove(chip);
 262}
 263
 264static struct pnp_driver tis_pnp_driver = {
 265        .name = "tpm_tis",
 266        .id_table = tpm_pnp_tbl,
 267        .probe = tpm_tis_pnp_init,
 268        .remove = tpm_tis_pnp_remove,
 269        .driver = {
 270                .pm = &tpm_tis_pm,
 271        },
 272};
 273
 274#define TIS_HID_USR_IDX (ARRAY_SIZE(tpm_pnp_tbl) - 2)
 275module_param_string(hid, tpm_pnp_tbl[TIS_HID_USR_IDX].id,
 276                    sizeof(tpm_pnp_tbl[TIS_HID_USR_IDX].id), 0444);
 277MODULE_PARM_DESC(hid, "Set additional specific HID for this driver to probe");
 278
 279static struct platform_device *force_pdev;
 280
 281static int tpm_tis_plat_probe(struct platform_device *pdev)
 282{
 283        struct tpm_info tpm_info = {};
 284        struct resource *res;
 285
 286        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 287        if (res == NULL) {
 288                dev_err(&pdev->dev, "no memory resource defined\n");
 289                return -ENODEV;
 290        }
 291        tpm_info.res = *res;
 292
 293        tpm_info.irq = platform_get_irq(pdev, 0);
 294        if (tpm_info.irq <= 0) {
 295                if (pdev != force_pdev)
 296                        tpm_info.irq = -1;
 297                else
 298                        /* When forcing auto probe the IRQ */
 299                        tpm_info.irq = 0;
 300        }
 301
 302        return tpm_tis_init(&pdev->dev, &tpm_info);
 303}
 304
 305static int tpm_tis_plat_remove(struct platform_device *pdev)
 306{
 307        struct tpm_chip *chip = dev_get_drvdata(&pdev->dev);
 308
 309        tpm_chip_unregister(chip);
 310        tpm_tis_remove(chip);
 311
 312        return 0;
 313}
 314
 315#ifdef CONFIG_OF
 316static const struct of_device_id tis_of_platform_match[] = {
 317        {.compatible = "tcg,tpm-tis-mmio"},
 318        {},
 319};
 320MODULE_DEVICE_TABLE(of, tis_of_platform_match);
 321#endif
 322
 323static struct platform_driver tis_drv = {
 324        .probe = tpm_tis_plat_probe,
 325        .remove = tpm_tis_plat_remove,
 326        .driver = {
 327                .name           = "tpm_tis",
 328                .pm             = &tpm_tis_pm,
 329                .of_match_table = of_match_ptr(tis_of_platform_match),
 330                .acpi_match_table = ACPI_PTR(tpm_acpi_tbl),
 331        },
 332};
 333
 334static int tpm_tis_force_device(void)
 335{
 336        struct platform_device *pdev;
 337        static const struct resource x86_resources[] = {
 338                {
 339                        .start = 0xFED40000,
 340                        .end = 0xFED40000 + TIS_MEM_LEN - 1,
 341                        .flags = IORESOURCE_MEM,
 342                },
 343        };
 344
 345        if (!force)
 346                return 0;
 347
 348        /* The driver core will match the name tpm_tis of the device to
 349         * the tpm_tis platform driver and complete the setup via
 350         * tpm_tis_plat_probe
 351         */
 352        pdev = platform_device_register_simple("tpm_tis", -1, x86_resources,
 353                                               ARRAY_SIZE(x86_resources));
 354        if (IS_ERR(pdev))
 355                return PTR_ERR(pdev);
 356        force_pdev = pdev;
 357
 358        return 0;
 359}
 360
 361static int __init init_tis(void)
 362{
 363        int rc;
 364
 365        rc = tpm_tis_force_device();
 366        if (rc)
 367                goto err_force;
 368
 369        rc = platform_driver_register(&tis_drv);
 370        if (rc)
 371                goto err_platform;
 372
 373
 374        if (IS_ENABLED(CONFIG_PNP)) {
 375                rc = pnp_register_driver(&tis_pnp_driver);
 376                if (rc)
 377                        goto err_pnp;
 378        }
 379
 380        return 0;
 381
 382err_pnp:
 383        platform_driver_unregister(&tis_drv);
 384err_platform:
 385        if (force_pdev)
 386                platform_device_unregister(force_pdev);
 387err_force:
 388        return rc;
 389}
 390
 391static void __exit cleanup_tis(void)
 392{
 393        pnp_unregister_driver(&tis_pnp_driver);
 394        platform_driver_unregister(&tis_drv);
 395
 396        if (force_pdev)
 397                platform_device_unregister(force_pdev);
 398}
 399
 400module_init(init_tis);
 401module_exit(cleanup_tis);
 402MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
 403MODULE_DESCRIPTION("TPM Driver");
 404MODULE_VERSION("2.0");
 405MODULE_LICENSE("GPL");
 406