xref: /openbmc/linux/drivers/char/tpm/tpm_atmel.c (revision b888c87b)
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