Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 */ | |
3122a88a KH |
25 | enum tpm_nsc_addr { |
26 | TPM_NSC_BASE = 0x360, | |
27 | TPM_NSC_IRQ = 0x07 | |
28 | }; | |
1da177e4 | 29 | |
3122a88a KH |
30 | enum tpm_nsc_index { |
31 | NSC_LDN_INDEX = 0x07, | |
32 | NSC_SID_INDEX = 0x20, | |
33 | NSC_LDC_INDEX = 0x30, | |
34 | NSC_DIO_INDEX = 0x60, | |
35 | NSC_CIO_INDEX = 0x62, | |
36 | NSC_IRQ_INDEX = 0x70, | |
37 | NSC_ITS_INDEX = 0x71 | |
38 | }; | |
1da177e4 | 39 | |
3122a88a KH |
40 | enum tpm_nsc_status_loc { |
41 | NSC_STATUS = 0x01, | |
42 | NSC_COMMAND = 0x01, | |
43 | NSC_DATA = 0x00 | |
44 | }; | |
1da177e4 LT |
45 | |
46 | /* status bits */ | |
3122a88a KH |
47 | enum tpm_nsc_status{ |
48 | NSC_STATUS_OBF = 0x01, /* output buffer full */ | |
49 | NSC_STATUS_IBF = 0x02, /* input buffer full */ | |
50 | NSC_STATUS_F0 = 0x04, /* F0 */ | |
51 | NSC_STATUS_A2 = 0x08, /* A2 */ | |
52 | NSC_STATUS_RDY = 0x10, /* ready to receive command */ | |
53 | NSC_STATUS_IBR = 0x20 /* ready to receive data */ | |
54 | }; | |
1da177e4 | 55 | /* command bits */ |
3122a88a KH |
56 | enum tpm_nsc_cmd_mode { |
57 | NSC_COMMAND_NORMAL = 0x01, /* normal mode */ | |
58 | NSC_COMMAND_EOC = 0x03, | |
59 | NSC_COMMAND_CANCEL = 0x22 | |
60 | }; | |
1da177e4 LT |
61 | /* |
62 | * Wait for a certain status to appear | |
63 | */ | |
64 | static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data) | |
65 | { | |
700d8bdc | 66 | unsigned long stop; |
1da177e4 LT |
67 | |
68 | /* status immediately available check */ | |
69 | *data = inb(chip->vendor->base + NSC_STATUS); | |
70 | if ((*data & mask) == val) | |
71 | return 0; | |
72 | ||
73 | /* wait for status */ | |
700d8bdc | 74 | stop = jiffies + 10 * HZ; |
1da177e4 | 75 | do { |
700d8bdc | 76 | msleep(TPM_TIMEOUT); |
1da177e4 | 77 | *data = inb(chip->vendor->base + 1); |
700d8bdc | 78 | if ((*data & mask) == val) |
1da177e4 | 79 | return 0; |
1da177e4 | 80 | } |
700d8bdc | 81 | while (time_before(jiffies, stop)); |
1da177e4 LT |
82 | |
83 | return -EBUSY; | |
84 | } | |
85 | ||
86 | static int nsc_wait_for_ready(struct tpm_chip *chip) | |
87 | { | |
88 | int status; | |
700d8bdc | 89 | unsigned long stop; |
1da177e4 LT |
90 | |
91 | /* status immediately available check */ | |
92 | status = inb(chip->vendor->base + NSC_STATUS); | |
93 | if (status & NSC_STATUS_OBF) | |
94 | status = inb(chip->vendor->base + NSC_DATA); | |
95 | if (status & NSC_STATUS_RDY) | |
96 | return 0; | |
97 | ||
98 | /* wait for status */ | |
700d8bdc | 99 | stop = jiffies + 100; |
1da177e4 | 100 | do { |
700d8bdc | 101 | msleep(TPM_TIMEOUT); |
1da177e4 LT |
102 | status = inb(chip->vendor->base + NSC_STATUS); |
103 | if (status & NSC_STATUS_OBF) | |
104 | status = inb(chip->vendor->base + NSC_DATA); | |
700d8bdc | 105 | if (status & NSC_STATUS_RDY) |
1da177e4 | 106 | return 0; |
1da177e4 | 107 | } |
700d8bdc | 108 | while (time_before(jiffies, stop)); |
1da177e4 LT |
109 | |
110 | dev_info(&chip->pci_dev->dev, "wait for ready failed\n"); | |
111 | return -EBUSY; | |
112 | } | |
113 | ||
114 | ||
115 | static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count) | |
116 | { | |
117 | u8 *buffer = buf; | |
118 | u8 data, *p; | |
119 | u32 size; | |
120 | __be32 *native_size; | |
121 | ||
122 | if (count < 6) | |
123 | return -EIO; | |
124 | ||
125 | if (wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0) { | |
126 | dev_err(&chip->pci_dev->dev, "F0 timeout\n"); | |
127 | return -EIO; | |
128 | } | |
129 | if ((data = | |
130 | inb(chip->vendor->base + NSC_DATA)) != NSC_COMMAND_NORMAL) { | |
131 | dev_err(&chip->pci_dev->dev, "not in normal mode (0x%x)\n", | |
132 | data); | |
133 | return -EIO; | |
134 | } | |
135 | ||
136 | /* read the whole packet */ | |
137 | for (p = buffer; p < &buffer[count]; p++) { | |
138 | if (wait_for_stat | |
139 | (chip, NSC_STATUS_OBF, NSC_STATUS_OBF, &data) < 0) { | |
140 | dev_err(&chip->pci_dev->dev, | |
141 | "OBF timeout (while reading data)\n"); | |
142 | return -EIO; | |
143 | } | |
144 | if (data & NSC_STATUS_F0) | |
145 | break; | |
146 | *p = inb(chip->vendor->base + NSC_DATA); | |
147 | } | |
148 | ||
149 | if ((data & NSC_STATUS_F0) == 0) { | |
150 | dev_err(&chip->pci_dev->dev, "F0 not set\n"); | |
151 | return -EIO; | |
152 | } | |
153 | if ((data = inb(chip->vendor->base + NSC_DATA)) != NSC_COMMAND_EOC) { | |
154 | dev_err(&chip->pci_dev->dev, | |
155 | "expected end of command(0x%x)\n", data); | |
156 | return -EIO; | |
157 | } | |
158 | ||
159 | native_size = (__force __be32 *) (buf + 2); | |
160 | size = be32_to_cpu(*native_size); | |
161 | ||
162 | if (count < size) | |
163 | return -EIO; | |
164 | ||
165 | return size; | |
166 | } | |
167 | ||
168 | static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count) | |
169 | { | |
170 | u8 data; | |
171 | int i; | |
172 | ||
173 | /* | |
174 | * If we hit the chip with back to back commands it locks up | |
175 | * and never set IBF. Hitting it with this "hammer" seems to | |
176 | * fix it. Not sure why this is needed, we followed the flow | |
177 | * chart in the manual to the letter. | |
178 | */ | |
179 | outb(NSC_COMMAND_CANCEL, chip->vendor->base + NSC_COMMAND); | |
180 | ||
181 | if (nsc_wait_for_ready(chip) != 0) | |
182 | return -EIO; | |
183 | ||
184 | if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) { | |
185 | dev_err(&chip->pci_dev->dev, "IBF timeout\n"); | |
186 | return -EIO; | |
187 | } | |
188 | ||
189 | outb(NSC_COMMAND_NORMAL, chip->vendor->base + NSC_COMMAND); | |
190 | if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) { | |
191 | dev_err(&chip->pci_dev->dev, "IBR timeout\n"); | |
192 | return -EIO; | |
193 | } | |
194 | ||
195 | for (i = 0; i < count; i++) { | |
196 | if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) { | |
197 | dev_err(&chip->pci_dev->dev, | |
198 | "IBF timeout (while writing data)\n"); | |
199 | return -EIO; | |
200 | } | |
201 | outb(buf[i], chip->vendor->base + NSC_DATA); | |
202 | } | |
203 | ||
204 | if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) { | |
205 | dev_err(&chip->pci_dev->dev, "IBF timeout\n"); | |
206 | return -EIO; | |
207 | } | |
208 | outb(NSC_COMMAND_EOC, chip->vendor->base + NSC_COMMAND); | |
209 | ||
210 | return count; | |
211 | } | |
212 | ||
213 | static void tpm_nsc_cancel(struct tpm_chip *chip) | |
214 | { | |
215 | outb(NSC_COMMAND_CANCEL, chip->vendor->base + NSC_COMMAND); | |
216 | } | |
217 | ||
218 | static struct file_operations nsc_ops = { | |
219 | .owner = THIS_MODULE, | |
220 | .llseek = no_llseek, | |
221 | .open = tpm_open, | |
222 | .read = tpm_read, | |
223 | .write = tpm_write, | |
224 | .release = tpm_release, | |
225 | }; | |
226 | ||
227 | static struct tpm_vendor_specific tpm_nsc = { | |
228 | .recv = tpm_nsc_recv, | |
229 | .send = tpm_nsc_send, | |
230 | .cancel = tpm_nsc_cancel, | |
231 | .req_complete_mask = NSC_STATUS_OBF, | |
232 | .req_complete_val = NSC_STATUS_OBF, | |
233 | .base = TPM_NSC_BASE, | |
234 | .miscdev = { .fops = &nsc_ops, }, | |
235 | ||
236 | }; | |
237 | ||
238 | static int __devinit tpm_nsc_init(struct pci_dev *pci_dev, | |
239 | const struct pci_device_id *pci_id) | |
240 | { | |
241 | int rc = 0; | |
242 | ||
243 | if (pci_enable_device(pci_dev)) | |
244 | return -EIO; | |
245 | ||
246 | if (tpm_lpc_bus_init(pci_dev, TPM_NSC_BASE)) { | |
247 | rc = -ENODEV; | |
248 | goto out_err; | |
249 | } | |
250 | ||
251 | /* verify that it is a National part (SID) */ | |
252 | if (tpm_read_index(NSC_SID_INDEX) != 0xEF) { | |
253 | rc = -ENODEV; | |
254 | goto out_err; | |
255 | } | |
256 | ||
257 | dev_dbg(&pci_dev->dev, "NSC TPM detected\n"); | |
258 | dev_dbg(&pci_dev->dev, | |
259 | "NSC LDN 0x%x, SID 0x%x, SRID 0x%x\n", | |
260 | tpm_read_index(0x07), tpm_read_index(0x20), | |
261 | tpm_read_index(0x27)); | |
262 | dev_dbg(&pci_dev->dev, | |
263 | "NSC SIOCF1 0x%x SIOCF5 0x%x SIOCF6 0x%x SIOCF8 0x%x\n", | |
264 | tpm_read_index(0x21), tpm_read_index(0x25), | |
265 | tpm_read_index(0x26), tpm_read_index(0x28)); | |
266 | dev_dbg(&pci_dev->dev, "NSC IO Base0 0x%x\n", | |
267 | (tpm_read_index(0x60) << 8) | tpm_read_index(0x61)); | |
268 | dev_dbg(&pci_dev->dev, "NSC IO Base1 0x%x\n", | |
269 | (tpm_read_index(0x62) << 8) | tpm_read_index(0x63)); | |
270 | dev_dbg(&pci_dev->dev, "NSC Interrupt number and wakeup 0x%x\n", | |
271 | tpm_read_index(0x70)); | |
272 | dev_dbg(&pci_dev->dev, "NSC IRQ type select 0x%x\n", | |
273 | tpm_read_index(0x71)); | |
274 | dev_dbg(&pci_dev->dev, | |
275 | "NSC DMA channel select0 0x%x, select1 0x%x\n", | |
276 | tpm_read_index(0x74), tpm_read_index(0x75)); | |
277 | dev_dbg(&pci_dev->dev, | |
278 | "NSC Config " | |
279 | "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", | |
280 | tpm_read_index(0xF0), tpm_read_index(0xF1), | |
281 | tpm_read_index(0xF2), tpm_read_index(0xF3), | |
282 | tpm_read_index(0xF4), tpm_read_index(0xF5), | |
283 | tpm_read_index(0xF6), tpm_read_index(0xF7), | |
284 | tpm_read_index(0xF8), tpm_read_index(0xF9)); | |
285 | ||
286 | dev_info(&pci_dev->dev, | |
287 | "NSC PC21100 TPM revision %d\n", | |
288 | tpm_read_index(0x27) & 0x1F); | |
289 | ||
290 | if (tpm_read_index(NSC_LDC_INDEX) == 0) | |
291 | dev_info(&pci_dev->dev, ": NSC TPM not active\n"); | |
292 | ||
293 | /* select PM channel 1 */ | |
294 | tpm_write_index(NSC_LDN_INDEX, 0x12); | |
295 | tpm_read_index(NSC_LDN_INDEX); | |
296 | ||
297 | /* disable the DPM module */ | |
298 | tpm_write_index(NSC_LDC_INDEX, 0); | |
299 | tpm_read_index(NSC_LDC_INDEX); | |
300 | ||
301 | /* set the data register base addresses */ | |
302 | tpm_write_index(NSC_DIO_INDEX, TPM_NSC_BASE >> 8); | |
303 | tpm_write_index(NSC_DIO_INDEX + 1, TPM_NSC_BASE); | |
304 | tpm_read_index(NSC_DIO_INDEX); | |
305 | tpm_read_index(NSC_DIO_INDEX + 1); | |
306 | ||
307 | /* set the command register base addresses */ | |
308 | tpm_write_index(NSC_CIO_INDEX, (TPM_NSC_BASE + 1) >> 8); | |
309 | tpm_write_index(NSC_CIO_INDEX + 1, (TPM_NSC_BASE + 1)); | |
310 | tpm_read_index(NSC_DIO_INDEX); | |
311 | tpm_read_index(NSC_DIO_INDEX + 1); | |
312 | ||
313 | /* set the interrupt number to be used for the host interface */ | |
314 | tpm_write_index(NSC_IRQ_INDEX, TPM_NSC_IRQ); | |
315 | tpm_write_index(NSC_ITS_INDEX, 0x00); | |
316 | tpm_read_index(NSC_IRQ_INDEX); | |
317 | ||
318 | /* enable the DPM module */ | |
319 | tpm_write_index(NSC_LDC_INDEX, 0x01); | |
320 | tpm_read_index(NSC_LDC_INDEX); | |
321 | ||
322 | if ((rc = tpm_register_hardware(pci_dev, &tpm_nsc)) < 0) | |
323 | goto out_err; | |
324 | ||
325 | return 0; | |
326 | ||
327 | out_err: | |
328 | pci_disable_device(pci_dev); | |
329 | return rc; | |
330 | } | |
331 | ||
332 | static struct pci_device_id tpm_pci_tbl[] __devinitdata = { | |
333 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0)}, | |
334 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12)}, | |
335 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0)}, | |
336 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12)}, | |
337 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0)}, | |
338 | {PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_LPC)}, | |
339 | {0,} | |
340 | }; | |
341 | ||
342 | MODULE_DEVICE_TABLE(pci, tpm_pci_tbl); | |
343 | ||
344 | static struct pci_driver nsc_pci_driver = { | |
345 | .name = "tpm_nsc", | |
346 | .id_table = tpm_pci_tbl, | |
347 | .probe = tpm_nsc_init, | |
348 | .remove = __devexit_p(tpm_remove), | |
349 | .suspend = tpm_pm_suspend, | |
350 | .resume = tpm_pm_resume, | |
351 | }; | |
352 | ||
353 | static int __init init_nsc(void) | |
354 | { | |
355 | return pci_register_driver(&nsc_pci_driver); | |
356 | } | |
357 | ||
358 | static void __exit cleanup_nsc(void) | |
359 | { | |
360 | pci_unregister_driver(&nsc_pci_driver); | |
361 | } | |
362 | ||
363 | module_init(init_nsc); | |
364 | module_exit(cleanup_nsc); | |
365 | ||
366 | MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)"); | |
367 | MODULE_DESCRIPTION("TPM Driver"); | |
368 | MODULE_VERSION("2.0"); | |
369 | MODULE_LICENSE("GPL"); |