11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Copyright (C) 2004 IBM Corporation 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Authors: 51da177e4SLinus Torvalds * Leendert van Doorn <leendert@watson.ibm.com> 61da177e4SLinus Torvalds * Dave Safford <safford@watson.ibm.com> 71da177e4SLinus Torvalds * Reiner Sailer <sailer@watson.ibm.com> 81da177e4SLinus Torvalds * Kylene Hall <kjhall@us.ibm.com> 91da177e4SLinus Torvalds * 108e81cc13SKent Yoder * Maintained by: <tpmdd-devel@lists.sourceforge.net> 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * Device driver for TCG/TCPA TPM (trusted platform module). 131da177e4SLinus Torvalds * Specifications at www.trustedcomputinggroup.org 141da177e4SLinus Torvalds * 151da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 161da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License as 171da177e4SLinus Torvalds * published by the Free Software Foundation, version 2 of the 181da177e4SLinus Torvalds * License. 191da177e4SLinus Torvalds * 201da177e4SLinus Torvalds */ 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds #include "tpm.h" 23ad5ea3ccSKylene Jo Hall #include "tpm_atmel.h" 241da177e4SLinus Torvalds 251da177e4SLinus Torvalds /* write status bits */ 263122a88aSKylene Hall enum tpm_atmel_write_status { 273122a88aSKylene Hall ATML_STATUS_ABORT = 0x01, 283122a88aSKylene Hall ATML_STATUS_LASTBYTE = 0x04 293122a88aSKylene Hall }; 301da177e4SLinus Torvalds /* read status bits */ 313122a88aSKylene Hall enum tpm_atmel_read_status { 323122a88aSKylene Hall ATML_STATUS_BUSY = 0x01, 333122a88aSKylene Hall ATML_STATUS_DATA_AVAIL = 0x02, 343122a88aSKylene Hall ATML_STATUS_REWRITE = 0x04, 353122a88aSKylene Hall ATML_STATUS_READY = 0x08 363122a88aSKylene Hall }; 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count) 391da177e4SLinus Torvalds { 401da177e4SLinus Torvalds u8 status, *hdr = buf; 411da177e4SLinus Torvalds u32 size; 421da177e4SLinus Torvalds int i; 431da177e4SLinus Torvalds __be32 *native_size; 441da177e4SLinus Torvalds 451da177e4SLinus Torvalds /* start reading header */ 461da177e4SLinus Torvalds if (count < 6) 471da177e4SLinus Torvalds return -EIO; 481da177e4SLinus Torvalds 491da177e4SLinus Torvalds for (i = 0; i < 6; i++) { 5090dda520SKylene Jo Hall status = ioread8(chip->vendor.iobase + 1); 511da177e4SLinus Torvalds if ((status & ATML_STATUS_DATA_AVAIL) == 0) { 528cfffc9dSJason Gunthorpe dev_err(&chip->dev, "error reading header\n"); 531da177e4SLinus Torvalds return -EIO; 541da177e4SLinus Torvalds } 5590dda520SKylene Jo Hall *buf++ = ioread8(chip->vendor.iobase); 561da177e4SLinus Torvalds } 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds /* size of the data received */ 591da177e4SLinus Torvalds native_size = (__force __be32 *) (hdr + 2); 601da177e4SLinus Torvalds size = be32_to_cpu(*native_size); 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds if (count < size) { 638cfffc9dSJason Gunthorpe dev_err(&chip->dev, 641da177e4SLinus Torvalds "Recv size(%d) less than available space\n", size); 651da177e4SLinus Torvalds for (; i < size; i++) { /* clear the waiting data anyway */ 6690dda520SKylene Jo Hall status = ioread8(chip->vendor.iobase + 1); 671da177e4SLinus Torvalds if ((status & ATML_STATUS_DATA_AVAIL) == 0) { 688cfffc9dSJason Gunthorpe dev_err(&chip->dev, "error reading data\n"); 691da177e4SLinus Torvalds return -EIO; 701da177e4SLinus Torvalds } 711da177e4SLinus Torvalds } 721da177e4SLinus Torvalds return -EIO; 731da177e4SLinus Torvalds } 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds /* read all the data available */ 761da177e4SLinus Torvalds for (; i < size; i++) { 7790dda520SKylene Jo Hall status = ioread8(chip->vendor.iobase + 1); 781da177e4SLinus Torvalds if ((status & ATML_STATUS_DATA_AVAIL) == 0) { 798cfffc9dSJason Gunthorpe dev_err(&chip->dev, "error reading data\n"); 801da177e4SLinus Torvalds return -EIO; 811da177e4SLinus Torvalds } 8290dda520SKylene Jo Hall *buf++ = ioread8(chip->vendor.iobase); 831da177e4SLinus Torvalds } 841da177e4SLinus Torvalds 851da177e4SLinus Torvalds /* make sure data available is gone */ 8690dda520SKylene Jo Hall status = ioread8(chip->vendor.iobase + 1); 8790612b30SKylene Jo Hall 881da177e4SLinus Torvalds if (status & ATML_STATUS_DATA_AVAIL) { 898cfffc9dSJason Gunthorpe dev_err(&chip->dev, "data available is stuck\n"); 901da177e4SLinus Torvalds return -EIO; 911da177e4SLinus Torvalds } 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds return size; 941da177e4SLinus Torvalds } 951da177e4SLinus Torvalds 961da177e4SLinus Torvalds static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count) 971da177e4SLinus Torvalds { 981da177e4SLinus Torvalds int i; 991da177e4SLinus Torvalds 1008cfffc9dSJason Gunthorpe dev_dbg(&chip->dev, "tpm_atml_send:\n"); 1011da177e4SLinus Torvalds for (i = 0; i < count; i++) { 1028cfffc9dSJason Gunthorpe dev_dbg(&chip->dev, "%d 0x%x(%d)\n", i, buf[i], buf[i]); 10390dda520SKylene Jo Hall iowrite8(buf[i], chip->vendor.iobase); 1041da177e4SLinus Torvalds } 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds return count; 1071da177e4SLinus Torvalds } 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds static void tpm_atml_cancel(struct tpm_chip *chip) 1101da177e4SLinus Torvalds { 11190dda520SKylene Jo Hall iowrite8(ATML_STATUS_ABORT, chip->vendor.iobase + 1); 1121da177e4SLinus Torvalds } 1131da177e4SLinus Torvalds 114b4ed3e3cSKylene Jo Hall static u8 tpm_atml_status(struct tpm_chip *chip) 115b4ed3e3cSKylene Jo Hall { 11690dda520SKylene Jo Hall return ioread8(chip->vendor.iobase + 1); 117b4ed3e3cSKylene Jo Hall } 118b4ed3e3cSKylene Jo Hall 1191f866057SStefan Berger static bool tpm_atml_req_canceled(struct tpm_chip *chip, u8 status) 1201f866057SStefan Berger { 1211f866057SStefan Berger return (status == ATML_STATUS_READY); 1221f866057SStefan Berger } 1231f866057SStefan Berger 12401ad1fa7SJason Gunthorpe static const struct tpm_class_ops tpm_atmel = { 1251da177e4SLinus Torvalds .recv = tpm_atml_recv, 1261da177e4SLinus Torvalds .send = tpm_atml_send, 1271da177e4SLinus Torvalds .cancel = tpm_atml_cancel, 128b4ed3e3cSKylene Jo Hall .status = tpm_atml_status, 1291da177e4SLinus Torvalds .req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL, 1301da177e4SLinus Torvalds .req_complete_val = ATML_STATUS_DATA_AVAIL, 1311f866057SStefan Berger .req_canceled = tpm_atml_req_canceled, 1321da177e4SLinus Torvalds }; 1331da177e4SLinus Torvalds 134b888c87bSAndrew Morton static struct platform_device *pdev; 135682e97acSKylene Jo Hall 136ad5ea3ccSKylene Jo Hall static void atml_plat_remove(void) 1371da177e4SLinus Torvalds { 138ad5ea3ccSKylene Jo Hall struct tpm_chip *chip = dev_get_drvdata(&pdev->dev); 139ad5ea3ccSKylene Jo Hall 140682e97acSKylene Jo Hall if (chip) { 141afb5abc2SJarkko Sakkinen tpm_chip_unregister(chip); 14290dda520SKylene Jo Hall if (chip->vendor.have_region) 14390dda520SKylene Jo Hall atmel_release_region(chip->vendor.base, 14490dda520SKylene Jo Hall chip->vendor.region_size); 14590dda520SKylene Jo Hall atmel_put_base_addr(chip->vendor.iobase); 146ad5ea3ccSKylene Jo Hall platform_device_unregister(pdev); 147e659a3feSKylene Jo Hall } 148682e97acSKylene Jo Hall } 149e659a3feSKylene Jo Hall 1508324be05SRafael J. Wysocki static SIMPLE_DEV_PM_OPS(tpm_atml_pm, tpm_pm_suspend, tpm_pm_resume); 1517a192ec3SMing Lei 1527a192ec3SMing Lei static struct platform_driver atml_drv = { 1537a192ec3SMing Lei .driver = { 1541da177e4SLinus Torvalds .name = "tpm_atmel", 1558324be05SRafael J. Wysocki .pm = &tpm_atml_pm, 1567a192ec3SMing Lei }, 1571da177e4SLinus Torvalds }; 1581da177e4SLinus Torvalds 1591da177e4SLinus Torvalds static int __init init_atmel(void) 1601da177e4SLinus Torvalds { 161682e97acSKylene Jo Hall int rc = 0; 162e0dd03caSKylene Jo Hall void __iomem *iobase = NULL; 163e0dd03caSKylene Jo Hall int have_region, region_size; 164e0dd03caSKylene Jo Hall unsigned long base; 165e0dd03caSKylene Jo Hall struct tpm_chip *chip; 166682e97acSKylene Jo Hall 1677a192ec3SMing Lei rc = platform_driver_register(&atml_drv); 168f33d9bd5SJeff Garzik if (rc) 169f33d9bd5SJeff Garzik return rc; 170682e97acSKylene Jo Hall 171e0dd03caSKylene Jo Hall if ((iobase = atmel_get_base_addr(&base, ®ion_size)) == NULL) { 172ad5ea3ccSKylene Jo Hall rc = -ENODEV; 173ad5ea3ccSKylene Jo Hall goto err_unreg_drv; 174682e97acSKylene Jo Hall } 175682e97acSKylene Jo Hall 176e0dd03caSKylene Jo Hall have_region = 17790612b30SKylene Jo Hall (atmel_request_region 1781e6e0974SJason Gunthorpe (base, region_size, "tpm_atmel0") == NULL) ? 0 : 1; 179e0dd03caSKylene Jo Hall 180f33d9bd5SJeff Garzik pdev = platform_device_register_simple("tpm_atmel", -1, NULL, 0); 181f33d9bd5SJeff Garzik if (IS_ERR(pdev)) { 182ad5ea3ccSKylene Jo Hall rc = PTR_ERR(pdev); 183ad5ea3ccSKylene Jo Hall goto err_rel_reg; 184682e97acSKylene Jo Hall } 185682e97acSKylene Jo Hall 186afb5abc2SJarkko Sakkinen chip = tpmm_chip_alloc(&pdev->dev, &tpm_atmel); 187afb5abc2SJarkko Sakkinen if (IS_ERR(chip)) { 188afb5abc2SJarkko Sakkinen rc = PTR_ERR(chip); 189ad5ea3ccSKylene Jo Hall goto err_unreg_dev; 190e0dd03caSKylene Jo Hall } 191e0dd03caSKylene Jo Hall 192e0dd03caSKylene Jo Hall chip->vendor.iobase = iobase; 193e0dd03caSKylene Jo Hall chip->vendor.base = base; 194e0dd03caSKylene Jo Hall chip->vendor.have_region = have_region; 195e0dd03caSKylene Jo Hall chip->vendor.region_size = region_size; 196e0dd03caSKylene Jo Hall 197afb5abc2SJarkko Sakkinen rc = tpm_chip_register(chip); 198afb5abc2SJarkko Sakkinen if (rc) 199afb5abc2SJarkko Sakkinen goto err_unreg_dev; 200afb5abc2SJarkko Sakkinen 201682e97acSKylene Jo Hall return 0; 202ad5ea3ccSKylene Jo Hall 203ad5ea3ccSKylene Jo Hall err_unreg_dev: 204ad5ea3ccSKylene Jo Hall platform_device_unregister(pdev); 205ad5ea3ccSKylene Jo Hall err_rel_reg: 206e0dd03caSKylene Jo Hall atmel_put_base_addr(iobase); 207e0dd03caSKylene Jo Hall if (have_region) 208e0dd03caSKylene Jo Hall atmel_release_region(base, 209e0dd03caSKylene Jo Hall region_size); 210ad5ea3ccSKylene Jo Hall err_unreg_drv: 2117a192ec3SMing Lei platform_driver_unregister(&atml_drv); 212ad5ea3ccSKylene Jo Hall return rc; 2131da177e4SLinus Torvalds } 2141da177e4SLinus Torvalds 2151da177e4SLinus Torvalds static void __exit cleanup_atmel(void) 2161da177e4SLinus Torvalds { 2177a192ec3SMing Lei platform_driver_unregister(&atml_drv); 218ad5ea3ccSKylene Jo Hall atml_plat_remove(); 2191da177e4SLinus Torvalds } 2201da177e4SLinus Torvalds 2211da177e4SLinus Torvalds module_init(init_atmel); 2221da177e4SLinus Torvalds module_exit(cleanup_atmel); 2231da177e4SLinus Torvalds 2241da177e4SLinus Torvalds MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)"); 2251da177e4SLinus Torvalds MODULE_DESCRIPTION("TPM Driver"); 2261da177e4SLinus Torvalds MODULE_VERSION("2.0"); 2271da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 228