mirror of
https://github.com/edk2-porting/linux-next.git
synced 2025-01-12 15:44:01 +08:00
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>
This commit is contained in:
parent
8dcd19874f
commit
c3804b8cdb
@ -38,6 +38,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/sysfs.h>
|
||||
@ -102,9 +103,9 @@ enum tis_defaults {
|
||||
|
||||
struct tpm_stm_dev {
|
||||
struct i2c_client *client;
|
||||
struct completion irq_detection;
|
||||
struct tpm_chip *chip;
|
||||
u8 buf[TPM_BUFSIZE + 1];
|
||||
u32 intrs;
|
||||
int io_lpcpd;
|
||||
};
|
||||
|
||||
@ -174,60 +175,17 @@ static int read8_reg(struct tpm_stm_dev *tpm_dev, u8 tpm_register,
|
||||
* clear_interruption
|
||||
* clear the TPM interrupt register.
|
||||
* @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;
|
||||
|
||||
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);
|
||||
return interrupt;
|
||||
} /* 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.
|
||||
* @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)
|
||||
|
||||
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() */
|
||||
|
||||
/*
|
||||
@ -297,30 +253,24 @@ static int request_locality(struct tpm_chip *chip)
|
||||
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;
|
||||
ret = I2C_WRITE_DATA(tpm_dev, TPM_ACCESS, &data, 1);
|
||||
if (ret < 0)
|
||||
goto end;
|
||||
|
||||
if (chip->vendor.irq) {
|
||||
ret = wait_for_serirq_timeout(chip, (check_locality
|
||||
(chip) >= 0),
|
||||
chip->vendor.timeout_a);
|
||||
if (ret > 0)
|
||||
stop = jiffies + chip->vendor.timeout_a;
|
||||
|
||||
/* Request locality is usually effective after the request */
|
||||
do {
|
||||
if (check_locality(chip) >= 0)
|
||||
return chip->vendor.locality;
|
||||
} else {
|
||||
stop = jiffies + chip->vendor.timeout_a;
|
||||
do {
|
||||
if (check_locality(chip) >= 0)
|
||||
return chip->vendor.locality;
|
||||
msleep(TPM_TIMEOUT);
|
||||
} while (time_before(jiffies, stop));
|
||||
}
|
||||
msleep(TPM_TIMEOUT);
|
||||
} while (time_before(jiffies, stop));
|
||||
ret = -EACCES;
|
||||
end:
|
||||
return ret;
|
||||
@ -378,36 +328,107 @@ end:
|
||||
return -EBUSY;
|
||||
} /* 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
|
||||
* @param: chip, the tpm chip description
|
||||
* @param: mask, the value mask to wait
|
||||
* @param: timeout, the timeout
|
||||
* @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.
|
||||
*/
|
||||
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;
|
||||
long ret;
|
||||
u8 status;
|
||||
int r;
|
||||
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) {
|
||||
ret = wait_for_serirq_timeout(chip, ((tpm_stm_i2c_status
|
||||
(chip) & mask) ==
|
||||
mask), timeout);
|
||||
if (ret > 0)
|
||||
cur_intrs = tpm_dev->intrs;
|
||||
interrupt = clear_interruption(tpm_dev);
|
||||
enable_irq(chip->vendor.irq);
|
||||
|
||||
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;
|
||||
}
|
||||
if (r == -ERESTARTSYS && freezing(current)) {
|
||||
clear_thread_flag(TIF_SIGPENDING);
|
||||
goto again;
|
||||
}
|
||||
disable_irq_nosync(chip->vendor.irq);
|
||||
|
||||
} else {
|
||||
stop = jiffies + timeout;
|
||||
do {
|
||||
msleep(TPM_TIMEOUT);
|
||||
status = tpm_stm_i2c_status(chip);
|
||||
status = chip->ops->status(chip);
|
||||
if ((status & mask) == mask)
|
||||
return 0;
|
||||
} while (time_before(jiffies, stop));
|
||||
}
|
||||
|
||||
return -ETIME;
|
||||
} /* wait_for_stat() */
|
||||
|
||||
@ -429,7 +450,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
|
||||
wait_for_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;
|
||||
@ -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)
|
||||
{
|
||||
struct tpm_chip *chip = dev_id;
|
||||
struct i2c_client *client;
|
||||
struct tpm_stm_dev *tpm_dev;
|
||||
|
||||
disable_irq_nosync(irq);
|
||||
|
||||
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;
|
||||
} /* tpm_ioserirq_handler() */
|
||||
|
||||
@ -500,7 +520,7 @@ static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf,
|
||||
tpm_stm_i2c_cancel(chip);
|
||||
if (wait_for_stat
|
||||
(chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b,
|
||||
&chip->vendor.int_queue) < 0) {
|
||||
&chip->vendor.read_queue, false) < 0) {
|
||||
ret = -ETIME;
|
||||
goto out_err;
|
||||
}
|
||||
@ -743,7 +763,10 @@ tpm_stm_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
chip->vendor.locality = LOCALITY0;
|
||||
|
||||
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) {
|
||||
ret = -ENODEV;
|
||||
goto _tpm_clean_answer;
|
||||
@ -765,9 +788,6 @@ tpm_stm_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
goto _tpm_clean_answer;
|
||||
|
||||
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_DATA_AVAIL_INT;
|
||||
|
||||
@ -787,6 +807,8 @@ tpm_stm_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
|
||||
chip->vendor.irq = client->irq;
|
||||
|
||||
disable_irq_nosync(chip->vendor.irq);
|
||||
|
||||
tpm_gen_interrupt(chip);
|
||||
}
|
||||
|
||||
@ -852,10 +874,9 @@ static int tpm_stm_i2c_pm_resume(struct device *dev)
|
||||
|
||||
if (gpio_is_valid(pin_infos->io_lpcpd)) {
|
||||
gpio_set_value(pin_infos->io_lpcpd, 1);
|
||||
ret = wait_for_serirq_timeout(chip,
|
||||
(chip->ops->status(chip) &
|
||||
TPM_STS_VALID) == TPM_STS_VALID,
|
||||
chip->vendor.timeout_b);
|
||||
ret = wait_for_stat(chip,
|
||||
TPM_STS_VALID, chip->vendor.timeout_b,
|
||||
&chip->vendor.read_queue, false);
|
||||
} else {
|
||||
ret = tpm_pm_resume(dev);
|
||||
if (!ret)
|
||||
|
Loading…
Reference in New Issue
Block a user