Commit c3804b8c authored by Christophe Ricard's avatar Christophe Ricard Committed by Peter Huewe

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: default avatarJason Gunthorpe <jgunthorpe@obsidianresearch.com>
Signed-off-by: default avatarChristophe Ricard <christophe-h.ricard@st.com>
Signed-off-by: default avatarPeter Huewe <peterhuewe@gmx.de>
parent 8dcd1987
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/freezer.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/sysfs.h> #include <linux/sysfs.h>
...@@ -102,9 +103,9 @@ enum tis_defaults { ...@@ -102,9 +103,9 @@ enum tis_defaults {
struct tpm_stm_dev { struct tpm_stm_dev {
struct i2c_client *client; struct i2c_client *client;
struct completion irq_detection;
struct tpm_chip *chip; struct tpm_chip *chip;
u8 buf[TPM_BUFSIZE + 1]; u8 buf[TPM_BUFSIZE + 1];
u32 intrs;
int io_lpcpd; int io_lpcpd;
}; };
...@@ -174,60 +175,17 @@ static int read8_reg(struct tpm_stm_dev *tpm_dev, u8 tpm_register, ...@@ -174,60 +175,17 @@ static int read8_reg(struct tpm_stm_dev *tpm_dev, u8 tpm_register,
* clear_interruption * clear_interruption
* clear the TPM interrupt register. * clear the TPM interrupt register.
* @param: tpm, the chip description * @param: tpm, the chip description
* @return: the TPM_INT_STATUS value
*/ */
static void clear_interruption(struct tpm_stm_dev *tpm_dev) static u8 clear_interruption(struct tpm_stm_dev *tpm_dev)
{ {
u8 interrupt; u8 interrupt;
I2C_READ_DATA(tpm_dev, TPM_INT_STATUS, &interrupt, 1); I2C_READ_DATA(tpm_dev, TPM_INT_STATUS, &interrupt, 1);
I2C_WRITE_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); return interrupt;
} /* clear_interruption() */ } /* 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() */
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() */
/* /*
* tpm_stm_i2c_cancel, cancel is not implemented. * tpm_stm_i2c_cancel, cancel is not implemented.
* @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h
...@@ -241,8 +199,6 @@ static void tpm_stm_i2c_cancel(struct tpm_chip *chip) ...@@ -241,8 +199,6 @@ static void tpm_stm_i2c_cancel(struct tpm_chip *chip)
data = TPM_STS_COMMAND_READY; data = TPM_STS_COMMAND_READY;
I2C_WRITE_DATA(tpm_dev, TPM_STS, &data, 1); 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() */ } /* tpm_stm_i2c_cancel() */
/* /*
...@@ -297,30 +253,24 @@ static int request_locality(struct tpm_chip *chip) ...@@ -297,30 +253,24 @@ static int request_locality(struct tpm_chip *chip)
struct tpm_stm_dev *tpm_dev; struct tpm_stm_dev *tpm_dev;
u8 data; u8 data;
tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip);
if (check_locality(chip) == chip->vendor.locality) if (check_locality(chip) == chip->vendor.locality)
return chip->vendor.locality; return chip->vendor.locality;
tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip);
data = TPM_ACCESS_REQUEST_USE; data = TPM_ACCESS_REQUEST_USE;
ret = I2C_WRITE_DATA(tpm_dev, TPM_ACCESS, &data, 1); ret = I2C_WRITE_DATA(tpm_dev, TPM_ACCESS, &data, 1);
if (ret < 0) if (ret < 0)
goto end; goto end;
if (chip->vendor.irq) { stop = jiffies + chip->vendor.timeout_a;
ret = wait_for_serirq_timeout(chip, (check_locality
(chip) >= 0), /* Request locality is usually effective after the request */
chip->vendor.timeout_a); do {
if (ret > 0) if (check_locality(chip) >= 0)
return chip->vendor.locality; return chip->vendor.locality;
} else { msleep(TPM_TIMEOUT);
stop = jiffies + chip->vendor.timeout_a; } while (time_before(jiffies, stop));
do {
if (check_locality(chip) >= 0)
return chip->vendor.locality;
msleep(TPM_TIMEOUT);
} while (time_before(jiffies, stop));
}
ret = -EACCES; ret = -EACCES;
end: end:
return ret; return ret;
...@@ -378,36 +328,107 @@ static int get_burstcount(struct tpm_chip *chip) ...@@ -378,36 +328,107 @@ static int get_burstcount(struct tpm_chip *chip)
return -EBUSY; return -EBUSY;
} /* get_burstcount() */ } /* get_burstcount() */
static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask,
bool check_cancel, bool *canceled)
{
u8 status = chip->ops->status(chip);
*canceled = false;
if ((status & mask) == mask)
return true;
if (check_cancel && chip->ops->req_canceled(chip, status)) {
*canceled = true;
return true;
}
return false;
}
/*
* interrupt_to_status
* @param: irq_mask, the irq mask value to wait
* @return: the corresponding tpm_sts value
*/
static u8 interrupt_to_status(u8 irq_mask)
{
u8 status = 0;
if ((irq_mask & TPM_INTF_STS_VALID_INT) == TPM_INTF_STS_VALID_INT)
status |= TPM_STS_VALID;
if ((irq_mask & TPM_INTF_DATA_AVAIL_INT) == TPM_INTF_DATA_AVAIL_INT)
status |= TPM_STS_DATA_AVAIL;
if ((irq_mask & TPM_INTF_CMD_READY_INT) == TPM_INTF_CMD_READY_INT)
status |= TPM_STS_COMMAND_READY;
return status;
} /* status_to_interrupt() */
/* /*
* wait_for_stat wait for a TPM_STS value * wait_for_stat wait for a TPM_STS value
* @param: chip, the tpm chip description * @param: chip, the tpm chip description
* @param: mask, the value mask to wait * @param: mask, the value mask to wait
* @param: timeout, the timeout * @param: timeout, the timeout
* @param: queue, the wait queue. * @param: queue, the wait queue.
* @param: check_cancel, does the command can be cancelled ?
* @return: the tpm status, 0 if success, -ETIME if timeout is reached. * @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, static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
wait_queue_head_t *queue) wait_queue_head_t *queue, bool check_cancel)
{ {
unsigned long stop; unsigned long stop;
long ret; int r;
u8 status; bool canceled = false;
bool condition;
u32 cur_intrs;
u8 interrupt, status;
struct tpm_stm_dev *tpm_dev;
tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip);
/* check current status */
status = tpm_stm_i2c_status(chip);
if ((status & mask) == mask)
return 0;
stop = jiffies + timeout;
if (chip->vendor.irq) { if (chip->vendor.irq) {
ret = wait_for_serirq_timeout(chip, ((tpm_stm_i2c_status cur_intrs = tpm_dev->intrs;
(chip) & mask) == interrupt = clear_interruption(tpm_dev);
mask), timeout); enable_irq(chip->vendor.irq);
if (ret > 0)
again:
timeout = stop - jiffies;
if ((long) timeout <= 0)
return -1;
r = wait_event_interruptible_timeout(*queue,
cur_intrs != tpm_dev->intrs, timeout);
interrupt |= clear_interruption(tpm_dev);
status = interrupt_to_status(interrupt);
condition = wait_for_tpm_stat_cond(chip, mask,
check_cancel, &canceled);
if (r >= 0 && condition) {
if (canceled)
return -ECANCELED;
return 0; return 0;
}
if (r == -ERESTARTSYS && freezing(current)) {
clear_thread_flag(TIF_SIGPENDING);
goto again;
}
disable_irq_nosync(chip->vendor.irq);
} else { } else {
stop = jiffies + timeout;
do { do {
msleep(TPM_TIMEOUT); msleep(TPM_TIMEOUT);
status = tpm_stm_i2c_status(chip); status = chip->ops->status(chip);
if ((status & mask) == mask) if ((status & mask) == mask)
return 0; return 0;
} while (time_before(jiffies, stop)); } while (time_before(jiffies, stop));
} }
return -ETIME; return -ETIME;
} /* wait_for_stat() */ } /* wait_for_stat() */
...@@ -429,7 +450,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) ...@@ -429,7 +450,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
wait_for_stat(chip, wait_for_stat(chip,
TPM_STS_DATA_AVAIL | TPM_STS_VALID, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
chip->vendor.timeout_c, chip->vendor.timeout_c,
&chip->vendor.read_queue) == 0) { &chip->vendor.read_queue, true) == 0) {
burstcnt = get_burstcount(chip); burstcnt = get_burstcount(chip);
if (burstcnt < 0) if (burstcnt < 0)
return burstcnt; return burstcnt;
...@@ -449,15 +470,14 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) ...@@ -449,15 +470,14 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id) static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id)
{ {
struct tpm_chip *chip = dev_id; struct tpm_chip *chip = dev_id;
struct i2c_client *client;
struct tpm_stm_dev *tpm_dev; struct tpm_stm_dev *tpm_dev;
disable_irq_nosync(irq);
tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip);
client = tpm_dev->client;
complete(&tpm_dev->irq_detection); tpm_dev->intrs++;
wake_up_interruptible(&chip->vendor.read_queue);
disable_irq_nosync(chip->vendor.irq);
return IRQ_HANDLED; return IRQ_HANDLED;
} /* tpm_ioserirq_handler() */ } /* tpm_ioserirq_handler() */
...@@ -500,7 +520,7 @@ static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf, ...@@ -500,7 +520,7 @@ static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf,
tpm_stm_i2c_cancel(chip); tpm_stm_i2c_cancel(chip);
if (wait_for_stat if (wait_for_stat
(chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b, (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b,
&chip->vendor.int_queue) < 0) { &chip->vendor.read_queue, false) < 0) {
ret = -ETIME; ret = -ETIME;
goto out_err; goto out_err;
} }
...@@ -743,7 +763,10 @@ tpm_stm_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) ...@@ -743,7 +763,10 @@ tpm_stm_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
chip->vendor.locality = LOCALITY0; chip->vendor.locality = LOCALITY0;
if (client->irq) { if (client->irq) {
init_completion(&tpm_dev->irq_detection); /* INTERRUPT Setup */
init_waitqueue_head(&chip->vendor.read_queue);
tpm_dev->intrs = 0;
if (request_locality(chip) != LOCALITY0) { if (request_locality(chip) != LOCALITY0) {
ret = -ENODEV; ret = -ENODEV;
goto _tpm_clean_answer; goto _tpm_clean_answer;
...@@ -765,9 +788,6 @@ tpm_stm_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) ...@@ -765,9 +788,6 @@ tpm_stm_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
goto _tpm_clean_answer; goto _tpm_clean_answer;
intmask |= TPM_INTF_CMD_READY_INT intmask |= TPM_INTF_CMD_READY_INT
| TPM_INTF_FIFO_AVALAIBLE_INT
| TPM_INTF_WAKE_UP_READY_INT
| TPM_INTF_LOCALITY_CHANGE_INT
| TPM_INTF_STS_VALID_INT | TPM_INTF_STS_VALID_INT
| TPM_INTF_DATA_AVAIL_INT; | TPM_INTF_DATA_AVAIL_INT;
...@@ -787,6 +807,8 @@ tpm_stm_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) ...@@ -787,6 +807,8 @@ tpm_stm_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
chip->vendor.irq = client->irq; chip->vendor.irq = client->irq;
disable_irq_nosync(chip->vendor.irq);
tpm_gen_interrupt(chip); tpm_gen_interrupt(chip);
} }
...@@ -852,10 +874,9 @@ static int tpm_stm_i2c_pm_resume(struct device *dev) ...@@ -852,10 +874,9 @@ static int tpm_stm_i2c_pm_resume(struct device *dev)
if (gpio_is_valid(pin_infos->io_lpcpd)) { if (gpio_is_valid(pin_infos->io_lpcpd)) {
gpio_set_value(pin_infos->io_lpcpd, 1); gpio_set_value(pin_infos->io_lpcpd, 1);
ret = wait_for_serirq_timeout(chip, ret = wait_for_stat(chip,
(chip->ops->status(chip) & TPM_STS_VALID, chip->vendor.timeout_b,
TPM_STS_VALID) == TPM_STS_VALID, &chip->vendor.read_queue, false);
chip->vendor.timeout_b);
} else { } else {
ret = tpm_pm_resume(dev); ret = tpm_pm_resume(dev);
if (!ret) if (!ret)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment