linux/drivers/char/tpm/tpm_atmel.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2004 IBM Corporation
   3 *
   4 * Authors:
   5 * Leendert van Doorn <leendert@watson.ibm.com>
   6 * Dave Safford <safford@watson.ibm.com>
   7 * Reiner Sailer <sailer@watson.ibm.com>
   8 * Kylene Hall <kjhall@us.ibm.com>
   9 *
  10 * Maintained by: <tpmdd-devel@lists.sourceforge.net>
  11 *
  12 * Device driver for TCG/TCPA TPM (trusted platform module).
  13 * Specifications at www.trustedcomputinggroup.org       
  14 *
  15 * This program is free software; you can redistribute it and/or
  16 * modify it under the terms of the GNU General Public License as
  17 * published by the Free Software Foundation, version 2 of the
  18 * License.
  19 * 
  20 */
  21
  22#include "tpm.h"
  23#include "tpm_atmel.h"
  24
  25/* write status bits */
  26enum tpm_atmel_write_status {
  27        ATML_STATUS_ABORT = 0x01,
  28        ATML_STATUS_LASTBYTE = 0x04
  29};
  30/* read status bits */
  31enum tpm_atmel_read_status {
  32        ATML_STATUS_BUSY = 0x01,
  33        ATML_STATUS_DATA_AVAIL = 0x02,
  34        ATML_STATUS_REWRITE = 0x04,
  35        ATML_STATUS_READY = 0x08
  36};
  37
  38static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
  39{
  40        u8 status, *hdr = buf;
  41        u32 size;
  42        int i;
  43        __be32 *native_size;
  44
  45        /* start reading header */
  46        if (count < 6)
  47                return -EIO;
  48
  49        for (i = 0; i < 6; i++) {
  50                status = ioread8(chip->vendor.iobase + 1);
  51                if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
  52                        dev_err(chip->pdev, "error reading header\n");
  53                        return -EIO;
  54                }
  55                *buf++ = ioread8(chip->vendor.iobase);
  56        }
  57
  58        /* size of the data received */
  59        native_size = (__force __be32 *) (hdr + 2);
  60        size = be32_to_cpu(*native_size);
  61
  62        if (count < size) {
  63                dev_err(chip->pdev,
  64                        "Recv size(%d) less than available space\n", size);
  65                for (; i < size; i++) { /* clear the waiting data anyway */
  66                        status = ioread8(chip->vendor.iobase + 1);
  67                        if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
  68                                dev_err(chip->pdev, "error reading data\n");
  69                                return -EIO;
  70                        }
  71                }
  72                return -EIO;
  73        }
  74
  75        /* read all the data available */
  76        for (; i < size; i++) {
  77                status = ioread8(chip->vendor.iobase + 1);
  78                if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
  79                        dev_err(chip->pdev, "error reading data\n");
  80                        return -EIO;
  81                }
  82                *buf++ = ioread8(chip->vendor.iobase);
  83        }
  84
  85        /* make sure data available is gone */
  86        status = ioread8(chip->vendor.iobase + 1);
  87
  88        if (status & ATML_STATUS_DATA_AVAIL) {
  89                dev_err(chip->pdev, "data available is stuck\n");
  90                return -EIO;
  91        }
  92
  93        return size;
  94}
  95
  96static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count)
  97{
  98        int i;
  99
 100        dev_dbg(chip->pdev, "tpm_atml_send:\n");
 101        for (i = 0; i < count; i++) {
 102                dev_dbg(chip->pdev, "%d 0x%x(%d)\n",  i, buf[i], buf[i]);
 103                iowrite8(buf[i], chip->vendor.iobase);
 104        }
 105
 106        return count;
 107}
 108
 109static void tpm_atml_cancel(struct tpm_chip *chip)
 110{
 111        iowrite8(ATML_STATUS_ABORT, chip->vendor.iobase + 1);
 112}
 113
 114static u8 tpm_atml_status(struct tpm_chip *chip)
 115{
 116        return ioread8(chip->vendor.iobase + 1);
 117}
 118
 119static bool tpm_atml_req_canceled(struct tpm_chip *chip, u8 status)
 120{
 121        return (status == ATML_STATUS_READY);
 122}
 123
 124static const struct tpm_class_ops tpm_atmel = {
 125        .recv = tpm_atml_recv,
 126        .send = tpm_atml_send,
 127        .cancel = tpm_atml_cancel,
 128        .status = tpm_atml_status,
 129        .req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL,
 130        .req_complete_val = ATML_STATUS_DATA_AVAIL,
 131        .req_canceled = tpm_atml_req_canceled,
 132};
 133
 134static struct platform_device *pdev;
 135
 136static void atml_plat_remove(void)
 137{
 138        struct tpm_chip *chip = dev_get_drvdata(&pdev->dev);
 139
 140        if (chip) {
 141                tpm_chip_unregister(chip);
 142                if (chip->vendor.have_region)
 143                        atmel_release_region(chip->vendor.base,
 144                                             chip->vendor.region_size);
 145                atmel_put_base_addr(chip->vendor.iobase);
 146                platform_device_unregister(pdev);
 147        }
 148}
 149
 150static SIMPLE_DEV_PM_OPS(tpm_atml_pm, tpm_pm_suspend, tpm_pm_resume);
 151
 152static struct platform_driver atml_drv = {
 153        .driver = {
 154                .name = "tpm_atmel",
 155                .pm             = &tpm_atml_pm,
 156        },
 157};
 158
 159static int __init init_atmel(void)
 160{
 161        int rc = 0;
 162        void __iomem *iobase = NULL;
 163        int have_region, region_size;
 164        unsigned long base;
 165        struct  tpm_chip *chip;
 166
 167        rc = platform_driver_register(&atml_drv);
 168        if (rc)
 169                return rc;
 170
 171        if ((iobase = atmel_get_base_addr(&base, &region_size)) == NULL) {
 172                rc = -ENODEV;
 173                goto err_unreg_drv;
 174        }
 175
 176        have_region =
 177            (atmel_request_region
 178             (base, region_size, "tpm_atmel0") == NULL) ? 0 : 1;
 179
 180        pdev = platform_device_register_simple("tpm_atmel", -1, NULL, 0);
 181        if (IS_ERR(pdev)) {
 182                rc = PTR_ERR(pdev);
 183                goto err_rel_reg;
 184        }
 185
 186        chip = tpmm_chip_alloc(&pdev->dev, &tpm_atmel);
 187        if (IS_ERR(chip)) {
 188                rc = PTR_ERR(chip);
 189                goto err_unreg_dev;
 190        }
 191
 192        chip->vendor.iobase = iobase;
 193        chip->vendor.base = base;
 194        chip->vendor.have_region = have_region;
 195        chip->vendor.region_size = region_size;
 196
 197        rc = tpm_chip_register(chip);
 198        if (rc)
 199                goto err_unreg_dev;
 200
 201        return 0;
 202
 203err_unreg_dev:
 204        platform_device_unregister(pdev);
 205err_rel_reg:
 206        atmel_put_base_addr(iobase);
 207        if (have_region)
 208                atmel_release_region(base,
 209                                     region_size);
 210err_unreg_drv:
 211        platform_driver_unregister(&atml_drv);
 212        return rc;
 213}
 214
 215static void __exit cleanup_atmel(void)
 216{
 217        platform_driver_unregister(&atml_drv);
 218        atml_plat_remove();
 219}
 220
 221module_init(init_atmel);
 222module_exit(cleanup_atmel);
 223
 224MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
 225MODULE_DESCRIPTION("TPM Driver");
 226MODULE_VERSION("2.0");
 227MODULE_LICENSE("GPL");
 228