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" 231da177e4SLinus Torvalds 241da177e4SLinus Torvalds /* Atmel definitions */ 253122a88aSKylene Hall enum tpm_atmel_addr { 26e1a23c66SKylene Hall TPM_ATMEL_BASE_ADDR_LO = 0x08, 27e1a23c66SKylene Hall TPM_ATMEL_BASE_ADDR_HI = 0x09 283122a88aSKylene Hall }; 291da177e4SLinus Torvalds 301da177e4SLinus Torvalds /* write status bits */ 313122a88aSKylene Hall enum tpm_atmel_write_status { 323122a88aSKylene Hall ATML_STATUS_ABORT = 0x01, 333122a88aSKylene Hall ATML_STATUS_LASTBYTE = 0x04 343122a88aSKylene Hall }; 351da177e4SLinus Torvalds /* read status bits */ 363122a88aSKylene Hall enum tpm_atmel_read_status { 373122a88aSKylene Hall ATML_STATUS_BUSY = 0x01, 383122a88aSKylene Hall ATML_STATUS_DATA_AVAIL = 0x02, 393122a88aSKylene Hall ATML_STATUS_REWRITE = 0x04, 403122a88aSKylene Hall ATML_STATUS_READY = 0x08 413122a88aSKylene Hall }; 421da177e4SLinus Torvalds 431da177e4SLinus Torvalds static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count) 441da177e4SLinus Torvalds { 451da177e4SLinus Torvalds u8 status, *hdr = buf; 461da177e4SLinus Torvalds u32 size; 471da177e4SLinus Torvalds int i; 481da177e4SLinus Torvalds __be32 *native_size; 491da177e4SLinus Torvalds 501da177e4SLinus Torvalds /* start reading header */ 511da177e4SLinus Torvalds if (count < 6) 521da177e4SLinus Torvalds return -EIO; 531da177e4SLinus Torvalds 541da177e4SLinus Torvalds for (i = 0; i < 6; i++) { 551da177e4SLinus Torvalds status = inb(chip->vendor->base + 1); 561da177e4SLinus Torvalds if ((status & ATML_STATUS_DATA_AVAIL) == 0) { 57e659a3feSKylene Jo Hall dev_err(chip->dev, 581da177e4SLinus Torvalds "error reading header\n"); 591da177e4SLinus Torvalds return -EIO; 601da177e4SLinus Torvalds } 611da177e4SLinus Torvalds *buf++ = inb(chip->vendor->base); 621da177e4SLinus Torvalds } 631da177e4SLinus Torvalds 641da177e4SLinus Torvalds /* size of the data received */ 651da177e4SLinus Torvalds native_size = (__force __be32 *) (hdr + 2); 661da177e4SLinus Torvalds size = be32_to_cpu(*native_size); 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds if (count < size) { 69e659a3feSKylene Jo Hall dev_err(chip->dev, 701da177e4SLinus Torvalds "Recv size(%d) less than available space\n", size); 711da177e4SLinus Torvalds for (; i < size; i++) { /* clear the waiting data anyway */ 721da177e4SLinus Torvalds status = inb(chip->vendor->base + 1); 731da177e4SLinus Torvalds if ((status & ATML_STATUS_DATA_AVAIL) == 0) { 74e659a3feSKylene Jo Hall dev_err(chip->dev, 751da177e4SLinus Torvalds "error reading data\n"); 761da177e4SLinus Torvalds return -EIO; 771da177e4SLinus Torvalds } 781da177e4SLinus Torvalds } 791da177e4SLinus Torvalds return -EIO; 801da177e4SLinus Torvalds } 811da177e4SLinus Torvalds 821da177e4SLinus Torvalds /* read all the data available */ 831da177e4SLinus Torvalds for (; i < size; i++) { 841da177e4SLinus Torvalds status = inb(chip->vendor->base + 1); 851da177e4SLinus Torvalds if ((status & ATML_STATUS_DATA_AVAIL) == 0) { 86e659a3feSKylene Jo Hall dev_err(chip->dev, 871da177e4SLinus Torvalds "error reading data\n"); 881da177e4SLinus Torvalds return -EIO; 891da177e4SLinus Torvalds } 901da177e4SLinus Torvalds *buf++ = inb(chip->vendor->base); 911da177e4SLinus Torvalds } 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds /* make sure data available is gone */ 941da177e4SLinus Torvalds status = inb(chip->vendor->base + 1); 951da177e4SLinus Torvalds if (status & ATML_STATUS_DATA_AVAIL) { 96e659a3feSKylene Jo Hall dev_err(chip->dev, "data available is stuck\n"); 971da177e4SLinus Torvalds return -EIO; 981da177e4SLinus Torvalds } 991da177e4SLinus Torvalds 1001da177e4SLinus Torvalds return size; 1011da177e4SLinus Torvalds } 1021da177e4SLinus Torvalds 1031da177e4SLinus Torvalds static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count) 1041da177e4SLinus Torvalds { 1051da177e4SLinus Torvalds int i; 1061da177e4SLinus Torvalds 107e659a3feSKylene Jo Hall dev_dbg(chip->dev, "tpm_atml_send:\n"); 1081da177e4SLinus Torvalds for (i = 0; i < count; i++) { 109e659a3feSKylene Jo Hall dev_dbg(chip->dev, "%d 0x%x(%d)\n", i, buf[i], buf[i]); 1101da177e4SLinus Torvalds outb(buf[i], chip->vendor->base); 1111da177e4SLinus Torvalds } 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds return count; 1141da177e4SLinus Torvalds } 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds static void tpm_atml_cancel(struct tpm_chip *chip) 1171da177e4SLinus Torvalds { 1181da177e4SLinus Torvalds outb(ATML_STATUS_ABORT, chip->vendor->base + 1); 1191da177e4SLinus Torvalds } 1201da177e4SLinus Torvalds 121b4ed3e3cSKylene Jo Hall static u8 tpm_atml_status(struct tpm_chip *chip) 122b4ed3e3cSKylene Jo Hall { 123b4ed3e3cSKylene Jo Hall return inb(chip->vendor->base + 1); 124b4ed3e3cSKylene Jo Hall } 125b4ed3e3cSKylene Jo Hall 1261da177e4SLinus Torvalds static struct file_operations atmel_ops = { 1271da177e4SLinus Torvalds .owner = THIS_MODULE, 1281da177e4SLinus Torvalds .llseek = no_llseek, 1291da177e4SLinus Torvalds .open = tpm_open, 1301da177e4SLinus Torvalds .read = tpm_read, 1311da177e4SLinus Torvalds .write = tpm_write, 1321da177e4SLinus Torvalds .release = tpm_release, 1331da177e4SLinus Torvalds }; 1341da177e4SLinus Torvalds 1356659ca2aSKylene Hall static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL); 1366659ca2aSKylene Hall static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL); 1376659ca2aSKylene Hall static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL); 1386659ca2aSKylene Hall static DEVICE_ATTR(cancel, S_IWUSR |S_IWGRP, NULL, tpm_store_cancel); 1396659ca2aSKylene Hall 1406659ca2aSKylene Hall static struct attribute* atmel_attrs[] = { 1416659ca2aSKylene Hall &dev_attr_pubek.attr, 1426659ca2aSKylene Hall &dev_attr_pcrs.attr, 1436659ca2aSKylene Hall &dev_attr_caps.attr, 1446659ca2aSKylene Hall &dev_attr_cancel.attr, 1456659ca2aSKylene Hall 0, 1466659ca2aSKylene Hall }; 1476659ca2aSKylene Hall 1486659ca2aSKylene Hall static struct attribute_group atmel_attr_grp = { .attrs = atmel_attrs }; 1496659ca2aSKylene Hall 1501da177e4SLinus Torvalds static struct tpm_vendor_specific tpm_atmel = { 1511da177e4SLinus Torvalds .recv = tpm_atml_recv, 1521da177e4SLinus Torvalds .send = tpm_atml_send, 1531da177e4SLinus Torvalds .cancel = tpm_atml_cancel, 154b4ed3e3cSKylene Jo Hall .status = tpm_atml_status, 1551da177e4SLinus Torvalds .req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL, 1561da177e4SLinus Torvalds .req_complete_val = ATML_STATUS_DATA_AVAIL, 157d9e5b6bfSKylene Hall .req_canceled = ATML_STATUS_READY, 1586659ca2aSKylene Hall .attr_group = &atmel_attr_grp, 1591da177e4SLinus Torvalds .miscdev = { .fops = &atmel_ops, }, 1601da177e4SLinus Torvalds }; 1611da177e4SLinus Torvalds 162b888c87bSAndrew Morton static struct platform_device *pdev; 163682e97acSKylene Jo Hall 164682e97acSKylene Jo Hall static void __devexit tpm_atml_remove(struct device *dev) 1651da177e4SLinus Torvalds { 166682e97acSKylene Jo Hall struct tpm_chip *chip = dev_get_drvdata(dev); 167682e97acSKylene Jo Hall if (chip) { 168682e97acSKylene Jo Hall release_region(chip->vendor->base, 2); 169e659a3feSKylene Jo Hall tpm_remove_hardware(chip->dev); 170e659a3feSKylene Jo Hall } 171682e97acSKylene Jo Hall } 172e659a3feSKylene Jo Hall 173682e97acSKylene Jo Hall static struct device_driver atml_drv = { 1741da177e4SLinus Torvalds .name = "tpm_atmel", 175682e97acSKylene Jo Hall .bus = &platform_bus_type, 176682e97acSKylene Jo Hall .owner = THIS_MODULE, 1771da177e4SLinus Torvalds .suspend = tpm_pm_suspend, 1781da177e4SLinus Torvalds .resume = tpm_pm_resume, 1791da177e4SLinus Torvalds }; 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds static int __init init_atmel(void) 1821da177e4SLinus Torvalds { 183682e97acSKylene Jo Hall int rc = 0; 184682e97acSKylene Jo Hall int lo, hi; 185682e97acSKylene Jo Hall 186682e97acSKylene Jo Hall driver_register(&atml_drv); 187682e97acSKylene Jo Hall 188682e97acSKylene Jo Hall lo = tpm_read_index(TPM_ADDR, TPM_ATMEL_BASE_ADDR_LO); 189682e97acSKylene Jo Hall hi = tpm_read_index(TPM_ADDR, TPM_ATMEL_BASE_ADDR_HI); 190682e97acSKylene Jo Hall 191682e97acSKylene Jo Hall tpm_atmel.base = (hi<<8)|lo; 192682e97acSKylene Jo Hall 193682e97acSKylene Jo Hall /* verify that it is an Atmel part */ 194682e97acSKylene Jo Hall if (tpm_read_index(TPM_ADDR, 4) != 'A' || tpm_read_index(TPM_ADDR, 5) != 'T' 195682e97acSKylene Jo Hall || tpm_read_index(TPM_ADDR, 6) != 'M' || tpm_read_index(TPM_ADDR, 7) != 'L') { 196682e97acSKylene Jo Hall return -ENODEV; 197682e97acSKylene Jo Hall } 198682e97acSKylene Jo Hall 199682e97acSKylene Jo Hall /* verify chip version number is 1.1 */ 200682e97acSKylene Jo Hall if ( (tpm_read_index(TPM_ADDR, 0x00) != 0x01) || 201682e97acSKylene Jo Hall (tpm_read_index(TPM_ADDR, 0x01) != 0x01 )) 202682e97acSKylene Jo Hall return -ENODEV; 203682e97acSKylene Jo Hall 204b888c87bSAndrew Morton pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL); 205682e97acSKylene Jo Hall if ( !pdev ) 206682e97acSKylene Jo Hall return -ENOMEM; 207682e97acSKylene Jo Hall 208682e97acSKylene Jo Hall pdev->name = "tpm_atmel0"; 209682e97acSKylene Jo Hall pdev->id = -1; 210682e97acSKylene Jo Hall pdev->num_resources = 0; 211682e97acSKylene Jo Hall pdev->dev.release = tpm_atml_remove; 212682e97acSKylene Jo Hall pdev->dev.driver = &atml_drv; 213682e97acSKylene Jo Hall 214682e97acSKylene Jo Hall if ((rc = platform_device_register(pdev)) < 0) { 215682e97acSKylene Jo Hall kfree(pdev); 216682e97acSKylene Jo Hall pdev = NULL; 217682e97acSKylene Jo Hall return rc; 218682e97acSKylene Jo Hall } 219682e97acSKylene Jo Hall 220682e97acSKylene Jo Hall if (request_region(tpm_atmel.base, 2, "tpm_atmel0") == NULL ) { 221682e97acSKylene Jo Hall platform_device_unregister(pdev); 222682e97acSKylene Jo Hall kfree(pdev); 223682e97acSKylene Jo Hall pdev = NULL; 224682e97acSKylene Jo Hall return -EBUSY; 225682e97acSKylene Jo Hall } 226682e97acSKylene Jo Hall 227682e97acSKylene Jo Hall if ((rc = tpm_register_hardware(&pdev->dev, &tpm_atmel)) < 0) { 228682e97acSKylene Jo Hall release_region(tpm_atmel.base, 2); 229682e97acSKylene Jo Hall platform_device_unregister(pdev); 230682e97acSKylene Jo Hall kfree(pdev); 231682e97acSKylene Jo Hall pdev = NULL; 232682e97acSKylene Jo Hall return rc; 233682e97acSKylene Jo Hall } 234682e97acSKylene Jo Hall 235b888c87bSAndrew Morton dev_info(&pdev->dev, "Atmel TPM 1.1, Base Address: 0x%x\n", 236b888c87bSAndrew Morton tpm_atmel.base); 237682e97acSKylene Jo Hall return 0; 2381da177e4SLinus Torvalds } 2391da177e4SLinus Torvalds 2401da177e4SLinus Torvalds static void __exit cleanup_atmel(void) 2411da177e4SLinus Torvalds { 242682e97acSKylene Jo Hall if (pdev) { 243682e97acSKylene Jo Hall tpm_atml_remove(&pdev->dev); 244682e97acSKylene Jo Hall platform_device_unregister(pdev); 245682e97acSKylene Jo Hall kfree(pdev); 246682e97acSKylene Jo Hall pdev = NULL; 247682e97acSKylene Jo Hall } 248682e97acSKylene Jo Hall 249682e97acSKylene Jo Hall driver_unregister(&atml_drv); 2501da177e4SLinus Torvalds } 2511da177e4SLinus Torvalds 2521da177e4SLinus Torvalds module_init(init_atmel); 2531da177e4SLinus Torvalds module_exit(cleanup_atmel); 2541da177e4SLinus Torvalds 2551da177e4SLinus Torvalds MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)"); 2561da177e4SLinus Torvalds MODULE_DESCRIPTION("TPM Driver"); 2571da177e4SLinus Torvalds MODULE_VERSION("2.0"); 2581da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 259