xref: /openbmc/linux/drivers/char/tpm/tpm_atmel.c (revision b886d83c)
1b886d83cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * Copyright (C) 2004 IBM Corporation
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Authors:
61da177e4SLinus Torvalds  * Leendert van Doorn <leendert@watson.ibm.com>
71da177e4SLinus Torvalds  * Dave Safford <safford@watson.ibm.com>
81da177e4SLinus Torvalds  * Reiner Sailer <sailer@watson.ibm.com>
91da177e4SLinus Torvalds  * Kylene Hall <kjhall@us.ibm.com>
101da177e4SLinus Torvalds  *
118e81cc13SKent Yoder  * Maintained by: <tpmdd-devel@lists.sourceforge.net>
121da177e4SLinus Torvalds  *
131da177e4SLinus Torvalds  * Device driver for TCG/TCPA TPM (trusted platform module).
141da177e4SLinus Torvalds  * Specifications at www.trustedcomputinggroup.org
151da177e4SLinus Torvalds  */
161da177e4SLinus Torvalds 
171da177e4SLinus Torvalds #include "tpm.h"
18ad5ea3ccSKylene Jo Hall #include "tpm_atmel.h"
191da177e4SLinus Torvalds 
201da177e4SLinus Torvalds /* write status bits */
213122a88aSKylene Hall enum tpm_atmel_write_status {
223122a88aSKylene Hall 	ATML_STATUS_ABORT = 0x01,
233122a88aSKylene Hall 	ATML_STATUS_LASTBYTE = 0x04
243122a88aSKylene Hall };
251da177e4SLinus Torvalds /* read status bits */
263122a88aSKylene Hall enum tpm_atmel_read_status {
273122a88aSKylene Hall 	ATML_STATUS_BUSY = 0x01,
283122a88aSKylene Hall 	ATML_STATUS_DATA_AVAIL = 0x02,
293122a88aSKylene Hall 	ATML_STATUS_REWRITE = 0x04,
303122a88aSKylene Hall 	ATML_STATUS_READY = 0x08
313122a88aSKylene Hall };
321da177e4SLinus Torvalds 
tpm_atml_recv(struct tpm_chip * chip,u8 * buf,size_t count)331da177e4SLinus Torvalds static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
341da177e4SLinus Torvalds {
359e0d39d8SChristophe Ricard 	struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
361da177e4SLinus Torvalds 	u8 status, *hdr = buf;
371da177e4SLinus Torvalds 	u32 size;
381da177e4SLinus Torvalds 	int i;
391da177e4SLinus Torvalds 	__be32 *native_size;
401da177e4SLinus Torvalds 
411da177e4SLinus Torvalds 	/* start reading header */
421da177e4SLinus Torvalds 	if (count < 6)
431da177e4SLinus Torvalds 		return -EIO;
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds 	for (i = 0; i < 6; i++) {
464eea703cSChristophe Ricard 		status = ioread8(priv->iobase + 1);
471da177e4SLinus Torvalds 		if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
488cfffc9dSJason Gunthorpe 			dev_err(&chip->dev, "error reading header\n");
491da177e4SLinus Torvalds 			return -EIO;
501da177e4SLinus Torvalds 		}
514eea703cSChristophe Ricard 		*buf++ = ioread8(priv->iobase);
521da177e4SLinus Torvalds 	}
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds 	/* size of the data received */
551da177e4SLinus Torvalds 	native_size = (__force __be32 *) (hdr + 2);
561da177e4SLinus Torvalds 	size = be32_to_cpu(*native_size);
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds 	if (count < size) {
598cfffc9dSJason Gunthorpe 		dev_err(&chip->dev,
601da177e4SLinus Torvalds 			"Recv size(%d) less than available space\n", size);
611da177e4SLinus Torvalds 		for (; i < size; i++) {	/* clear the waiting data anyway */
624eea703cSChristophe Ricard 			status = ioread8(priv->iobase + 1);
631da177e4SLinus Torvalds 			if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
648cfffc9dSJason Gunthorpe 				dev_err(&chip->dev, "error reading data\n");
651da177e4SLinus Torvalds 				return -EIO;
661da177e4SLinus Torvalds 			}
671da177e4SLinus Torvalds 		}
681da177e4SLinus Torvalds 		return -EIO;
691da177e4SLinus Torvalds 	}
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds 	/* read all the data available */
721da177e4SLinus Torvalds 	for (; i < size; i++) {
734eea703cSChristophe Ricard 		status = ioread8(priv->iobase + 1);
741da177e4SLinus Torvalds 		if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
758cfffc9dSJason Gunthorpe 			dev_err(&chip->dev, "error reading data\n");
761da177e4SLinus Torvalds 			return -EIO;
771da177e4SLinus Torvalds 		}
784eea703cSChristophe Ricard 		*buf++ = ioread8(priv->iobase);
791da177e4SLinus Torvalds 	}
801da177e4SLinus Torvalds 
811da177e4SLinus Torvalds 	/* make sure data available is gone */
824eea703cSChristophe Ricard 	status = ioread8(priv->iobase + 1);
8390612b30SKylene Jo Hall 
841da177e4SLinus Torvalds 	if (status & ATML_STATUS_DATA_AVAIL) {
858cfffc9dSJason Gunthorpe 		dev_err(&chip->dev, "data available is stuck\n");
861da177e4SLinus Torvalds 		return -EIO;
871da177e4SLinus Torvalds 	}
881da177e4SLinus Torvalds 
891da177e4SLinus Torvalds 	return size;
901da177e4SLinus Torvalds }
911da177e4SLinus Torvalds 
tpm_atml_send(struct tpm_chip * chip,u8 * buf,size_t count)921da177e4SLinus Torvalds static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count)
931da177e4SLinus Torvalds {
949e0d39d8SChristophe Ricard 	struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
951da177e4SLinus Torvalds 	int i;
961da177e4SLinus Torvalds 
978cfffc9dSJason Gunthorpe 	dev_dbg(&chip->dev, "tpm_atml_send:\n");
981da177e4SLinus Torvalds 	for (i = 0; i < count; i++) {
998cfffc9dSJason Gunthorpe 		dev_dbg(&chip->dev, "%d 0x%x(%d)\n",  i, buf[i], buf[i]);
1004eea703cSChristophe Ricard 		iowrite8(buf[i], priv->iobase);
1011da177e4SLinus Torvalds 	}
1021da177e4SLinus Torvalds 
103f5595f5bSJarkko Sakkinen 	return 0;
1041da177e4SLinus Torvalds }
1051da177e4SLinus Torvalds 
tpm_atml_cancel(struct tpm_chip * chip)1061da177e4SLinus Torvalds static void tpm_atml_cancel(struct tpm_chip *chip)
1071da177e4SLinus Torvalds {
1089e0d39d8SChristophe Ricard 	struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
1094eea703cSChristophe Ricard 
1104eea703cSChristophe Ricard 	iowrite8(ATML_STATUS_ABORT, priv->iobase + 1);
1111da177e4SLinus Torvalds }
1121da177e4SLinus Torvalds 
tpm_atml_status(struct tpm_chip * chip)113b4ed3e3cSKylene Jo Hall static u8 tpm_atml_status(struct tpm_chip *chip)
114b4ed3e3cSKylene Jo Hall {
1159e0d39d8SChristophe Ricard 	struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
1164eea703cSChristophe Ricard 
1174eea703cSChristophe Ricard 	return ioread8(priv->iobase + 1);
118b4ed3e3cSKylene Jo Hall }
119b4ed3e3cSKylene Jo Hall 
tpm_atml_req_canceled(struct tpm_chip * chip,u8 status)1201f866057SStefan Berger static bool tpm_atml_req_canceled(struct tpm_chip *chip, u8 status)
1211f866057SStefan Berger {
1221f866057SStefan Berger 	return (status == ATML_STATUS_READY);
1231f866057SStefan Berger }
1241f866057SStefan Berger 
12501ad1fa7SJason Gunthorpe static const struct tpm_class_ops tpm_atmel = {
1261da177e4SLinus Torvalds 	.recv = tpm_atml_recv,
1271da177e4SLinus Torvalds 	.send = tpm_atml_send,
1281da177e4SLinus Torvalds 	.cancel = tpm_atml_cancel,
129b4ed3e3cSKylene Jo Hall 	.status = tpm_atml_status,
1301da177e4SLinus Torvalds 	.req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL,
1311da177e4SLinus Torvalds 	.req_complete_val = ATML_STATUS_DATA_AVAIL,
1321f866057SStefan Berger 	.req_canceled = tpm_atml_req_canceled,
1331da177e4SLinus Torvalds };
1341da177e4SLinus Torvalds 
135b888c87bSAndrew Morton static struct platform_device *pdev;
136682e97acSKylene Jo Hall 
atml_plat_remove(void)137ad5ea3ccSKylene Jo Hall static void atml_plat_remove(void)
1381da177e4SLinus Torvalds {
139ad5ea3ccSKylene Jo Hall 	struct tpm_chip *chip = dev_get_drvdata(&pdev->dev);
1409e0d39d8SChristophe Ricard 	struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
141ad5ea3ccSKylene Jo Hall 
142afb5abc2SJarkko Sakkinen 	tpm_chip_unregister(chip);
14323d06ff7SJarkko Sakkinen 	if (priv->have_region)
144ee177984SJarkko Sakkinen 		atmel_release_region(priv->base, priv->region_size);
1454eea703cSChristophe Ricard 	atmel_put_base_addr(priv->iobase);
146ad5ea3ccSKylene Jo Hall 	platform_device_unregister(pdev);
147e659a3feSKylene Jo Hall }
148e659a3feSKylene Jo Hall 
1498324be05SRafael J. Wysocki static SIMPLE_DEV_PM_OPS(tpm_atml_pm, tpm_pm_suspend, tpm_pm_resume);
1507a192ec3SMing Lei 
1517a192ec3SMing Lei static struct platform_driver atml_drv = {
1527a192ec3SMing Lei 	.driver = {
1531da177e4SLinus Torvalds 		.name = "tpm_atmel",
1548324be05SRafael J. Wysocki 		.pm		= &tpm_atml_pm,
1557a192ec3SMing Lei 	},
1561da177e4SLinus Torvalds };
1571da177e4SLinus Torvalds 
init_atmel(void)1581da177e4SLinus Torvalds static int __init init_atmel(void)
1591da177e4SLinus Torvalds {
160682e97acSKylene Jo Hall 	int rc = 0;
161e0dd03caSKylene Jo Hall 	void __iomem *iobase = NULL;
162e0dd03caSKylene Jo Hall 	int have_region, region_size;
163e0dd03caSKylene Jo Hall 	unsigned long base;
164e0dd03caSKylene Jo Hall 	struct  tpm_chip *chip;
16523d06ff7SJarkko Sakkinen 	struct tpm_atmel_priv *priv;
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, &region_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 
18623d06ff7SJarkko Sakkinen 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
18723d06ff7SJarkko Sakkinen 	if (!priv) {
18823d06ff7SJarkko Sakkinen 		rc = -ENOMEM;
18923d06ff7SJarkko Sakkinen 		goto err_unreg_dev;
19023d06ff7SJarkko Sakkinen 	}
19123d06ff7SJarkko Sakkinen 
1924eea703cSChristophe Ricard 	priv->iobase = iobase;
193ee177984SJarkko Sakkinen 	priv->base = base;
19423d06ff7SJarkko Sakkinen 	priv->have_region = have_region;
19523d06ff7SJarkko Sakkinen 	priv->region_size = region_size;
19623d06ff7SJarkko Sakkinen 
197afb5abc2SJarkko Sakkinen 	chip = tpmm_chip_alloc(&pdev->dev, &tpm_atmel);
198afb5abc2SJarkko Sakkinen 	if (IS_ERR(chip)) {
199afb5abc2SJarkko Sakkinen 		rc = PTR_ERR(chip);
200ad5ea3ccSKylene Jo Hall 		goto err_unreg_dev;
201e0dd03caSKylene Jo Hall 	}
202e0dd03caSKylene Jo Hall 
2039e0d39d8SChristophe Ricard 	dev_set_drvdata(&chip->dev, priv);
204e0dd03caSKylene Jo Hall 
205afb5abc2SJarkko Sakkinen 	rc = tpm_chip_register(chip);
206afb5abc2SJarkko Sakkinen 	if (rc)
207afb5abc2SJarkko Sakkinen 		goto err_unreg_dev;
208afb5abc2SJarkko Sakkinen 
209682e97acSKylene Jo Hall 	return 0;
210ad5ea3ccSKylene Jo Hall 
211ad5ea3ccSKylene Jo Hall err_unreg_dev:
212ad5ea3ccSKylene Jo Hall 	platform_device_unregister(pdev);
213ad5ea3ccSKylene Jo Hall err_rel_reg:
214e0dd03caSKylene Jo Hall 	atmel_put_base_addr(iobase);
215e0dd03caSKylene Jo Hall 	if (have_region)
216e0dd03caSKylene Jo Hall 		atmel_release_region(base,
217e0dd03caSKylene Jo Hall 				     region_size);
218ad5ea3ccSKylene Jo Hall err_unreg_drv:
2197a192ec3SMing Lei 	platform_driver_unregister(&atml_drv);
220ad5ea3ccSKylene Jo Hall 	return rc;
2211da177e4SLinus Torvalds }
2221da177e4SLinus Torvalds 
cleanup_atmel(void)2231da177e4SLinus Torvalds static void __exit cleanup_atmel(void)
2241da177e4SLinus Torvalds {
2257a192ec3SMing Lei 	platform_driver_unregister(&atml_drv);
226ad5ea3ccSKylene Jo Hall 	atml_plat_remove();
2271da177e4SLinus Torvalds }
2281da177e4SLinus Torvalds 
2291da177e4SLinus Torvalds module_init(init_atmel);
2301da177e4SLinus Torvalds module_exit(cleanup_atmel);
2311da177e4SLinus Torvalds 
2321da177e4SLinus Torvalds MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
2331da177e4SLinus Torvalds MODULE_DESCRIPTION("TPM Driver");
2341da177e4SLinus Torvalds MODULE_VERSION("2.0");
2351da177e4SLinus Torvalds MODULE_LICENSE("GPL");
236