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 * 101da177e4SLinus Torvalds * 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++) { 5090612b30SKylene Jo Hall status = ioread8(chip->vendor->iobase + 1); 511da177e4SLinus Torvalds if ((status & ATML_STATUS_DATA_AVAIL) == 0) { 5290612b30SKylene Jo Hall dev_err(chip->dev, "error reading header\n"); 531da177e4SLinus Torvalds return -EIO; 541da177e4SLinus Torvalds } 5590612b30SKylene 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) { 63e659a3feSKylene Jo Hall 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 */ 6690612b30SKylene Jo Hall status = ioread8(chip->vendor->iobase + 1); 671da177e4SLinus Torvalds if ((status & ATML_STATUS_DATA_AVAIL) == 0) { 6890612b30SKylene Jo Hall 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++) { 7790612b30SKylene Jo Hall status = ioread8(chip->vendor->iobase + 1); 781da177e4SLinus Torvalds if ((status & ATML_STATUS_DATA_AVAIL) == 0) { 7990612b30SKylene Jo Hall dev_err(chip->dev, "error reading data\n"); 801da177e4SLinus Torvalds return -EIO; 811da177e4SLinus Torvalds } 8290612b30SKylene Jo Hall *buf++ = ioread8(chip->vendor->iobase); 831da177e4SLinus Torvalds } 841da177e4SLinus Torvalds 851da177e4SLinus Torvalds /* make sure data available is gone */ 8690612b30SKylene Jo Hall status = ioread8(chip->vendor->iobase + 1); 8790612b30SKylene Jo Hall 881da177e4SLinus Torvalds if (status & ATML_STATUS_DATA_AVAIL) { 89e659a3feSKylene Jo Hall 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 100e659a3feSKylene Jo Hall dev_dbg(chip->dev, "tpm_atml_send:\n"); 1011da177e4SLinus Torvalds for (i = 0; i < count; i++) { 102e659a3feSKylene Jo Hall dev_dbg(chip->dev, "%d 0x%x(%d)\n", i, buf[i], buf[i]); 10390612b30SKylene 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 { 11190612b30SKylene 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 { 11690612b30SKylene Jo Hall return ioread8(chip->vendor->iobase + 1); 117b4ed3e3cSKylene Jo Hall } 118b4ed3e3cSKylene Jo Hall 1191da177e4SLinus Torvalds static struct file_operations atmel_ops = { 1201da177e4SLinus Torvalds .owner = THIS_MODULE, 1211da177e4SLinus Torvalds .llseek = no_llseek, 1221da177e4SLinus Torvalds .open = tpm_open, 1231da177e4SLinus Torvalds .read = tpm_read, 1241da177e4SLinus Torvalds .write = tpm_write, 1251da177e4SLinus Torvalds .release = tpm_release, 1261da177e4SLinus Torvalds }; 1271da177e4SLinus Torvalds 1286659ca2aSKylene Hall static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL); 1296659ca2aSKylene Hall static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL); 1306659ca2aSKylene Hall static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL); 1316659ca2aSKylene Hall static DEVICE_ATTR(cancel, S_IWUSR |S_IWGRP, NULL, tpm_store_cancel); 1326659ca2aSKylene Hall 1336659ca2aSKylene Hall static struct attribute* atmel_attrs[] = { 1346659ca2aSKylene Hall &dev_attr_pubek.attr, 1356659ca2aSKylene Hall &dev_attr_pcrs.attr, 1366659ca2aSKylene Hall &dev_attr_caps.attr, 1376659ca2aSKylene Hall &dev_attr_cancel.attr, 138874ec33fSRandy Dunlap NULL, 1396659ca2aSKylene Hall }; 1406659ca2aSKylene Hall 1416659ca2aSKylene Hall static struct attribute_group atmel_attr_grp = { .attrs = atmel_attrs }; 1426659ca2aSKylene Hall 1431da177e4SLinus Torvalds static struct tpm_vendor_specific tpm_atmel = { 1441da177e4SLinus Torvalds .recv = tpm_atml_recv, 1451da177e4SLinus Torvalds .send = tpm_atml_send, 1461da177e4SLinus Torvalds .cancel = tpm_atml_cancel, 147b4ed3e3cSKylene Jo Hall .status = tpm_atml_status, 1481da177e4SLinus Torvalds .req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL, 1491da177e4SLinus Torvalds .req_complete_val = ATML_STATUS_DATA_AVAIL, 150d9e5b6bfSKylene Hall .req_canceled = ATML_STATUS_READY, 1516659ca2aSKylene Hall .attr_group = &atmel_attr_grp, 1521da177e4SLinus Torvalds .miscdev = { .fops = &atmel_ops, }, 1531da177e4SLinus Torvalds }; 1541da177e4SLinus Torvalds 155b888c87bSAndrew Morton static struct platform_device *pdev; 156682e97acSKylene Jo Hall 157ad5ea3ccSKylene Jo Hall static void atml_plat_remove(void) 1581da177e4SLinus Torvalds { 159ad5ea3ccSKylene Jo Hall struct tpm_chip *chip = dev_get_drvdata(&pdev->dev); 160ad5ea3ccSKylene Jo Hall 161682e97acSKylene Jo Hall if (chip) { 162ad5ea3ccSKylene Jo Hall if (chip->vendor->have_region) 16390612b30SKylene Jo Hall atmel_release_region(chip->vendor->base, 16490612b30SKylene Jo Hall chip->vendor->region_size); 165ad5ea3ccSKylene Jo Hall atmel_put_base_addr(chip->vendor); 166e659a3feSKylene Jo Hall tpm_remove_hardware(chip->dev); 167ad5ea3ccSKylene Jo Hall platform_device_unregister(pdev); 168e659a3feSKylene Jo Hall } 169682e97acSKylene Jo Hall } 170e659a3feSKylene Jo Hall 171682e97acSKylene Jo Hall static struct device_driver atml_drv = { 1721da177e4SLinus Torvalds .name = "tpm_atmel", 173682e97acSKylene Jo Hall .bus = &platform_bus_type, 174682e97acSKylene Jo Hall .owner = THIS_MODULE, 1751da177e4SLinus Torvalds .suspend = tpm_pm_suspend, 1761da177e4SLinus Torvalds .resume = tpm_pm_resume, 1771da177e4SLinus Torvalds }; 1781da177e4SLinus Torvalds 1791da177e4SLinus Torvalds static int __init init_atmel(void) 1801da177e4SLinus Torvalds { 181682e97acSKylene Jo Hall int rc = 0; 182682e97acSKylene Jo Hall 183682e97acSKylene Jo Hall driver_register(&atml_drv); 184682e97acSKylene Jo Hall 18590612b30SKylene Jo Hall if ((tpm_atmel.iobase = atmel_get_base_addr(&tpm_atmel)) == NULL) { 186ad5ea3ccSKylene Jo Hall rc = -ENODEV; 187ad5ea3ccSKylene Jo Hall goto err_unreg_drv; 188682e97acSKylene Jo Hall } 189682e97acSKylene Jo Hall 19090612b30SKylene Jo Hall tpm_atmel.have_region = 19190612b30SKylene Jo Hall (atmel_request_region 19290612b30SKylene Jo Hall (tpm_atmel.base, tpm_atmel.region_size, 19390612b30SKylene Jo Hall "tpm_atmel0") == NULL) ? 0 : 1; 194682e97acSKylene Jo Hall 19590612b30SKylene Jo Hall if (IS_ERR 19690612b30SKylene Jo Hall (pdev = 19790612b30SKylene Jo Hall platform_device_register_simple("tpm_atmel", -1, NULL, 0))) { 198ad5ea3ccSKylene Jo Hall rc = PTR_ERR(pdev); 199ad5ea3ccSKylene Jo Hall goto err_rel_reg; 200682e97acSKylene Jo Hall } 201682e97acSKylene Jo Hall 202ad5ea3ccSKylene Jo Hall if ((rc = tpm_register_hardware(&pdev->dev, &tpm_atmel)) < 0) 203ad5ea3ccSKylene Jo Hall goto err_unreg_dev; 204682e97acSKylene Jo Hall return 0; 205ad5ea3ccSKylene Jo Hall 206ad5ea3ccSKylene Jo Hall err_unreg_dev: 207ad5ea3ccSKylene Jo Hall platform_device_unregister(pdev); 208ad5ea3ccSKylene Jo Hall err_rel_reg: 209ad5ea3ccSKylene Jo Hall atmel_put_base_addr(&tpm_atmel); 21090612b30SKylene Jo Hall if (tpm_atmel.have_region) 21190612b30SKylene Jo Hall atmel_release_region(tpm_atmel.base, 21290612b30SKylene Jo Hall tpm_atmel.region_size); 213ad5ea3ccSKylene Jo Hall err_unreg_drv: 214ad5ea3ccSKylene Jo Hall driver_unregister(&atml_drv); 215ad5ea3ccSKylene Jo Hall return rc; 2161da177e4SLinus Torvalds } 2171da177e4SLinus Torvalds 2181da177e4SLinus Torvalds static void __exit cleanup_atmel(void) 2191da177e4SLinus Torvalds { 220682e97acSKylene Jo Hall driver_unregister(&atml_drv); 221ad5ea3ccSKylene Jo Hall atml_plat_remove(); 2221da177e4SLinus Torvalds } 2231da177e4SLinus Torvalds 2241da177e4SLinus Torvalds module_init(init_atmel); 2251da177e4SLinus Torvalds module_exit(cleanup_atmel); 2261da177e4SLinus Torvalds 2271da177e4SLinus Torvalds MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)"); 2281da177e4SLinus Torvalds MODULE_DESCRIPTION("TPM Driver"); 2291da177e4SLinus Torvalds MODULE_VERSION("2.0"); 2301da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 231