diff options
Diffstat (limited to 'drivers/char/tpm')
-rw-r--r-- | drivers/char/tpm/Kconfig | 11 | ||||
-rw-r--r-- | drivers/char/tpm/Makefile | 1 | ||||
-rw-r--r-- | drivers/char/tpm/tpm.c | 3 | ||||
-rw-r--r-- | drivers/char/tpm/tpm.h | 9 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_tis.c | 647 |
5 files changed, 670 insertions, 1 deletions
diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index a6873bf89ffa..1efde3b27619 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig | |||
@@ -20,9 +20,18 @@ config TCG_TPM | |||
20 | Note: For more TPM drivers enable CONFIG_PNP, CONFIG_ACPI | 20 | Note: For more TPM drivers enable CONFIG_PNP, CONFIG_ACPI |
21 | and CONFIG_PNPACPI. | 21 | and CONFIG_PNPACPI. |
22 | 22 | ||
23 | config TCG_TIS | ||
24 | tristate "TPM Interface Specification 1.2 Interface" | ||
25 | depends on TCG_TPM | ||
26 | ---help--- | ||
27 | If you have a TPM security chip that is compliant with the | ||
28 | TCG TIS 1.2 TPM specification say Yes and it will be accessible | ||
29 | from within Linux. To compile this driver as a module, choose | ||
30 | M here; the module will be called tpm_tis. | ||
31 | |||
23 | config TCG_NSC | 32 | config TCG_NSC |
24 | tristate "National Semiconductor TPM Interface" | 33 | tristate "National Semiconductor TPM Interface" |
25 | depends on TCG_TPM | 34 | depends on TCG_TPM && PNPACPI |
26 | ---help--- | 35 | ---help--- |
27 | If you have a TPM security chip from National Semicondutor | 36 | If you have a TPM security chip from National Semicondutor |
28 | say Yes and it will be accessible from within Linux. To | 37 | say Yes and it will be accessible from within Linux. To |
diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index ba4582d160fd..ea3a1e02a824 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile | |||
@@ -5,6 +5,7 @@ obj-$(CONFIG_TCG_TPM) += tpm.o | |||
5 | ifdef CONFIG_ACPI | 5 | ifdef CONFIG_ACPI |
6 | obj-$(CONFIG_TCG_TPM) += tpm_bios.o | 6 | obj-$(CONFIG_TCG_TPM) += tpm_bios.o |
7 | endif | 7 | endif |
8 | obj-$(CONFIG_TCG_TIS) += tpm_tis.o | ||
8 | obj-$(CONFIG_TCG_NSC) += tpm_nsc.o | 9 | obj-$(CONFIG_TCG_NSC) += tpm_nsc.o |
9 | obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o | 10 | obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o |
10 | obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o | 11 | obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o |
diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 24c4423d4851..150c86af7809 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c | |||
@@ -390,6 +390,9 @@ static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, | |||
390 | goto out; | 390 | goto out; |
391 | } | 391 | } |
392 | 392 | ||
393 | if (chip->vendor.irq) | ||
394 | goto out_recv; | ||
395 | |||
393 | stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal); | 396 | stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal); |
394 | do { | 397 | do { |
395 | u8 status = chip->vendor.status(chip); | 398 | u8 status = chip->vendor.status(chip); |
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 75d105c4e65d..24541a556d02 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h | |||
@@ -64,6 +64,8 @@ struct tpm_vendor_specific { | |||
64 | void __iomem *iobase; /* ioremapped address */ | 64 | void __iomem *iobase; /* ioremapped address */ |
65 | unsigned long base; /* TPM base address */ | 65 | unsigned long base; /* TPM base address */ |
66 | 66 | ||
67 | int irq; | ||
68 | |||
67 | int region_size; | 69 | int region_size; |
68 | int have_region; | 70 | int have_region; |
69 | 71 | ||
@@ -73,8 +75,13 @@ struct tpm_vendor_specific { | |||
73 | u8 (*status) (struct tpm_chip *); | 75 | u8 (*status) (struct tpm_chip *); |
74 | struct miscdevice miscdev; | 76 | struct miscdevice miscdev; |
75 | struct attribute_group *attr_group; | 77 | struct attribute_group *attr_group; |
78 | struct list_head list; | ||
79 | int locality; | ||
76 | u32 timeout_a, timeout_b, timeout_c, timeout_d; | 80 | u32 timeout_a, timeout_b, timeout_c, timeout_d; |
77 | u32 duration[3]; | 81 | u32 duration[3]; |
82 | |||
83 | wait_queue_head_t read_queue; | ||
84 | wait_queue_head_t int_queue; | ||
78 | }; | 85 | }; |
79 | 86 | ||
80 | struct tpm_chip { | 87 | struct tpm_chip { |
@@ -100,6 +107,8 @@ struct tpm_chip { | |||
100 | struct list_head list; | 107 | struct list_head list; |
101 | }; | 108 | }; |
102 | 109 | ||
110 | #define to_tpm_chip(n) container_of(n, struct tpm_chip, vendor) | ||
111 | |||
103 | static inline int tpm_read_index(int base, int index) | 112 | static inline int tpm_read_index(int base, int index) |
104 | { | 113 | { |
105 | outb(index, base); | 114 | outb(index, base); |
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c new file mode 100644 index 000000000000..02759307f736 --- /dev/null +++ b/drivers/char/tpm/tpm_tis.c | |||
@@ -0,0 +1,647 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2005, 2006 IBM Corporation | ||
3 | * | ||
4 | * Authors: | ||
5 | * Leendert van Doorn <leendert@watson.ibm.com> | ||
6 | * Kylene Hall <kjhall@us.ibm.com> | ||
7 | * | ||
8 | * Device driver for TCG/TCPA TPM (trusted platform module). | ||
9 | * Specifications at www.trustedcomputinggroup.org | ||
10 | * | ||
11 | * This device driver implements the TPM interface as defined in | ||
12 | * the TCG TPM Interface Spec version 1.2, revision 1.0. | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or | ||
15 | * modify it under the terms of the GNU General Public License as | ||
16 | * published by the Free Software Foundation, version 2 of the | ||
17 | * License. | ||
18 | */ | ||
19 | #include <linux/pnp.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/wait.h> | ||
22 | #include "tpm.h" | ||
23 | |||
24 | #define TPM_HEADER_SIZE 10 | ||
25 | |||
26 | enum tis_access { | ||
27 | TPM_ACCESS_VALID = 0x80, | ||
28 | TPM_ACCESS_ACTIVE_LOCALITY = 0x20, | ||
29 | TPM_ACCESS_REQUEST_PENDING = 0x04, | ||
30 | TPM_ACCESS_REQUEST_USE = 0x02, | ||
31 | }; | ||
32 | |||
33 | enum tis_status { | ||
34 | TPM_STS_VALID = 0x80, | ||
35 | TPM_STS_COMMAND_READY = 0x40, | ||
36 | TPM_STS_GO = 0x20, | ||
37 | TPM_STS_DATA_AVAIL = 0x10, | ||
38 | TPM_STS_DATA_EXPECT = 0x08, | ||
39 | }; | ||
40 | |||
41 | enum tis_int_flags { | ||
42 | TPM_GLOBAL_INT_ENABLE = 0x80000000, | ||
43 | TPM_INTF_BURST_COUNT_STATIC = 0x100, | ||
44 | TPM_INTF_CMD_READY_INT = 0x080, | ||
45 | TPM_INTF_INT_EDGE_FALLING = 0x040, | ||
46 | TPM_INTF_INT_EDGE_RISING = 0x020, | ||
47 | TPM_INTF_INT_LEVEL_LOW = 0x010, | ||
48 | TPM_INTF_INT_LEVEL_HIGH = 0x008, | ||
49 | TPM_INTF_LOCALITY_CHANGE_INT = 0x004, | ||
50 | TPM_INTF_STS_VALID_INT = 0x002, | ||
51 | TPM_INTF_DATA_AVAIL_INT = 0x001, | ||
52 | }; | ||
53 | |||
54 | #define TPM_ACCESS(l) (0x0000 | ((l) << 12)) | ||
55 | #define TPM_INT_ENABLE(l) (0x0008 | ((l) << 12)) | ||
56 | #define TPM_INT_VECTOR(l) (0x000C | ((l) << 12)) | ||
57 | #define TPM_INT_STATUS(l) (0x0010 | ((l) << 12)) | ||
58 | #define TPM_INTF_CAPS(l) (0x0014 | ((l) << 12)) | ||
59 | #define TPM_STS(l) (0x0018 | ((l) << 12)) | ||
60 | #define TPM_DATA_FIFO(l) (0x0024 | ((l) << 12)) | ||
61 | |||
62 | #define TPM_DID_VID(l) (0x0F00 | ((l) << 12)) | ||
63 | #define TPM_RID(l) (0x0F04 | ((l) << 12)) | ||
64 | |||
65 | static LIST_HEAD(tis_chips); | ||
66 | static DEFINE_SPINLOCK(tis_lock); | ||
67 | |||
68 | static int check_locality(struct tpm_chip *chip, int l) | ||
69 | { | ||
70 | if ((ioread8(chip->vendor.iobase + TPM_ACCESS(l)) & | ||
71 | (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == | ||
72 | (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) | ||
73 | return chip->vendor.locality = l; | ||
74 | |||
75 | return -1; | ||
76 | } | ||
77 | |||
78 | static void release_locality(struct tpm_chip *chip, int l, int force) | ||
79 | { | ||
80 | if (force || (ioread8(chip->vendor.iobase + TPM_ACCESS(l)) & | ||
81 | (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) == | ||
82 | (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) | ||
83 | iowrite8(TPM_ACCESS_ACTIVE_LOCALITY, | ||
84 | chip->vendor.iobase + TPM_ACCESS(l)); | ||
85 | } | ||
86 | |||
87 | static int request_locality(struct tpm_chip *chip, int l) | ||
88 | { | ||
89 | unsigned long stop; | ||
90 | long rc; | ||
91 | |||
92 | if (check_locality(chip, l) >= 0) | ||
93 | return l; | ||
94 | |||
95 | iowrite8(TPM_ACCESS_REQUEST_USE, | ||
96 | chip->vendor.iobase + TPM_ACCESS(l)); | ||
97 | |||
98 | if (chip->vendor.irq) { | ||
99 | rc = wait_event_interruptible_timeout(chip->vendor. | ||
100 | int_queue, | ||
101 | (check_locality | ||
102 | (chip, l) >= 0), | ||
103 | msecs_to_jiffies | ||
104 | (chip->vendor. | ||
105 | timeout_a)); | ||
106 | if (rc > 0) | ||
107 | return l; | ||
108 | |||
109 | } else { | ||
110 | /* wait for burstcount */ | ||
111 | stop = jiffies + (HZ * chip->vendor.timeout_a / 1000); | ||
112 | do { | ||
113 | if (check_locality(chip, l) >= 0) | ||
114 | return l; | ||
115 | msleep(TPM_TIMEOUT); | ||
116 | } | ||
117 | while (time_before(jiffies, stop)); | ||
118 | } | ||
119 | return -1; | ||
120 | } | ||
121 | |||
122 | static u8 tpm_tis_status(struct tpm_chip *chip) | ||
123 | { | ||
124 | return ioread8(chip->vendor.iobase + | ||
125 | TPM_STS(chip->vendor.locality)); | ||
126 | } | ||
127 | |||
128 | static void tpm_tis_ready(struct tpm_chip *chip) | ||
129 | { | ||
130 | /* this causes the current command to be aborted */ | ||
131 | iowrite8(TPM_STS_COMMAND_READY, | ||
132 | chip->vendor.iobase + TPM_STS(chip->vendor.locality)); | ||
133 | } | ||
134 | |||
135 | static int get_burstcount(struct tpm_chip *chip) | ||
136 | { | ||
137 | unsigned long stop; | ||
138 | int burstcnt; | ||
139 | |||
140 | /* wait for burstcount */ | ||
141 | /* which timeout value, spec has 2 answers (c & d) */ | ||
142 | stop = jiffies + (HZ * chip->vendor.timeout_d / 1000); | ||
143 | do { | ||
144 | burstcnt = ioread8(chip->vendor.iobase + | ||
145 | TPM_STS(chip->vendor.locality) + 1); | ||
146 | burstcnt += ioread8(chip->vendor.iobase + | ||
147 | TPM_STS(chip->vendor.locality) + | ||
148 | 2) << 8; | ||
149 | if (burstcnt) | ||
150 | return burstcnt; | ||
151 | msleep(TPM_TIMEOUT); | ||
152 | } while (time_before(jiffies, stop)); | ||
153 | return -EBUSY; | ||
154 | } | ||
155 | |||
156 | static int wait_for_stat(struct tpm_chip *chip, u8 mask, u32 timeout, | ||
157 | wait_queue_head_t *queue) | ||
158 | { | ||
159 | unsigned long stop; | ||
160 | long rc; | ||
161 | u8 status; | ||
162 | |||
163 | /* check current status */ | ||
164 | status = tpm_tis_status(chip); | ||
165 | if ((status & mask) == mask) | ||
166 | return 0; | ||
167 | |||
168 | if (chip->vendor.irq) { | ||
169 | rc = wait_event_interruptible_timeout(*queue, | ||
170 | ((tpm_tis_status | ||
171 | (chip) & mask) == | ||
172 | mask), | ||
173 | msecs_to_jiffies | ||
174 | (timeout)); | ||
175 | if (rc > 0) | ||
176 | return 0; | ||
177 | } else { | ||
178 | stop = jiffies + (HZ * timeout / 1000); | ||
179 | do { | ||
180 | msleep(TPM_TIMEOUT); | ||
181 | status = tpm_tis_status(chip); | ||
182 | if ((status & mask) == mask) | ||
183 | return 0; | ||
184 | } while (time_before(jiffies, stop)); | ||
185 | } | ||
186 | return -ETIME; | ||
187 | } | ||
188 | |||
189 | static int recv_data(struct tpm_chip *chip, u8 * buf, size_t count) | ||
190 | { | ||
191 | int size = 0, burstcnt; | ||
192 | while (size < count && | ||
193 | wait_for_stat(chip, | ||
194 | TPM_STS_DATA_AVAIL | TPM_STS_VALID, | ||
195 | chip->vendor.timeout_c, | ||
196 | &chip->vendor.read_queue) | ||
197 | == 0) { | ||
198 | burstcnt = get_burstcount(chip); | ||
199 | for (; burstcnt > 0 && size < count; burstcnt--) | ||
200 | buf[size++] = ioread8(chip->vendor.iobase + | ||
201 | TPM_DATA_FIFO(chip->vendor. | ||
202 | locality)); | ||
203 | } | ||
204 | return size; | ||
205 | } | ||
206 | |||
207 | static int tpm_tis_recv(struct tpm_chip *chip, u8 * buf, size_t count) | ||
208 | { | ||
209 | int size = 0; | ||
210 | int expected, status; | ||
211 | |||
212 | if (count < TPM_HEADER_SIZE) { | ||
213 | size = -EIO; | ||
214 | goto out; | ||
215 | } | ||
216 | |||
217 | /* read first 10 bytes, including tag, paramsize, and result */ | ||
218 | if ((size = | ||
219 | recv_data(chip, buf, TPM_HEADER_SIZE)) < TPM_HEADER_SIZE) { | ||
220 | dev_err(chip->dev, "Unable to read header\n"); | ||
221 | goto out; | ||
222 | } | ||
223 | |||
224 | expected = be32_to_cpu(*(__be32 *) (buf + 2)); | ||
225 | if (expected > count) { | ||
226 | size = -EIO; | ||
227 | goto out; | ||
228 | } | ||
229 | |||
230 | if ((size += | ||
231 | recv_data(chip, &buf[TPM_HEADER_SIZE], | ||
232 | expected - TPM_HEADER_SIZE)) < expected) { | ||
233 | dev_err(chip->dev, "Unable to read remainder of result\n"); | ||
234 | size = -ETIME; | ||
235 | goto out; | ||
236 | } | ||
237 | |||
238 | wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, | ||
239 | &chip->vendor.int_queue); | ||
240 | status = tpm_tis_status(chip); | ||
241 | if (status & TPM_STS_DATA_AVAIL) { /* retry? */ | ||
242 | dev_err(chip->dev, "Error left over data\n"); | ||
243 | size = -EIO; | ||
244 | goto out; | ||
245 | } | ||
246 | |||
247 | out: | ||
248 | tpm_tis_ready(chip); | ||
249 | release_locality(chip, chip->vendor.locality, 0); | ||
250 | return size; | ||
251 | } | ||
252 | |||
253 | /* | ||
254 | * If interrupts are used (signaled by an irq set in the vendor structure) | ||
255 | * tpm.c can skip polling for the data to be available as the interrupt is | ||
256 | * waited for here | ||
257 | */ | ||
258 | static int tpm_tis_send(struct tpm_chip *chip, u8 * buf, size_t len) | ||
259 | { | ||
260 | int rc, status, burstcnt; | ||
261 | size_t count = 0; | ||
262 | u32 ordinal; | ||
263 | |||
264 | if (request_locality(chip, 0) < 0) | ||
265 | return -EBUSY; | ||
266 | |||
267 | status = tpm_tis_status(chip); | ||
268 | if ((status & TPM_STS_COMMAND_READY) == 0) { | ||
269 | tpm_tis_ready(chip); | ||
270 | if (wait_for_stat | ||
271 | (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b, | ||
272 | &chip->vendor.int_queue) < 0) { | ||
273 | rc = -ETIME; | ||
274 | goto out_err; | ||
275 | } | ||
276 | } | ||
277 | |||
278 | while (count < len - 1) { | ||
279 | burstcnt = get_burstcount(chip); | ||
280 | for (; burstcnt > 0 && count < len - 1; burstcnt--) { | ||
281 | iowrite8(buf[count], chip->vendor.iobase + | ||
282 | TPM_DATA_FIFO(chip->vendor.locality)); | ||
283 | count++; | ||
284 | } | ||
285 | |||
286 | wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, | ||
287 | &chip->vendor.int_queue); | ||
288 | status = tpm_tis_status(chip); | ||
289 | if ((status & TPM_STS_DATA_EXPECT) == 0) { | ||
290 | rc = -EIO; | ||
291 | goto out_err; | ||
292 | } | ||
293 | } | ||
294 | |||
295 | /* write last byte */ | ||
296 | iowrite8(buf[count], | ||
297 | chip->vendor.iobase + | ||
298 | TPM_DATA_FIFO(chip->vendor.locality)); | ||
299 | wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, | ||
300 | &chip->vendor.int_queue); | ||
301 | status = tpm_tis_status(chip); | ||
302 | if ((status & TPM_STS_DATA_EXPECT) != 0) { | ||
303 | rc = -EIO; | ||
304 | goto out_err; | ||
305 | } | ||
306 | |||
307 | /* go and do it */ | ||
308 | iowrite8(TPM_STS_GO, | ||
309 | chip->vendor.iobase + TPM_STS(chip->vendor.locality)); | ||
310 | |||
311 | if (chip->vendor.irq) { | ||
312 | ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); | ||
313 | if (wait_for_stat | ||
314 | (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, | ||
315 | tpm_calc_ordinal_duration(chip, ordinal), | ||
316 | &chip->vendor.read_queue) < 0) { | ||
317 | rc = -ETIME; | ||
318 | goto out_err; | ||
319 | } | ||
320 | } | ||
321 | return len; | ||
322 | out_err: | ||
323 | tpm_tis_ready(chip); | ||
324 | release_locality(chip, chip->vendor.locality, 0); | ||
325 | return rc; | ||
326 | } | ||
327 | |||
328 | static struct file_operations tis_ops = { | ||
329 | .owner = THIS_MODULE, | ||
330 | .llseek = no_llseek, | ||
331 | .open = tpm_open, | ||
332 | .read = tpm_read, | ||
333 | .write = tpm_write, | ||
334 | .release = tpm_release, | ||
335 | }; | ||
336 | |||
337 | static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL); | ||
338 | static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL); | ||
339 | static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL); | ||
340 | static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL); | ||
341 | static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL); | ||
342 | static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, | ||
343 | NULL); | ||
344 | static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL); | ||
345 | static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel); | ||
346 | |||
347 | static struct attribute *tis_attrs[] = { | ||
348 | &dev_attr_pubek.attr, | ||
349 | &dev_attr_pcrs.attr, | ||
350 | &dev_attr_enabled.attr, | ||
351 | &dev_attr_active.attr, | ||
352 | &dev_attr_owned.attr, | ||
353 | &dev_attr_temp_deactivated.attr, | ||
354 | &dev_attr_caps.attr, | ||
355 | &dev_attr_cancel.attr, NULL, | ||
356 | }; | ||
357 | |||
358 | static struct attribute_group tis_attr_grp = { | ||
359 | .attrs = tis_attrs | ||
360 | }; | ||
361 | |||
362 | static struct tpm_vendor_specific tpm_tis = { | ||
363 | .status = tpm_tis_status, | ||
364 | .recv = tpm_tis_recv, | ||
365 | .send = tpm_tis_send, | ||
366 | .cancel = tpm_tis_ready, | ||
367 | .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, | ||
368 | .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, | ||
369 | .req_canceled = TPM_STS_COMMAND_READY, | ||
370 | .attr_group = &tis_attr_grp, | ||
371 | .miscdev = { | ||
372 | .fops = &tis_ops,}, | ||
373 | }; | ||
374 | |||
375 | static irqreturn_t tis_int_probe(int irq, void *dev_id, struct pt_regs | ||
376 | *regs) | ||
377 | { | ||
378 | struct tpm_chip *chip = (struct tpm_chip *) dev_id; | ||
379 | u32 interrupt; | ||
380 | |||
381 | interrupt = ioread32(chip->vendor.iobase + | ||
382 | TPM_INT_STATUS(chip->vendor.locality)); | ||
383 | |||
384 | if (interrupt == 0) | ||
385 | return IRQ_NONE; | ||
386 | |||
387 | chip->vendor.irq = irq; | ||
388 | |||
389 | /* Clear interrupts handled with TPM_EOI */ | ||
390 | iowrite32(interrupt, | ||
391 | chip->vendor.iobase + | ||
392 | TPM_INT_STATUS(chip->vendor.locality)); | ||
393 | return IRQ_HANDLED; | ||
394 | } | ||
395 | |||
396 | static irqreturn_t tis_int_handler(int irq, void *dev_id, struct pt_regs | ||
397 | *regs) | ||
398 | { | ||
399 | struct tpm_chip *chip = (struct tpm_chip *) dev_id; | ||
400 | u32 interrupt; | ||
401 | int i; | ||
402 | |||
403 | interrupt = ioread32(chip->vendor.iobase + | ||
404 | TPM_INT_STATUS(chip->vendor.locality)); | ||
405 | |||
406 | if (interrupt == 0) | ||
407 | return IRQ_NONE; | ||
408 | |||
409 | if (interrupt & TPM_INTF_DATA_AVAIL_INT) | ||
410 | wake_up_interruptible(&chip->vendor.read_queue); | ||
411 | if (interrupt & TPM_INTF_LOCALITY_CHANGE_INT) | ||
412 | for (i = 0; i < 5; i++) | ||
413 | if (check_locality(chip, i) >= 0) | ||
414 | break; | ||
415 | if (interrupt & | ||
416 | (TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_STS_VALID_INT | | ||
417 | TPM_INTF_CMD_READY_INT)) | ||
418 | wake_up_interruptible(&chip->vendor.int_queue); | ||
419 | |||
420 | /* Clear interrupts handled with TPM_EOI */ | ||
421 | iowrite32(interrupt, | ||
422 | chip->vendor.iobase + | ||
423 | TPM_INT_STATUS(chip->vendor.locality)); | ||
424 | return IRQ_HANDLED; | ||
425 | } | ||
426 | |||
427 | static int __devinit tpm_tis_pnp_init(struct pnp_dev | ||
428 | *pnp_dev, const struct | ||
429 | pnp_device_id | ||
430 | *pnp_id) | ||
431 | { | ||
432 | u32 vendor, intfcaps, intmask; | ||
433 | int rc, i; | ||
434 | unsigned long start, len; | ||
435 | struct tpm_chip *chip; | ||
436 | |||
437 | start = pnp_mem_start(pnp_dev, 0); | ||
438 | len = pnp_mem_len(pnp_dev, 0); | ||
439 | |||
440 | if (!(chip = tpm_register_hardware(&pnp_dev->dev, &tpm_tis))) | ||
441 | return -ENODEV; | ||
442 | |||
443 | chip->vendor.iobase = ioremap(start, len); | ||
444 | if (!chip->vendor.iobase) { | ||
445 | rc = -EIO; | ||
446 | goto out_err; | ||
447 | } | ||
448 | |||
449 | vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0)); | ||
450 | if ((vendor & 0xFFFF) == 0xFFFF) { | ||
451 | rc = -ENODEV; | ||
452 | goto out_err; | ||
453 | } | ||
454 | |||
455 | /* Default timeouts */ | ||
456 | chip->vendor.timeout_a = 750; /* ms */ | ||
457 | chip->vendor.timeout_b = 2000; /* 2 sec */ | ||
458 | chip->vendor.timeout_c = 750; /* ms */ | ||
459 | chip->vendor.timeout_d = 750; /* ms */ | ||
460 | |||
461 | dev_info(&pnp_dev->dev, | ||
462 | "1.2 TPM (device-id 0x%X, rev-id %d)\n", | ||
463 | vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0))); | ||
464 | |||
465 | /* Figure out the capabilities */ | ||
466 | intfcaps = | ||
467 | ioread32(chip->vendor.iobase + | ||
468 | TPM_INTF_CAPS(chip->vendor.locality)); | ||
469 | dev_dbg(&pnp_dev->dev, "TPM interface capabilities (0x%x):\n", | ||
470 | intfcaps); | ||
471 | if (intfcaps & TPM_INTF_BURST_COUNT_STATIC) | ||
472 | dev_dbg(&pnp_dev->dev, "\tBurst Count Static\n"); | ||
473 | if (intfcaps & TPM_INTF_CMD_READY_INT) | ||
474 | dev_dbg(&pnp_dev->dev, "\tCommand Ready Int Support\n"); | ||
475 | if (intfcaps & TPM_INTF_INT_EDGE_FALLING) | ||
476 | dev_dbg(&pnp_dev->dev, "\tInterrupt Edge Falling\n"); | ||
477 | if (intfcaps & TPM_INTF_INT_EDGE_RISING) | ||
478 | dev_dbg(&pnp_dev->dev, "\tInterrupt Edge Rising\n"); | ||
479 | if (intfcaps & TPM_INTF_INT_LEVEL_LOW) | ||
480 | dev_dbg(&pnp_dev->dev, "\tInterrupt Level Low\n"); | ||
481 | if (intfcaps & TPM_INTF_INT_LEVEL_HIGH) | ||
482 | dev_dbg(&pnp_dev->dev, "\tInterrupt Level High\n"); | ||
483 | if (intfcaps & TPM_INTF_LOCALITY_CHANGE_INT) | ||
484 | dev_dbg(&pnp_dev->dev, "\tLocality Change Int Support\n"); | ||
485 | if (intfcaps & TPM_INTF_STS_VALID_INT) | ||
486 | dev_dbg(&pnp_dev->dev, "\tSts Valid Int Support\n"); | ||
487 | if (intfcaps & TPM_INTF_DATA_AVAIL_INT) | ||
488 | dev_dbg(&pnp_dev->dev, "\tData Avail Int Support\n"); | ||
489 | |||
490 | if (request_locality(chip, 0) != 0) { | ||
491 | rc = -ENODEV; | ||
492 | goto out_err; | ||
493 | } | ||
494 | |||
495 | /* INTERRUPT Setup */ | ||
496 | init_waitqueue_head(&chip->vendor.read_queue); | ||
497 | init_waitqueue_head(&chip->vendor.int_queue); | ||
498 | |||
499 | intmask = | ||
500 | ioread32(chip->vendor.iobase + | ||
501 | TPM_INT_ENABLE(chip->vendor.locality)); | ||
502 | |||
503 | intmask |= TPM_INTF_CMD_READY_INT | ||
504 | | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT | ||
505 | | TPM_INTF_STS_VALID_INT; | ||
506 | |||
507 | iowrite32(intmask, | ||
508 | chip->vendor.iobase + | ||
509 | TPM_INT_ENABLE(chip->vendor.locality)); | ||
510 | |||
511 | chip->vendor.irq = | ||
512 | ioread8(chip->vendor.iobase + | ||
513 | TPM_INT_VECTOR(chip->vendor.locality)); | ||
514 | |||
515 | for (i = 3; i < 16 && chip->vendor.irq == 0; i++) { | ||
516 | iowrite8(i, | ||
517 | chip->vendor.iobase + | ||
518 | TPM_INT_VECTOR(chip->vendor.locality)); | ||
519 | if (request_irq | ||
520 | (i, tis_int_probe, SA_SHIRQ, | ||
521 | chip->vendor.miscdev.name, chip) != 0) { | ||
522 | dev_info(chip->dev, | ||
523 | "Unable to request irq: %d for probe\n", | ||
524 | i); | ||
525 | continue; | ||
526 | } | ||
527 | |||
528 | /* Clear all existing */ | ||
529 | iowrite32(ioread32 | ||
530 | (chip->vendor.iobase + | ||
531 | TPM_INT_STATUS(chip->vendor.locality)), | ||
532 | chip->vendor.iobase + | ||
533 | TPM_INT_STATUS(chip->vendor.locality)); | ||
534 | |||
535 | /* Turn on */ | ||
536 | iowrite32(intmask | TPM_GLOBAL_INT_ENABLE, | ||
537 | chip->vendor.iobase + | ||
538 | TPM_INT_ENABLE(chip->vendor.locality)); | ||
539 | |||
540 | /* Generate Interrupts */ | ||
541 | tpm_gen_interrupt(chip); | ||
542 | |||
543 | /* Turn off */ | ||
544 | iowrite32(intmask, | ||
545 | chip->vendor.iobase + | ||
546 | TPM_INT_ENABLE(chip->vendor.locality)); | ||
547 | free_irq(i, chip); | ||
548 | } | ||
549 | if (chip->vendor.irq) { | ||
550 | iowrite8(chip->vendor.irq, | ||
551 | chip->vendor.iobase + | ||
552 | TPM_INT_VECTOR(chip->vendor.locality)); | ||
553 | if (request_irq | ||
554 | (chip->vendor.irq, tis_int_handler, SA_SHIRQ, | ||
555 | chip->vendor.miscdev.name, chip) != 0) { | ||
556 | dev_info(chip->dev, | ||
557 | "Unable to request irq: %d for use\n", i); | ||
558 | chip->vendor.irq = 0; | ||
559 | } else { | ||
560 | /* Clear all existing */ | ||
561 | iowrite32(ioread32 | ||
562 | (chip->vendor.iobase + | ||
563 | TPM_INT_STATUS(chip->vendor.locality)), | ||
564 | chip->vendor.iobase + | ||
565 | TPM_INT_STATUS(chip->vendor.locality)); | ||
566 | |||
567 | /* Turn on */ | ||
568 | iowrite32(intmask | TPM_GLOBAL_INT_ENABLE, | ||
569 | chip->vendor.iobase + | ||
570 | TPM_INT_ENABLE(chip->vendor.locality)); | ||
571 | } | ||
572 | } | ||
573 | |||
574 | INIT_LIST_HEAD(&chip->vendor.list); | ||
575 | spin_lock(&tis_lock); | ||
576 | list_add(&chip->vendor.list, &tis_chips); | ||
577 | spin_unlock(&tis_lock); | ||
578 | |||
579 | tpm_get_timeouts(chip); | ||
580 | tpm_continue_selftest(chip); | ||
581 | |||
582 | return 0; | ||
583 | out_err: | ||
584 | if (chip->vendor.iobase) | ||
585 | iounmap(chip->vendor.iobase); | ||
586 | tpm_remove_hardware(chip->dev); | ||
587 | return rc; | ||
588 | } | ||
589 | |||
590 | static int tpm_tis_pnp_suspend(struct pnp_dev *dev, pm_message_t msg) | ||
591 | { | ||
592 | return tpm_pm_suspend(&dev->dev, msg); | ||
593 | } | ||
594 | |||
595 | static int tpm_tis_pnp_resume(struct pnp_dev *dev) | ||
596 | { | ||
597 | return tpm_pm_resume(&dev->dev); | ||
598 | } | ||
599 | |||
600 | static struct pnp_device_id tpm_pnp_tbl[] __devinitdata = { | ||
601 | {"PNP0C31", 0}, /* TPM */ | ||
602 | {"", 0} | ||
603 | }; | ||
604 | |||
605 | static struct pnp_driver tis_pnp_driver = { | ||
606 | .name = "tpm_tis", | ||
607 | .id_table = tpm_pnp_tbl, | ||
608 | .probe = tpm_tis_pnp_init, | ||
609 | .suspend = tpm_tis_pnp_suspend, | ||
610 | .resume = tpm_tis_pnp_resume, | ||
611 | }; | ||
612 | |||
613 | static int __init init_tis(void) | ||
614 | { | ||
615 | return pnp_register_driver(&tis_pnp_driver); | ||
616 | } | ||
617 | |||
618 | static void __exit cleanup_tis(void) | ||
619 | { | ||
620 | struct tpm_vendor_specific *i, *j; | ||
621 | struct tpm_chip *chip; | ||
622 | spin_lock(&tis_lock); | ||
623 | list_for_each_entry_safe(i, j, &tis_chips, list) { | ||
624 | chip = to_tpm_chip(i); | ||
625 | iowrite32(~TPM_GLOBAL_INT_ENABLE & | ||
626 | ioread32(chip->vendor.iobase + | ||
627 | TPM_INT_ENABLE(chip->vendor. | ||
628 | locality)), | ||
629 | chip->vendor.iobase + | ||
630 | TPM_INT_ENABLE(chip->vendor.locality)); | ||
631 | release_locality(chip, chip->vendor.locality, 1); | ||
632 | if (chip->vendor.irq) | ||
633 | free_irq(chip->vendor.irq, chip); | ||
634 | iounmap(i->iobase); | ||
635 | list_del(&i->list); | ||
636 | tpm_remove_hardware(chip->dev); | ||
637 | } | ||
638 | spin_unlock(&tis_lock); | ||
639 | pnp_unregister_driver(&tis_pnp_driver); | ||
640 | } | ||
641 | |||
642 | module_init(init_tis); | ||
643 | module_exit(cleanup_tis); | ||
644 | MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)"); | ||
645 | MODULE_DESCRIPTION("TPM Driver"); | ||
646 | MODULE_VERSION("2.0"); | ||
647 | MODULE_LICENSE("GPL"); | ||