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