@@ -39,6 +39,7 @@
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/wait.h>
+#include <linux/freezer.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
@@ -106,7 +107,6 @@ enum tis_defaults {
struct tpm_stm_dev {
struct i2c_client *client;
- struct completion irq_detection;
struct mutex lock;
struct tpm_chip *chip;
u8 buf[TPM_BUFSIZE + 1];
@@ -181,58 +181,16 @@ static int read8_reg(struct tpm_stm_dev *tpm_dev, u8 tpm_register,
* clear the TPM interrupt register.
* @param: tpm, the chip description
*/
-static void clear_interruption(struct tpm_stm_dev *tpm_dev)
+static u8 clear_interruption(struct tpm_stm_dev *tpm_dev)
{
u8 interrupt;
- I2C_READ_DATA(client, TPM_INT_STATUS, &interrupt, 1);
- I2C_WRITE_DATA(client, TPM_INT_STATUS, &interrupt, 1);
- I2C_READ_DATA(client, TPM_INT_STATUS, &interrupt, 1);
-} /* clear_interruption() */
-
-/*
- * _wait_for_interrupt_serirq_timeout
- * @param: tpm, the chip description
- * @param: timeout, the timeout of the interrupt
- * @return: the status of the interruption.
- */
-static long _wait_for_interrupt_serirq_timeout(struct tpm_chip *chip,
- unsigned long timeout)
-{
- long status;
- struct i2c_client *client;
- struct tpm_stm_dev *tpm_dev;
-
- tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip);
- client = tpm_dev->client;
-
- status = wait_for_completion_interruptible_timeout(
- &tpm_dev->irq_detection,
- timeout);
- if (status > 0)
- enable_irq(client->irq);
-
- return status;
-} /* wait_for_interrupt_serirq_timeout() */
+ I2C_READ_DATA(tpm_dev, TPM_INT_STATUS, &interrupt, 1);
+ I2C_WRITE_DATA(tpm_dev, TPM_INT_STATUS, &interrupt, 1);
+ I2C_READ_DATA(tpm_dev, TPM_INT_STATUS, &interrupt, 1);
-static int wait_for_serirq_timeout(struct tpm_chip *chip, bool condition,
- unsigned long timeout)
-{
- int status = 2;
- struct tpm_stm_dev *tpm_dev;
-
- tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip);
-
- status = _wait_for_interrupt_serirq_timeout(chip, timeout);
- if (!status) {
- status = -EBUSY;
- } else {
- clear_interruption(tpm_dev);
- if (condition)
- status = 1;
- }
- return status;
-} /* wait_for_serirq_timeout() */
+ return interrupt;
+} /* clear_interruption() */
/*
* tpm_stm_i2c_cancel, cancel is not implemented.
@@ -247,8 +205,6 @@ static void tpm_stm_i2c_cancel(struct tpm_chip *chip)
data = TPM_STS_COMMAND_READY;
I2C_WRITE_DATA(tpm_dev, TPM_STS, &data, 1);
- if (chip->vendor.irq)
- wait_for_serirq_timeout(chip, 1, chip->vendor.timeout_a);
} /* tpm_stm_i2c_cancel() */
/*
@@ -298,27 +254,37 @@ static int check_locality(struct tpm_chip *chip)
*/
static int request_locality(struct tpm_chip *chip)
{
- unsigned long stop;
+ unsigned long stop, timeout;
long r;
struct tpm_stm_dev *tpm_dev;
u8 data;
- tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip);
-
if (check_locality(chip) == chip->vendor.locality)
return chip->vendor.locality;
+ tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip);
+
data = TPM_ACCESS_REQUEST_USE;
r = I2C_WRITE_DATA(tpm_dev, TPM_ACCESS, &data, 1);
if (r < 0)
goto end;
+ stop = jiffies + chip->vendor.timeout_a;
+
if (chip->vendor.irq) {
- r = wait_for_serirq_timeout(chip, (check_locality
- (chip) >= 0),
- chip->vendor.timeout_a);
+again:
+ timeout = stop - jiffies;
+ if ((long) timeout <= 0)
+ return -1;
+ r = wait_event_interruptible_timeout(chip->vendor.int_queue,
+ check_locality(chip) >= 0,
+ timeout);
if (r > 0)
return chip->vendor.locality;
+ if (r == -ERESTARTSYS && freezing(current)) {
+ clear_thread_flag(TIF_SIGPENDING);
+ goto again;
+ }
} else {
stop = jiffies + chip->vendor.timeout_a;
do {
@@ -385,39 +351,6 @@ end:
} /* get_burstcount() */
/*
- * wait_for_stat wait for a TPM_STS value
- * @param: chip, the tpm chip description
- * @param: mask, the value mask to wait
- * @param: timeout, the timeout
- * @param: queue, the wait queue.
- * @return: the tpm status, 0 if success, -ETIME if timeout is reached.
- */
-static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
- wait_queue_head_t *queue)
-{
- unsigned long stop;
- long r;
- u8 status;
-
- if (chip->vendor.irq) {
- r = wait_for_serirq_timeout(chip, ((tpm_stm_i2c_status
- (chip) & mask) ==
- mask), timeout);
- if (r > 0)
- return 0;
- } else {
- stop = jiffies + timeout;
- do {
- msleep(TPM_TIMEOUT);
- status = tpm_stm_i2c_status(chip);
- if ((status & mask) == mask)
- return 0;
- } while (time_before(jiffies, stop));
- }
- return -ETIME;
-} /* wait_for_stat() */
-
-/*
* recv_data receive data
* @param: chip, the tpm chip description
* @param: buf, the buffer where the data are received
@@ -432,10 +365,10 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip);
while (size < count &&
- wait_for_stat(chip,
+ wait_for_tpm_stat(chip,
TPM_STS_DATA_AVAIL | TPM_STS_VALID,
chip->vendor.timeout_c,
- &chip->vendor.read_queue) == 0) {
+ &chip->vendor.read_queue, true) == 0) {
burstcnt = get_burstcount(chip);
if (burstcnt < 0)
return burstcnt;
@@ -455,15 +388,23 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id)
{
struct tpm_chip *chip = dev_id;
- struct i2c_client *client;
struct tpm_stm_dev *tpm_dev;
-
- disable_irq_nosync(irq);
+ u8 interrupt;
tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip);
- client = tpm_dev->client;
- complete(&tpm_dev->irq_detection);
+ interrupt = clear_interruption(tpm_dev);
+ if (!interrupt)
+ return IRQ_HANDLED;
+
+ if (interrupt & TPM_INTF_DATA_AVAIL_INT)
+ wake_up_interruptible(&chip->vendor.read_queue);
+
+ if (interrupt &
+ (TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_STS_VALID_INT |
+ TPM_INTF_CMD_READY_INT))
+ wake_up_interruptible(&chip->vendor.int_queue);
+
return IRQ_HANDLED;
} /* tpm_ioserirq_handler() */
@@ -504,9 +445,9 @@ static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf,
status = tpm_stm_i2c_status(chip);
if ((status & TPM_STS_COMMAND_READY) == 0) {
tpm_stm_i2c_cancel(chip);
- if (wait_for_stat
+ if (wait_for_tpm_stat
(chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b,
- &chip->vendor.int_queue) < 0) {
+ &chip->vendor.int_queue, false) < 0) {
r = -ETIME;
goto out_err;
}
@@ -793,7 +734,10 @@ tpm_stm_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
chip->vendor.locality = LOCALITY0;
if (interrupts) {
- init_completion(&tpm_dev->irq_detection);
+ /* INTERRUPT Setup */
+ init_waitqueue_head(&chip->vendor.read_queue);
+ init_waitqueue_head(&chip->vendor.int_queue);
+
if (request_locality(chip) != LOCALITY0) {
r = -ENODEV;
goto _tpm_clean_answer;
@@ -900,10 +844,10 @@ static int tpm_stm_i2c_pm_resume(struct device *dev)
if (power_mgt) {
gpio_set_value(pin_infos->io_lpcpd, 1);
- r = wait_for_serirq_timeout(chip,
- (chip->ops->status(chip) &
- TPM_STS_VALID) == TPM_STS_VALID,
- chip->vendor.timeout_b);
+ r = wait_for_tpm_stat(chip, TPM_STS_VALID,
+ chip->vendor.timeout_b,
+ &chip->vendor.read_queue,
+ false);
} else {
r = tpm_pm_resume(dev);
if (!r)
The tpm layer already provides a function to wait for a TIS event wait_for_tpm_stat. Exactly like wait_for_serirq_timeout, it can work in polling or interrupt mode. Instead of using a completion struct, we rely on the waitqueue read_queue and int_queue from chip->vendor field. Signed-off-by: Christophe Ricard <christophe-h.ricard@st.com> --- drivers/char/tpm/tpm_i2c_stm_st33.c | 152 ++++++++++++------------------------ 1 file changed, 48 insertions(+), 104 deletions(-)