tpm_atmel.c (5364B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) 2004 IBM Corporation 4 * 5 * Authors: 6 * Leendert van Doorn <leendert@watson.ibm.com> 7 * Dave Safford <safford@watson.ibm.com> 8 * Reiner Sailer <sailer@watson.ibm.com> 9 * Kylene Hall <kjhall@us.ibm.com> 10 * 11 * Maintained by: <tpmdd-devel@lists.sourceforge.net> 12 * 13 * Device driver for TCG/TCPA TPM (trusted platform module). 14 * Specifications at www.trustedcomputinggroup.org 15 */ 16 17#include "tpm.h" 18#include "tpm_atmel.h" 19 20/* write status bits */ 21enum tpm_atmel_write_status { 22 ATML_STATUS_ABORT = 0x01, 23 ATML_STATUS_LASTBYTE = 0x04 24}; 25/* read status bits */ 26enum tpm_atmel_read_status { 27 ATML_STATUS_BUSY = 0x01, 28 ATML_STATUS_DATA_AVAIL = 0x02, 29 ATML_STATUS_REWRITE = 0x04, 30 ATML_STATUS_READY = 0x08 31}; 32 33static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count) 34{ 35 struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev); 36 u8 status, *hdr = buf; 37 u32 size; 38 int i; 39 __be32 *native_size; 40 41 /* start reading header */ 42 if (count < 6) 43 return -EIO; 44 45 for (i = 0; i < 6; i++) { 46 status = ioread8(priv->iobase + 1); 47 if ((status & ATML_STATUS_DATA_AVAIL) == 0) { 48 dev_err(&chip->dev, "error reading header\n"); 49 return -EIO; 50 } 51 *buf++ = ioread8(priv->iobase); 52 } 53 54 /* size of the data received */ 55 native_size = (__force __be32 *) (hdr + 2); 56 size = be32_to_cpu(*native_size); 57 58 if (count < size) { 59 dev_err(&chip->dev, 60 "Recv size(%d) less than available space\n", size); 61 for (; i < size; i++) { /* clear the waiting data anyway */ 62 status = ioread8(priv->iobase + 1); 63 if ((status & ATML_STATUS_DATA_AVAIL) == 0) { 64 dev_err(&chip->dev, "error reading data\n"); 65 return -EIO; 66 } 67 } 68 return -EIO; 69 } 70 71 /* read all the data available */ 72 for (; i < size; i++) { 73 status = ioread8(priv->iobase + 1); 74 if ((status & ATML_STATUS_DATA_AVAIL) == 0) { 75 dev_err(&chip->dev, "error reading data\n"); 76 return -EIO; 77 } 78 *buf++ = ioread8(priv->iobase); 79 } 80 81 /* make sure data available is gone */ 82 status = ioread8(priv->iobase + 1); 83 84 if (status & ATML_STATUS_DATA_AVAIL) { 85 dev_err(&chip->dev, "data available is stuck\n"); 86 return -EIO; 87 } 88 89 return size; 90} 91 92static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count) 93{ 94 struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev); 95 int i; 96 97 dev_dbg(&chip->dev, "tpm_atml_send:\n"); 98 for (i = 0; i < count; i++) { 99 dev_dbg(&chip->dev, "%d 0x%x(%d)\n", i, buf[i], buf[i]); 100 iowrite8(buf[i], priv->iobase); 101 } 102 103 return 0; 104} 105 106static void tpm_atml_cancel(struct tpm_chip *chip) 107{ 108 struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev); 109 110 iowrite8(ATML_STATUS_ABORT, priv->iobase + 1); 111} 112 113static u8 tpm_atml_status(struct tpm_chip *chip) 114{ 115 struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev); 116 117 return ioread8(priv->iobase + 1); 118} 119 120static bool tpm_atml_req_canceled(struct tpm_chip *chip, u8 status) 121{ 122 return (status == ATML_STATUS_READY); 123} 124 125static const struct tpm_class_ops tpm_atmel = { 126 .recv = tpm_atml_recv, 127 .send = tpm_atml_send, 128 .cancel = tpm_atml_cancel, 129 .status = tpm_atml_status, 130 .req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL, 131 .req_complete_val = ATML_STATUS_DATA_AVAIL, 132 .req_canceled = tpm_atml_req_canceled, 133}; 134 135static struct platform_device *pdev; 136 137static void atml_plat_remove(void) 138{ 139 struct tpm_chip *chip = dev_get_drvdata(&pdev->dev); 140 struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev); 141 142 tpm_chip_unregister(chip); 143 if (priv->have_region) 144 atmel_release_region(priv->base, priv->region_size); 145 atmel_put_base_addr(priv->iobase); 146 platform_device_unregister(pdev); 147} 148 149static SIMPLE_DEV_PM_OPS(tpm_atml_pm, tpm_pm_suspend, tpm_pm_resume); 150 151static struct platform_driver atml_drv = { 152 .driver = { 153 .name = "tpm_atmel", 154 .pm = &tpm_atml_pm, 155 }, 156}; 157 158static int __init init_atmel(void) 159{ 160 int rc = 0; 161 void __iomem *iobase = NULL; 162 int have_region, region_size; 163 unsigned long base; 164 struct tpm_chip *chip; 165 struct tpm_atmel_priv *priv; 166 167 rc = platform_driver_register(&atml_drv); 168 if (rc) 169 return rc; 170 171 if ((iobase = atmel_get_base_addr(&base, ®ion_size)) == NULL) { 172 rc = -ENODEV; 173 goto err_unreg_drv; 174 } 175 176 have_region = 177 (atmel_request_region 178 (base, region_size, "tpm_atmel0") == NULL) ? 0 : 1; 179 180 pdev = platform_device_register_simple("tpm_atmel", -1, NULL, 0); 181 if (IS_ERR(pdev)) { 182 rc = PTR_ERR(pdev); 183 goto err_rel_reg; 184 } 185 186 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 187 if (!priv) { 188 rc = -ENOMEM; 189 goto err_unreg_dev; 190 } 191 192 priv->iobase = iobase; 193 priv->base = base; 194 priv->have_region = have_region; 195 priv->region_size = region_size; 196 197 chip = tpmm_chip_alloc(&pdev->dev, &tpm_atmel); 198 if (IS_ERR(chip)) { 199 rc = PTR_ERR(chip); 200 goto err_unreg_dev; 201 } 202 203 dev_set_drvdata(&chip->dev, priv); 204 205 rc = tpm_chip_register(chip); 206 if (rc) 207 goto err_unreg_dev; 208 209 return 0; 210 211err_unreg_dev: 212 platform_device_unregister(pdev); 213err_rel_reg: 214 atmel_put_base_addr(iobase); 215 if (have_region) 216 atmel_release_region(base, 217 region_size); 218err_unreg_drv: 219 platform_driver_unregister(&atml_drv); 220 return rc; 221} 222 223static void __exit cleanup_atmel(void) 224{ 225 platform_driver_unregister(&atml_drv); 226 atml_plat_remove(); 227} 228 229module_init(init_atmel); 230module_exit(cleanup_atmel); 231 232MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)"); 233MODULE_DESCRIPTION("TPM Driver"); 234MODULE_VERSION("2.0"); 235MODULE_LICENSE("GPL");