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