diff options
author | Christophe Ricard <christophe.ricard@gmail.com> | 2014-12-01 13:32:58 -0500 |
---|---|---|
committer | Peter Huewe <peterhuewe@gmx.de> | 2015-01-17 08:00:08 -0500 |
commit | c3804b8cdbd2abb50cf87318b13660dff46b7fe8 (patch) | |
tree | 46d42a57c7b6b7e1a9590ddc5241ba56649a92ce | |
parent | 8dcd19874f31b7a806a2a2b129e94882918c3aaf (diff) |
tpm/tpm_i2c_stm_st33: Interrupt management improvement
Improve the irq management by using a new function wait_for_stat.
Instead of using a completion struct, we rely on the waitqueue read_queue
and int_queue from chip->vendor field.
Reviewed-by: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
Signed-off-by: Christophe Ricard <christophe-h.ricard@st.com>
Signed-off-by: Peter Huewe <peterhuewe@gmx.de>
-rw-r--r-- | drivers/char/tpm/tpm_i2c_stm_st33.c | 197 |
1 files changed, 109 insertions, 88 deletions
diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.c b/drivers/char/tpm/tpm_i2c_stm_st33.c index a444cd3cb17d..eb0244a2ec9d 100644 --- a/drivers/char/tpm/tpm_i2c_stm_st33.c +++ b/drivers/char/tpm/tpm_i2c_stm_st33.c | |||
@@ -38,6 +38,7 @@ | |||
38 | #include <linux/kernel.h> | 38 | #include <linux/kernel.h> |
39 | #include <linux/delay.h> | 39 | #include <linux/delay.h> |
40 | #include <linux/wait.h> | 40 | #include <linux/wait.h> |
41 | #include <linux/freezer.h> | ||
41 | #include <linux/string.h> | 42 | #include <linux/string.h> |
42 | #include <linux/interrupt.h> | 43 | #include <linux/interrupt.h> |
43 | #include <linux/sysfs.h> | 44 | #include <linux/sysfs.h> |
@@ -102,9 +103,9 @@ enum tis_defaults { | |||
102 | 103 | ||
103 | struct tpm_stm_dev { | 104 | struct tpm_stm_dev { |
104 | struct i2c_client *client; | 105 | struct i2c_client *client; |
105 | struct completion irq_detection; | ||
106 | struct tpm_chip *chip; | 106 | struct tpm_chip *chip; |
107 | u8 buf[TPM_BUFSIZE + 1]; | 107 | u8 buf[TPM_BUFSIZE + 1]; |
108 | u32 intrs; | ||
108 | int io_lpcpd; | 109 | int io_lpcpd; |
109 | }; | 110 | }; |
110 | 111 | ||
@@ -174,61 +175,18 @@ static int read8_reg(struct tpm_stm_dev *tpm_dev, u8 tpm_register, | |||
174 | * clear_interruption | 175 | * clear_interruption |
175 | * clear the TPM interrupt register. | 176 | * clear the TPM interrupt register. |
176 | * @param: tpm, the chip description | 177 | * @param: tpm, the chip description |
178 | * @return: the TPM_INT_STATUS value | ||
177 | */ | 179 | */ |
178 | static void clear_interruption(struct tpm_stm_dev *tpm_dev) | 180 | static u8 clear_interruption(struct tpm_stm_dev *tpm_dev) |
179 | { | 181 | { |
180 | u8 interrupt; | 182 | u8 interrupt; |
181 | 183 | ||
182 | I2C_READ_DATA(tpm_dev, TPM_INT_STATUS, &interrupt, 1); | 184 | I2C_READ_DATA(tpm_dev, TPM_INT_STATUS, &interrupt, 1); |
183 | I2C_WRITE_DATA(tpm_dev, TPM_INT_STATUS, &interrupt, 1); | 185 | I2C_WRITE_DATA(tpm_dev, TPM_INT_STATUS, &interrupt, 1); |
184 | I2C_READ_DATA(tpm_dev, TPM_INT_STATUS, &interrupt, 1); | 186 | return interrupt; |
185 | } /* clear_interruption() */ | 187 | } /* clear_interruption() */ |
186 | 188 | ||
187 | /* | 189 | /* |
188 | * _wait_for_interrupt_serirq_timeout | ||
189 | * @param: tpm, the chip description | ||
190 | * @param: timeout, the timeout of the interrupt | ||
191 | * @return: the status of the interruption. | ||
192 | */ | ||
193 | static long _wait_for_interrupt_serirq_timeout(struct tpm_chip *chip, | ||
194 | unsigned long timeout) | ||
195 | { | ||
196 | long status; | ||
197 | struct i2c_client *client; | ||
198 | struct tpm_stm_dev *tpm_dev; | ||
199 | |||
200 | tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); | ||
201 | client = tpm_dev->client; | ||
202 | |||
203 | status = wait_for_completion_interruptible_timeout( | ||
204 | &tpm_dev->irq_detection, | ||
205 | timeout); | ||
206 | if (status > 0) | ||
207 | enable_irq(client->irq); | ||
208 | |||
209 | return status; | ||
210 | } /* wait_for_interrupt_serirq_timeout() */ | ||
211 | |||
212 | static int wait_for_serirq_timeout(struct tpm_chip *chip, bool condition, | ||
213 | unsigned long timeout) | ||
214 | { | ||
215 | int status = 2; | ||
216 | struct tpm_stm_dev *tpm_dev; | ||
217 | |||
218 | tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); | ||
219 | |||
220 | status = _wait_for_interrupt_serirq_timeout(chip, timeout); | ||
221 | if (!status) { | ||
222 | status = -EBUSY; | ||
223 | } else { | ||
224 | clear_interruption(tpm_dev); | ||
225 | if (condition) | ||
226 | status = 1; | ||
227 | } | ||
228 | return status; | ||
229 | } /* wait_for_serirq_timeout() */ | ||
230 | |||
231 | /* | ||
232 | * tpm_stm_i2c_cancel, cancel is not implemented. | 190 | * tpm_stm_i2c_cancel, cancel is not implemented. |
233 | * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h | 191 | * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h |
234 | */ | 192 | */ |
@@ -241,8 +199,6 @@ static void tpm_stm_i2c_cancel(struct tpm_chip *chip) | |||
241 | 199 | ||
242 | data = TPM_STS_COMMAND_READY; | 200 | data = TPM_STS_COMMAND_READY; |
243 | I2C_WRITE_DATA(tpm_dev, TPM_STS, &data, 1); | 201 | I2C_WRITE_DATA(tpm_dev, TPM_STS, &data, 1); |
244 | if (chip->vendor.irq) | ||
245 | wait_for_serirq_timeout(chip, 1, chip->vendor.timeout_a); | ||
246 | } /* tpm_stm_i2c_cancel() */ | 202 | } /* tpm_stm_i2c_cancel() */ |
247 | 203 | ||
248 | /* | 204 | /* |
@@ -297,30 +253,24 @@ static int request_locality(struct tpm_chip *chip) | |||
297 | struct tpm_stm_dev *tpm_dev; | 253 | struct tpm_stm_dev *tpm_dev; |
298 | u8 data; | 254 | u8 data; |
299 | 255 | ||
300 | tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); | ||
301 | |||
302 | if (check_locality(chip) == chip->vendor.locality) | 256 | if (check_locality(chip) == chip->vendor.locality) |
303 | return chip->vendor.locality; | 257 | return chip->vendor.locality; |
304 | 258 | ||
259 | tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); | ||
260 | |||
305 | data = TPM_ACCESS_REQUEST_USE; | 261 | data = TPM_ACCESS_REQUEST_USE; |
306 | ret = I2C_WRITE_DATA(tpm_dev, TPM_ACCESS, &data, 1); | 262 | ret = I2C_WRITE_DATA(tpm_dev, TPM_ACCESS, &data, 1); |
307 | if (ret < 0) | 263 | if (ret < 0) |
308 | goto end; | 264 | goto end; |
309 | 265 | ||
310 | if (chip->vendor.irq) { | 266 | stop = jiffies + chip->vendor.timeout_a; |
311 | ret = wait_for_serirq_timeout(chip, (check_locality | 267 | |
312 | (chip) >= 0), | 268 | /* Request locality is usually effective after the request */ |
313 | chip->vendor.timeout_a); | 269 | do { |
314 | if (ret > 0) | 270 | if (check_locality(chip) >= 0) |
315 | return chip->vendor.locality; | 271 | return chip->vendor.locality; |
316 | } else { | 272 | msleep(TPM_TIMEOUT); |
317 | stop = jiffies + chip->vendor.timeout_a; | 273 | } while (time_before(jiffies, stop)); |
318 | do { | ||
319 | if (check_locality(chip) >= 0) | ||
320 | return chip->vendor.locality; | ||
321 | msleep(TPM_TIMEOUT); | ||
322 | } while (time_before(jiffies, stop)); | ||
323 | } | ||
324 | ret = -EACCES; | 274 | ret = -EACCES; |
325 | end: | 275 | end: |
326 | return ret; | 276 | return ret; |
@@ -378,36 +328,107 @@ end: | |||
378 | return -EBUSY; | 328 | return -EBUSY; |
379 | } /* get_burstcount() */ | 329 | } /* get_burstcount() */ |
380 | 330 | ||
331 | static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask, | ||
332 | bool check_cancel, bool *canceled) | ||
333 | { | ||
334 | u8 status = chip->ops->status(chip); | ||
335 | |||
336 | *canceled = false; | ||
337 | if ((status & mask) == mask) | ||
338 | return true; | ||
339 | if (check_cancel && chip->ops->req_canceled(chip, status)) { | ||
340 | *canceled = true; | ||
341 | return true; | ||
342 | } | ||
343 | return false; | ||
344 | } | ||
345 | |||
346 | /* | ||
347 | * interrupt_to_status | ||
348 | * @param: irq_mask, the irq mask value to wait | ||
349 | * @return: the corresponding tpm_sts value | ||
350 | */ | ||
351 | static u8 interrupt_to_status(u8 irq_mask) | ||
352 | { | ||
353 | u8 status = 0; | ||
354 | |||
355 | if ((irq_mask & TPM_INTF_STS_VALID_INT) == TPM_INTF_STS_VALID_INT) | ||
356 | status |= TPM_STS_VALID; | ||
357 | if ((irq_mask & TPM_INTF_DATA_AVAIL_INT) == TPM_INTF_DATA_AVAIL_INT) | ||
358 | status |= TPM_STS_DATA_AVAIL; | ||
359 | if ((irq_mask & TPM_INTF_CMD_READY_INT) == TPM_INTF_CMD_READY_INT) | ||
360 | status |= TPM_STS_COMMAND_READY; | ||
361 | |||
362 | return status; | ||
363 | } /* status_to_interrupt() */ | ||
364 | |||
381 | /* | 365 | /* |
382 | * wait_for_stat wait for a TPM_STS value | 366 | * wait_for_stat wait for a TPM_STS value |
383 | * @param: chip, the tpm chip description | 367 | * @param: chip, the tpm chip description |
384 | * @param: mask, the value mask to wait | 368 | * @param: mask, the value mask to wait |
385 | * @param: timeout, the timeout | 369 | * @param: timeout, the timeout |
386 | * @param: queue, the wait queue. | 370 | * @param: queue, the wait queue. |
371 | * @param: check_cancel, does the command can be cancelled ? | ||
387 | * @return: the tpm status, 0 if success, -ETIME if timeout is reached. | 372 | * @return: the tpm status, 0 if success, -ETIME if timeout is reached. |
388 | */ | 373 | */ |
389 | static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, | 374 | static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, |
390 | wait_queue_head_t *queue) | 375 | wait_queue_head_t *queue, bool check_cancel) |
391 | { | 376 | { |
392 | unsigned long stop; | 377 | unsigned long stop; |
393 | long ret; | 378 | int r; |
394 | u8 status; | 379 | bool canceled = false; |
380 | bool condition; | ||
381 | u32 cur_intrs; | ||
382 | u8 interrupt, status; | ||
383 | struct tpm_stm_dev *tpm_dev; | ||
384 | |||
385 | tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); | ||
386 | |||
387 | /* check current status */ | ||
388 | status = tpm_stm_i2c_status(chip); | ||
389 | if ((status & mask) == mask) | ||
390 | return 0; | ||
391 | |||
392 | stop = jiffies + timeout; | ||
395 | 393 | ||
396 | if (chip->vendor.irq) { | 394 | if (chip->vendor.irq) { |
397 | ret = wait_for_serirq_timeout(chip, ((tpm_stm_i2c_status | 395 | cur_intrs = tpm_dev->intrs; |
398 | (chip) & mask) == | 396 | interrupt = clear_interruption(tpm_dev); |
399 | mask), timeout); | 397 | enable_irq(chip->vendor.irq); |
400 | if (ret > 0) | 398 | |
399 | again: | ||
400 | timeout = stop - jiffies; | ||
401 | if ((long) timeout <= 0) | ||
402 | return -1; | ||
403 | |||
404 | r = wait_event_interruptible_timeout(*queue, | ||
405 | cur_intrs != tpm_dev->intrs, timeout); | ||
406 | |||
407 | interrupt |= clear_interruption(tpm_dev); | ||
408 | status = interrupt_to_status(interrupt); | ||
409 | condition = wait_for_tpm_stat_cond(chip, mask, | ||
410 | check_cancel, &canceled); | ||
411 | |||
412 | if (r >= 0 && condition) { | ||
413 | if (canceled) | ||
414 | return -ECANCELED; | ||
401 | return 0; | 415 | return 0; |
416 | } | ||
417 | if (r == -ERESTARTSYS && freezing(current)) { | ||
418 | clear_thread_flag(TIF_SIGPENDING); | ||
419 | goto again; | ||
420 | } | ||
421 | disable_irq_nosync(chip->vendor.irq); | ||
422 | |||
402 | } else { | 423 | } else { |
403 | stop = jiffies + timeout; | ||
404 | do { | 424 | do { |
405 | msleep(TPM_TIMEOUT); | 425 | msleep(TPM_TIMEOUT); |
406 | status = tpm_stm_i2c_status(chip); | 426 | status = chip->ops->status(chip); |
407 | if ((status & mask) == mask) | 427 | if ((status & mask) == mask) |
408 | return 0; | 428 | return 0; |
409 | } while (time_before(jiffies, stop)); | 429 | } while (time_before(jiffies, stop)); |
410 | } | 430 | } |
431 | |||
411 | return -ETIME; | 432 | return -ETIME; |
412 | } /* wait_for_stat() */ | 433 | } /* wait_for_stat() */ |
413 | 434 | ||
@@ -429,7 +450,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) | |||
429 | wait_for_stat(chip, | 450 | wait_for_stat(chip, |
430 | TPM_STS_DATA_AVAIL | TPM_STS_VALID, | 451 | TPM_STS_DATA_AVAIL | TPM_STS_VALID, |
431 | chip->vendor.timeout_c, | 452 | chip->vendor.timeout_c, |
432 | &chip->vendor.read_queue) == 0) { | 453 | &chip->vendor.read_queue, true) == 0) { |
433 | burstcnt = get_burstcount(chip); | 454 | burstcnt = get_burstcount(chip); |
434 | if (burstcnt < 0) | 455 | if (burstcnt < 0) |
435 | return burstcnt; | 456 | return burstcnt; |
@@ -449,15 +470,14 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) | |||
449 | static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id) | 470 | static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id) |
450 | { | 471 | { |
451 | struct tpm_chip *chip = dev_id; | 472 | struct tpm_chip *chip = dev_id; |
452 | struct i2c_client *client; | ||
453 | struct tpm_stm_dev *tpm_dev; | 473 | struct tpm_stm_dev *tpm_dev; |
454 | 474 | ||
455 | disable_irq_nosync(irq); | ||
456 | |||
457 | tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); | 475 | tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); |
458 | client = tpm_dev->client; | ||
459 | 476 | ||
460 | complete(&tpm_dev->irq_detection); | 477 | tpm_dev->intrs++; |
478 | wake_up_interruptible(&chip->vendor.read_queue); | ||
479 | disable_irq_nosync(chip->vendor.irq); | ||
480 | |||
461 | return IRQ_HANDLED; | 481 | return IRQ_HANDLED; |
462 | } /* tpm_ioserirq_handler() */ | 482 | } /* tpm_ioserirq_handler() */ |
463 | 483 | ||
@@ -500,7 +520,7 @@ static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf, | |||
500 | tpm_stm_i2c_cancel(chip); | 520 | tpm_stm_i2c_cancel(chip); |
501 | if (wait_for_stat | 521 | if (wait_for_stat |
502 | (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b, | 522 | (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b, |
503 | &chip->vendor.int_queue) < 0) { | 523 | &chip->vendor.read_queue, false) < 0) { |
504 | ret = -ETIME; | 524 | ret = -ETIME; |
505 | goto out_err; | 525 | goto out_err; |
506 | } | 526 | } |
@@ -743,7 +763,10 @@ tpm_stm_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) | |||
743 | chip->vendor.locality = LOCALITY0; | 763 | chip->vendor.locality = LOCALITY0; |
744 | 764 | ||
745 | if (client->irq) { | 765 | if (client->irq) { |
746 | init_completion(&tpm_dev->irq_detection); | 766 | /* INTERRUPT Setup */ |
767 | init_waitqueue_head(&chip->vendor.read_queue); | ||
768 | tpm_dev->intrs = 0; | ||
769 | |||
747 | if (request_locality(chip) != LOCALITY0) { | 770 | if (request_locality(chip) != LOCALITY0) { |
748 | ret = -ENODEV; | 771 | ret = -ENODEV; |
749 | goto _tpm_clean_answer; | 772 | goto _tpm_clean_answer; |
@@ -765,9 +788,6 @@ tpm_stm_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) | |||
765 | goto _tpm_clean_answer; | 788 | goto _tpm_clean_answer; |
766 | 789 | ||
767 | intmask |= TPM_INTF_CMD_READY_INT | 790 | intmask |= TPM_INTF_CMD_READY_INT |
768 | | TPM_INTF_FIFO_AVALAIBLE_INT | ||
769 | | TPM_INTF_WAKE_UP_READY_INT | ||
770 | | TPM_INTF_LOCALITY_CHANGE_INT | ||
771 | | TPM_INTF_STS_VALID_INT | 791 | | TPM_INTF_STS_VALID_INT |
772 | | TPM_INTF_DATA_AVAIL_INT; | 792 | | TPM_INTF_DATA_AVAIL_INT; |
773 | 793 | ||
@@ -787,6 +807,8 @@ tpm_stm_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) | |||
787 | 807 | ||
788 | chip->vendor.irq = client->irq; | 808 | chip->vendor.irq = client->irq; |
789 | 809 | ||
810 | disable_irq_nosync(chip->vendor.irq); | ||
811 | |||
790 | tpm_gen_interrupt(chip); | 812 | tpm_gen_interrupt(chip); |
791 | } | 813 | } |
792 | 814 | ||
@@ -852,10 +874,9 @@ static int tpm_stm_i2c_pm_resume(struct device *dev) | |||
852 | 874 | ||
853 | if (gpio_is_valid(pin_infos->io_lpcpd)) { | 875 | if (gpio_is_valid(pin_infos->io_lpcpd)) { |
854 | gpio_set_value(pin_infos->io_lpcpd, 1); | 876 | gpio_set_value(pin_infos->io_lpcpd, 1); |
855 | ret = wait_for_serirq_timeout(chip, | 877 | ret = wait_for_stat(chip, |
856 | (chip->ops->status(chip) & | 878 | TPM_STS_VALID, chip->vendor.timeout_b, |
857 | TPM_STS_VALID) == TPM_STS_VALID, | 879 | &chip->vendor.read_queue, false); |
858 | chip->vendor.timeout_b); | ||
859 | } else { | 880 | } else { |
860 | ret = tpm_pm_resume(dev); | 881 | ret = tpm_pm_resume(dev); |
861 | if (!ret) | 882 | if (!ret) |