diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/char/tpm/tpm_nsc.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/char/tpm/tpm_nsc.c')
-rw-r--r-- | drivers/char/tpm/tpm_nsc.c | 373 |
1 files changed, 373 insertions, 0 deletions
diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c new file mode 100644 index 000000000000..9cce833a0923 --- /dev/null +++ b/drivers/char/tpm/tpm_nsc.c | |||
@@ -0,0 +1,373 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2004 IBM Corporation | ||
3 | * | ||
4 | * Authors: | ||
5 | * Leendert van Doorn <leendert@watson.ibm.com> | ||
6 | * Dave Safford <safford@watson.ibm.com> | ||
7 | * Reiner Sailer <sailer@watson.ibm.com> | ||
8 | * Kylene Hall <kjhall@us.ibm.com> | ||
9 | * | ||
10 | * Maintained by: <tpmdd_devel@lists.sourceforge.net> | ||
11 | * | ||
12 | * Device driver for TCG/TCPA TPM (trusted platform module). | ||
13 | * Specifications at www.trustedcomputinggroup.org | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or | ||
16 | * modify it under the terms of the GNU General Public License as | ||
17 | * published by the Free Software Foundation, version 2 of the | ||
18 | * License. | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "tpm.h" | ||
23 | |||
24 | /* National definitions */ | ||
25 | #define TPM_NSC_BASE 0x360 | ||
26 | #define TPM_NSC_IRQ 0x07 | ||
27 | |||
28 | #define NSC_LDN_INDEX 0x07 | ||
29 | #define NSC_SID_INDEX 0x20 | ||
30 | #define NSC_LDC_INDEX 0x30 | ||
31 | #define NSC_DIO_INDEX 0x60 | ||
32 | #define NSC_CIO_INDEX 0x62 | ||
33 | #define NSC_IRQ_INDEX 0x70 | ||
34 | #define NSC_ITS_INDEX 0x71 | ||
35 | |||
36 | #define NSC_STATUS 0x01 | ||
37 | #define NSC_COMMAND 0x01 | ||
38 | #define NSC_DATA 0x00 | ||
39 | |||
40 | /* status bits */ | ||
41 | #define NSC_STATUS_OBF 0x01 /* output buffer full */ | ||
42 | #define NSC_STATUS_IBF 0x02 /* input buffer full */ | ||
43 | #define NSC_STATUS_F0 0x04 /* F0 */ | ||
44 | #define NSC_STATUS_A2 0x08 /* A2 */ | ||
45 | #define NSC_STATUS_RDY 0x10 /* ready to receive command */ | ||
46 | #define NSC_STATUS_IBR 0x20 /* ready to receive data */ | ||
47 | |||
48 | /* command bits */ | ||
49 | #define NSC_COMMAND_NORMAL 0x01 /* normal mode */ | ||
50 | #define NSC_COMMAND_EOC 0x03 | ||
51 | #define NSC_COMMAND_CANCEL 0x22 | ||
52 | |||
53 | /* | ||
54 | * Wait for a certain status to appear | ||
55 | */ | ||
56 | static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data) | ||
57 | { | ||
58 | int expired = 0; | ||
59 | struct timer_list status_timer = | ||
60 | TIMER_INITIALIZER(tpm_time_expired, jiffies + 10 * HZ, | ||
61 | (unsigned long) &expired); | ||
62 | |||
63 | /* status immediately available check */ | ||
64 | *data = inb(chip->vendor->base + NSC_STATUS); | ||
65 | if ((*data & mask) == val) | ||
66 | return 0; | ||
67 | |||
68 | /* wait for status */ | ||
69 | add_timer(&status_timer); | ||
70 | do { | ||
71 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
72 | schedule_timeout(TPM_TIMEOUT); | ||
73 | *data = inb(chip->vendor->base + 1); | ||
74 | if ((*data & mask) == val) { | ||
75 | del_singleshot_timer_sync(&status_timer); | ||
76 | return 0; | ||
77 | } | ||
78 | } | ||
79 | while (!expired); | ||
80 | |||
81 | return -EBUSY; | ||
82 | } | ||
83 | |||
84 | static int nsc_wait_for_ready(struct tpm_chip *chip) | ||
85 | { | ||
86 | int status; | ||
87 | int expired = 0; | ||
88 | struct timer_list status_timer = | ||
89 | TIMER_INITIALIZER(tpm_time_expired, jiffies + 100, | ||
90 | (unsigned long) &expired); | ||
91 | |||
92 | /* status immediately available check */ | ||
93 | status = inb(chip->vendor->base + NSC_STATUS); | ||
94 | if (status & NSC_STATUS_OBF) | ||
95 | status = inb(chip->vendor->base + NSC_DATA); | ||
96 | if (status & NSC_STATUS_RDY) | ||
97 | return 0; | ||
98 | |||
99 | /* wait for status */ | ||
100 | add_timer(&status_timer); | ||
101 | do { | ||
102 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
103 | schedule_timeout(TPM_TIMEOUT); | ||
104 | status = inb(chip->vendor->base + NSC_STATUS); | ||
105 | if (status & NSC_STATUS_OBF) | ||
106 | status = inb(chip->vendor->base + NSC_DATA); | ||
107 | if (status & NSC_STATUS_RDY) { | ||
108 | del_singleshot_timer_sync(&status_timer); | ||
109 | return 0; | ||
110 | } | ||
111 | } | ||
112 | while (!expired); | ||
113 | |||
114 | dev_info(&chip->pci_dev->dev, "wait for ready failed\n"); | ||
115 | return -EBUSY; | ||
116 | } | ||
117 | |||
118 | |||
119 | static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count) | ||
120 | { | ||
121 | u8 *buffer = buf; | ||
122 | u8 data, *p; | ||
123 | u32 size; | ||
124 | __be32 *native_size; | ||
125 | |||
126 | if (count < 6) | ||
127 | return -EIO; | ||
128 | |||
129 | if (wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0) { | ||
130 | dev_err(&chip->pci_dev->dev, "F0 timeout\n"); | ||
131 | return -EIO; | ||
132 | } | ||
133 | if ((data = | ||
134 | inb(chip->vendor->base + NSC_DATA)) != NSC_COMMAND_NORMAL) { | ||
135 | dev_err(&chip->pci_dev->dev, "not in normal mode (0x%x)\n", | ||
136 | data); | ||
137 | return -EIO; | ||
138 | } | ||
139 | |||
140 | /* read the whole packet */ | ||
141 | for (p = buffer; p < &buffer[count]; p++) { | ||
142 | if (wait_for_stat | ||
143 | (chip, NSC_STATUS_OBF, NSC_STATUS_OBF, &data) < 0) { | ||
144 | dev_err(&chip->pci_dev->dev, | ||
145 | "OBF timeout (while reading data)\n"); | ||
146 | return -EIO; | ||
147 | } | ||
148 | if (data & NSC_STATUS_F0) | ||
149 | break; | ||
150 | *p = inb(chip->vendor->base + NSC_DATA); | ||
151 | } | ||
152 | |||
153 | if ((data & NSC_STATUS_F0) == 0) { | ||
154 | dev_err(&chip->pci_dev->dev, "F0 not set\n"); | ||
155 | return -EIO; | ||
156 | } | ||
157 | if ((data = inb(chip->vendor->base + NSC_DATA)) != NSC_COMMAND_EOC) { | ||
158 | dev_err(&chip->pci_dev->dev, | ||
159 | "expected end of command(0x%x)\n", data); | ||
160 | return -EIO; | ||
161 | } | ||
162 | |||
163 | native_size = (__force __be32 *) (buf + 2); | ||
164 | size = be32_to_cpu(*native_size); | ||
165 | |||
166 | if (count < size) | ||
167 | return -EIO; | ||
168 | |||
169 | return size; | ||
170 | } | ||
171 | |||
172 | static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count) | ||
173 | { | ||
174 | u8 data; | ||
175 | int i; | ||
176 | |||
177 | /* | ||
178 | * If we hit the chip with back to back commands it locks up | ||
179 | * and never set IBF. Hitting it with this "hammer" seems to | ||
180 | * fix it. Not sure why this is needed, we followed the flow | ||
181 | * chart in the manual to the letter. | ||
182 | */ | ||
183 | outb(NSC_COMMAND_CANCEL, chip->vendor->base + NSC_COMMAND); | ||
184 | |||
185 | if (nsc_wait_for_ready(chip) != 0) | ||
186 | return -EIO; | ||
187 | |||
188 | if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) { | ||
189 | dev_err(&chip->pci_dev->dev, "IBF timeout\n"); | ||
190 | return -EIO; | ||
191 | } | ||
192 | |||
193 | outb(NSC_COMMAND_NORMAL, chip->vendor->base + NSC_COMMAND); | ||
194 | if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) { | ||
195 | dev_err(&chip->pci_dev->dev, "IBR timeout\n"); | ||
196 | return -EIO; | ||
197 | } | ||
198 | |||
199 | for (i = 0; i < count; i++) { | ||
200 | if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) { | ||
201 | dev_err(&chip->pci_dev->dev, | ||
202 | "IBF timeout (while writing data)\n"); | ||
203 | return -EIO; | ||
204 | } | ||
205 | outb(buf[i], chip->vendor->base + NSC_DATA); | ||
206 | } | ||
207 | |||
208 | if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) { | ||
209 | dev_err(&chip->pci_dev->dev, "IBF timeout\n"); | ||
210 | return -EIO; | ||
211 | } | ||
212 | outb(NSC_COMMAND_EOC, chip->vendor->base + NSC_COMMAND); | ||
213 | |||
214 | return count; | ||
215 | } | ||
216 | |||
217 | static void tpm_nsc_cancel(struct tpm_chip *chip) | ||
218 | { | ||
219 | outb(NSC_COMMAND_CANCEL, chip->vendor->base + NSC_COMMAND); | ||
220 | } | ||
221 | |||
222 | static struct file_operations nsc_ops = { | ||
223 | .owner = THIS_MODULE, | ||
224 | .llseek = no_llseek, | ||
225 | .open = tpm_open, | ||
226 | .read = tpm_read, | ||
227 | .write = tpm_write, | ||
228 | .release = tpm_release, | ||
229 | }; | ||
230 | |||
231 | static struct tpm_vendor_specific tpm_nsc = { | ||
232 | .recv = tpm_nsc_recv, | ||
233 | .send = tpm_nsc_send, | ||
234 | .cancel = tpm_nsc_cancel, | ||
235 | .req_complete_mask = NSC_STATUS_OBF, | ||
236 | .req_complete_val = NSC_STATUS_OBF, | ||
237 | .base = TPM_NSC_BASE, | ||
238 | .miscdev = { .fops = &nsc_ops, }, | ||
239 | |||
240 | }; | ||
241 | |||
242 | static int __devinit tpm_nsc_init(struct pci_dev *pci_dev, | ||
243 | const struct pci_device_id *pci_id) | ||
244 | { | ||
245 | int rc = 0; | ||
246 | |||
247 | if (pci_enable_device(pci_dev)) | ||
248 | return -EIO; | ||
249 | |||
250 | if (tpm_lpc_bus_init(pci_dev, TPM_NSC_BASE)) { | ||
251 | rc = -ENODEV; | ||
252 | goto out_err; | ||
253 | } | ||
254 | |||
255 | /* verify that it is a National part (SID) */ | ||
256 | if (tpm_read_index(NSC_SID_INDEX) != 0xEF) { | ||
257 | rc = -ENODEV; | ||
258 | goto out_err; | ||
259 | } | ||
260 | |||
261 | dev_dbg(&pci_dev->dev, "NSC TPM detected\n"); | ||
262 | dev_dbg(&pci_dev->dev, | ||
263 | "NSC LDN 0x%x, SID 0x%x, SRID 0x%x\n", | ||
264 | tpm_read_index(0x07), tpm_read_index(0x20), | ||
265 | tpm_read_index(0x27)); | ||
266 | dev_dbg(&pci_dev->dev, | ||
267 | "NSC SIOCF1 0x%x SIOCF5 0x%x SIOCF6 0x%x SIOCF8 0x%x\n", | ||
268 | tpm_read_index(0x21), tpm_read_index(0x25), | ||
269 | tpm_read_index(0x26), tpm_read_index(0x28)); | ||
270 | dev_dbg(&pci_dev->dev, "NSC IO Base0 0x%x\n", | ||
271 | (tpm_read_index(0x60) << 8) | tpm_read_index(0x61)); | ||
272 | dev_dbg(&pci_dev->dev, "NSC IO Base1 0x%x\n", | ||
273 | (tpm_read_index(0x62) << 8) | tpm_read_index(0x63)); | ||
274 | dev_dbg(&pci_dev->dev, "NSC Interrupt number and wakeup 0x%x\n", | ||
275 | tpm_read_index(0x70)); | ||
276 | dev_dbg(&pci_dev->dev, "NSC IRQ type select 0x%x\n", | ||
277 | tpm_read_index(0x71)); | ||
278 | dev_dbg(&pci_dev->dev, | ||
279 | "NSC DMA channel select0 0x%x, select1 0x%x\n", | ||
280 | tpm_read_index(0x74), tpm_read_index(0x75)); | ||
281 | dev_dbg(&pci_dev->dev, | ||
282 | "NSC Config " | ||
283 | "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", | ||
284 | tpm_read_index(0xF0), tpm_read_index(0xF1), | ||
285 | tpm_read_index(0xF2), tpm_read_index(0xF3), | ||
286 | tpm_read_index(0xF4), tpm_read_index(0xF5), | ||
287 | tpm_read_index(0xF6), tpm_read_index(0xF7), | ||
288 | tpm_read_index(0xF8), tpm_read_index(0xF9)); | ||
289 | |||
290 | dev_info(&pci_dev->dev, | ||
291 | "NSC PC21100 TPM revision %d\n", | ||
292 | tpm_read_index(0x27) & 0x1F); | ||
293 | |||
294 | if (tpm_read_index(NSC_LDC_INDEX) == 0) | ||
295 | dev_info(&pci_dev->dev, ": NSC TPM not active\n"); | ||
296 | |||
297 | /* select PM channel 1 */ | ||
298 | tpm_write_index(NSC_LDN_INDEX, 0x12); | ||
299 | tpm_read_index(NSC_LDN_INDEX); | ||
300 | |||
301 | /* disable the DPM module */ | ||
302 | tpm_write_index(NSC_LDC_INDEX, 0); | ||
303 | tpm_read_index(NSC_LDC_INDEX); | ||
304 | |||
305 | /* set the data register base addresses */ | ||
306 | tpm_write_index(NSC_DIO_INDEX, TPM_NSC_BASE >> 8); | ||
307 | tpm_write_index(NSC_DIO_INDEX + 1, TPM_NSC_BASE); | ||
308 | tpm_read_index(NSC_DIO_INDEX); | ||
309 | tpm_read_index(NSC_DIO_INDEX + 1); | ||
310 | |||
311 | /* set the command register base addresses */ | ||
312 | tpm_write_index(NSC_CIO_INDEX, (TPM_NSC_BASE + 1) >> 8); | ||
313 | tpm_write_index(NSC_CIO_INDEX + 1, (TPM_NSC_BASE + 1)); | ||
314 | tpm_read_index(NSC_DIO_INDEX); | ||
315 | tpm_read_index(NSC_DIO_INDEX + 1); | ||
316 | |||
317 | /* set the interrupt number to be used for the host interface */ | ||
318 | tpm_write_index(NSC_IRQ_INDEX, TPM_NSC_IRQ); | ||
319 | tpm_write_index(NSC_ITS_INDEX, 0x00); | ||
320 | tpm_read_index(NSC_IRQ_INDEX); | ||
321 | |||
322 | /* enable the DPM module */ | ||
323 | tpm_write_index(NSC_LDC_INDEX, 0x01); | ||
324 | tpm_read_index(NSC_LDC_INDEX); | ||
325 | |||
326 | if ((rc = tpm_register_hardware(pci_dev, &tpm_nsc)) < 0) | ||
327 | goto out_err; | ||
328 | |||
329 | return 0; | ||
330 | |||
331 | out_err: | ||
332 | pci_disable_device(pci_dev); | ||
333 | return rc; | ||
334 | } | ||
335 | |||
336 | static struct pci_device_id tpm_pci_tbl[] __devinitdata = { | ||
337 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0)}, | ||
338 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12)}, | ||
339 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0)}, | ||
340 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12)}, | ||
341 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0)}, | ||
342 | {PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_LPC)}, | ||
343 | {0,} | ||
344 | }; | ||
345 | |||
346 | MODULE_DEVICE_TABLE(pci, tpm_pci_tbl); | ||
347 | |||
348 | static struct pci_driver nsc_pci_driver = { | ||
349 | .name = "tpm_nsc", | ||
350 | .id_table = tpm_pci_tbl, | ||
351 | .probe = tpm_nsc_init, | ||
352 | .remove = __devexit_p(tpm_remove), | ||
353 | .suspend = tpm_pm_suspend, | ||
354 | .resume = tpm_pm_resume, | ||
355 | }; | ||
356 | |||
357 | static int __init init_nsc(void) | ||
358 | { | ||
359 | return pci_register_driver(&nsc_pci_driver); | ||
360 | } | ||
361 | |||
362 | static void __exit cleanup_nsc(void) | ||
363 | { | ||
364 | pci_unregister_driver(&nsc_pci_driver); | ||
365 | } | ||
366 | |||
367 | module_init(init_nsc); | ||
368 | module_exit(cleanup_nsc); | ||
369 | |||
370 | MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)"); | ||
371 | MODULE_DESCRIPTION("TPM Driver"); | ||
372 | MODULE_VERSION("2.0"); | ||
373 | MODULE_LICENSE("GPL"); | ||