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, ®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
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