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->dev, "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->dev,
  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->dev, "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->dev, "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->dev, "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->dev, "tpm_atml_send:\n");
 101        for (i = 0; i < count; i++) {
 102                dev_dbg(chip->dev, "%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 const struct file_operations atmel_ops = {
 120        .owner = THIS_MODULE,
 121        .llseek = no_llseek,
 122        .open = tpm_open,
 123        .read = tpm_read,
 124        .write = tpm_write,
 125        .release = tpm_release,
 126};
 127
 128static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
 129static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
 130static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL);
 131static DEVICE_ATTR(cancel, S_IWUSR |S_IWGRP, NULL, tpm_store_cancel);
 132
 133static struct attribute* atmel_attrs[] = {
 134        &dev_attr_pubek.attr,
 135        &dev_attr_pcrs.attr,
 136        &dev_attr_caps.attr,
 137        &dev_attr_cancel.attr,
 138        NULL,
 139};
 140
 141static struct attribute_group atmel_attr_grp = { .attrs = atmel_attrs };
 142
 143static const struct tpm_vendor_specific tpm_atmel = {
 144        .recv = tpm_atml_recv,
 145        .send = tpm_atml_send,
 146        .cancel = tpm_atml_cancel,
 147        .status = tpm_atml_status,
 148        .req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL,
 149        .req_complete_val = ATML_STATUS_DATA_AVAIL,
 150        .req_canceled = ATML_STATUS_READY,
 151        .attr_group = &atmel_attr_grp,
 152        .miscdev = { .fops = &atmel_ops, },
 153};
 154
 155static struct platform_device *pdev;
 156
 157static void atml_plat_remove(void)
 158{
 159        struct tpm_chip *chip = dev_get_drvdata(&pdev->dev);
 160
 161        if (chip) {
 162                if (chip->vendor.have_region)
 163                        atmel_release_region(chip->vendor.base,
 164                                             chip->vendor.region_size);
 165                atmel_put_base_addr(chip->vendor.iobase);
 166                tpm_remove_hardware(chip->dev);
 167                platform_device_unregister(pdev);
 168        }
 169}
 170
 171static int tpm_atml_suspend(struct platform_device *dev, pm_message_t msg)
 172{
 173        return tpm_pm_suspend(&dev->dev, msg);
 174}
 175
 176static int tpm_atml_resume(struct platform_device *dev)
 177{
 178        return tpm_pm_resume(&dev->dev);
 179}
 180static struct platform_driver atml_drv = {
 181        .driver = {
 182                .name = "tpm_atmel",
 183                .owner          = THIS_MODULE,
 184        },
 185        .suspend = tpm_atml_suspend,
 186        .resume = tpm_atml_resume,
 187};
 188
 189static int __init init_atmel(void)
 190{
 191        int rc = 0;
 192        void __iomem *iobase = NULL;
 193        int have_region, region_size;
 194        unsigned long base;
 195        struct  tpm_chip *chip;
 196
 197        rc = platform_driver_register(&atml_drv);
 198        if (rc)
 199                return rc;
 200
 201        if ((iobase = atmel_get_base_addr(&base, &region_size)) == NULL) {
 202                rc = -ENODEV;
 203                goto err_unreg_drv;
 204        }
 205
 206        have_region =
 207            (atmel_request_region
 208             (tpm_atmel.base, region_size, "tpm_atmel0") == NULL) ? 0 : 1;
 209
 210        pdev = platform_device_register_simple("tpm_atmel", -1, NULL, 0);
 211        if (IS_ERR(pdev)) {
 212                rc = PTR_ERR(pdev);
 213                goto err_rel_reg;
 214        }
 215
 216        if (!(chip = tpm_register_hardware(&pdev->dev, &tpm_atmel))) {
 217                rc = -ENODEV;
 218                goto err_unreg_dev;
 219        }
 220
 221        chip->vendor.iobase = iobase;
 222        chip->vendor.base = base;
 223        chip->vendor.have_region = have_region;
 224        chip->vendor.region_size = region_size;
 225
 226        return 0;
 227
 228err_unreg_dev:
 229        platform_device_unregister(pdev);
 230err_rel_reg:
 231        atmel_put_base_addr(iobase);
 232        if (have_region)
 233                atmel_release_region(base,
 234                                     region_size);
 235err_unreg_drv:
 236        platform_driver_unregister(&atml_drv);
 237        return rc;
 238}
 239
 240static void __exit cleanup_atmel(void)
 241{
 242        platform_driver_unregister(&atml_drv);
 243        atml_plat_remove();
 244}
 245
 246module_init(init_atmel);
 247module_exit(cleanup_atmel);
 248
 249MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
 250MODULE_DESCRIPTION("TPM Driver");
 251MODULE_VERSION("2.0");
 252MODULE_LICENSE("GPL");
 253