562 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			562 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0-or-later
 | |
| /*
 | |
|  * STMicroelectronics TPM Linux driver for TPM ST33ZP24
 | |
|  * Copyright (C) 2009 - 2016 STMicroelectronics
 | |
|  */
 | |
| 
 | |
| #include <linux/module.h>
 | |
| #include <linux/fs.h>
 | |
| #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/gpio.h>
 | |
| #include <linux/sched.h>
 | |
| #include <linux/uaccess.h>
 | |
| #include <linux/io.h>
 | |
| #include <linux/slab.h>
 | |
| 
 | |
| #include "../tpm.h"
 | |
| #include "st33zp24.h"
 | |
| 
 | |
| #define TPM_ACCESS			0x0
 | |
| #define TPM_STS				0x18
 | |
| #define TPM_DATA_FIFO			0x24
 | |
| #define TPM_INTF_CAPABILITY		0x14
 | |
| #define TPM_INT_STATUS			0x10
 | |
| #define TPM_INT_ENABLE			0x08
 | |
| 
 | |
| #define LOCALITY0			0
 | |
| 
 | |
| enum st33zp24_access {
 | |
| 	TPM_ACCESS_VALID = 0x80,
 | |
| 	TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
 | |
| 	TPM_ACCESS_REQUEST_PENDING = 0x04,
 | |
| 	TPM_ACCESS_REQUEST_USE = 0x02,
 | |
| };
 | |
| 
 | |
| enum st33zp24_status {
 | |
| 	TPM_STS_VALID = 0x80,
 | |
| 	TPM_STS_COMMAND_READY = 0x40,
 | |
| 	TPM_STS_GO = 0x20,
 | |
| 	TPM_STS_DATA_AVAIL = 0x10,
 | |
| 	TPM_STS_DATA_EXPECT = 0x08,
 | |
| };
 | |
| 
 | |
| enum st33zp24_int_flags {
 | |
| 	TPM_GLOBAL_INT_ENABLE = 0x80,
 | |
| 	TPM_INTF_CMD_READY_INT = 0x080,
 | |
| 	TPM_INTF_FIFO_AVALAIBLE_INT = 0x040,
 | |
| 	TPM_INTF_WAKE_UP_READY_INT = 0x020,
 | |
| 	TPM_INTF_LOCALITY_CHANGE_INT = 0x004,
 | |
| 	TPM_INTF_STS_VALID_INT = 0x002,
 | |
| 	TPM_INTF_DATA_AVAIL_INT = 0x001,
 | |
| };
 | |
| 
 | |
| enum tis_defaults {
 | |
| 	TIS_SHORT_TIMEOUT = 750,
 | |
| 	TIS_LONG_TIMEOUT = 2000,
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * clear the pending interrupt.
 | |
|  */
 | |
| static u8 clear_interruption(struct st33zp24_dev *tpm_dev)
 | |
| {
 | |
| 	u8 interrupt;
 | |
| 
 | |
| 	tpm_dev->ops->recv(tpm_dev->phy_id, TPM_INT_STATUS, &interrupt, 1);
 | |
| 	tpm_dev->ops->send(tpm_dev->phy_id, TPM_INT_STATUS, &interrupt, 1);
 | |
| 	return interrupt;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * cancel the current command execution or set STS to COMMAND READY.
 | |
|  */
 | |
| static void st33zp24_cancel(struct tpm_chip *chip)
 | |
| {
 | |
| 	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
 | |
| 	u8 data;
 | |
| 
 | |
| 	data = TPM_STS_COMMAND_READY;
 | |
| 	tpm_dev->ops->send(tpm_dev->phy_id, TPM_STS, &data, 1);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * return the TPM_STS register
 | |
|  */
 | |
| static u8 st33zp24_status(struct tpm_chip *chip)
 | |
| {
 | |
| 	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
 | |
| 	u8 data;
 | |
| 
 | |
| 	tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS, &data, 1);
 | |
| 	return data;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * if the locality is active
 | |
|  */
 | |
| static bool check_locality(struct tpm_chip *chip)
 | |
| {
 | |
| 	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
 | |
| 	u8 data;
 | |
| 	u8 status;
 | |
| 
 | |
| 	status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_ACCESS, &data, 1);
 | |
| 	if (status && (data &
 | |
| 		(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
 | |
| 		(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
 | |
| 		return true;
 | |
| 
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| static int request_locality(struct tpm_chip *chip)
 | |
| {
 | |
| 	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
 | |
| 	unsigned long stop;
 | |
| 	long ret;
 | |
| 	u8 data;
 | |
| 
 | |
| 	if (check_locality(chip))
 | |
| 		return tpm_dev->locality;
 | |
| 
 | |
| 	data = TPM_ACCESS_REQUEST_USE;
 | |
| 	ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_ACCESS, &data, 1);
 | |
| 	if (ret < 0)
 | |
| 		return ret;
 | |
| 
 | |
| 	stop = jiffies + chip->timeout_a;
 | |
| 
 | |
| 	/* Request locality is usually effective after the request */
 | |
| 	do {
 | |
| 		if (check_locality(chip))
 | |
| 			return tpm_dev->locality;
 | |
| 		msleep(TPM_TIMEOUT);
 | |
| 	} while (time_before(jiffies, stop));
 | |
| 
 | |
| 	/* could not get locality */
 | |
| 	return -EACCES;
 | |
| }
 | |
| 
 | |
| static void release_locality(struct tpm_chip *chip)
 | |
| {
 | |
| 	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
 | |
| 	u8 data;
 | |
| 
 | |
| 	data = TPM_ACCESS_ACTIVE_LOCALITY;
 | |
| 
 | |
| 	tpm_dev->ops->send(tpm_dev->phy_id, TPM_ACCESS, &data, 1);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * get_burstcount return the burstcount value
 | |
|  */
 | |
| static int get_burstcount(struct tpm_chip *chip)
 | |
| {
 | |
| 	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
 | |
| 	unsigned long stop;
 | |
| 	int burstcnt, status;
 | |
| 	u8 temp;
 | |
| 
 | |
| 	stop = jiffies + chip->timeout_d;
 | |
| 	do {
 | |
| 		status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS + 1,
 | |
| 					    &temp, 1);
 | |
| 		if (status < 0)
 | |
| 			return -EBUSY;
 | |
| 
 | |
| 		burstcnt = temp;
 | |
| 		status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS + 2,
 | |
| 					    &temp, 1);
 | |
| 		if (status < 0)
 | |
| 			return -EBUSY;
 | |
| 
 | |
| 		burstcnt |= temp << 8;
 | |
| 		if (burstcnt)
 | |
| 			return burstcnt;
 | |
| 		msleep(TPM_TIMEOUT);
 | |
| 	} while (time_before(jiffies, stop));
 | |
| 	return -EBUSY;
 | |
| }
 | |
| 
 | |
| 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;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * wait for a TPM_STS value
 | |
|  */
 | |
| static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
 | |
| 			wait_queue_head_t *queue, bool check_cancel)
 | |
| {
 | |
| 	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
 | |
| 	unsigned long stop;
 | |
| 	int ret = 0;
 | |
| 	bool canceled = false;
 | |
| 	bool condition;
 | |
| 	u32 cur_intrs;
 | |
| 	u8 status;
 | |
| 
 | |
| 	/* check current status */
 | |
| 	status = st33zp24_status(chip);
 | |
| 	if ((status & mask) == mask)
 | |
| 		return 0;
 | |
| 
 | |
| 	stop = jiffies + timeout;
 | |
| 
 | |
| 	if (chip->flags & TPM_CHIP_FLAG_IRQ) {
 | |
| 		cur_intrs = tpm_dev->intrs;
 | |
| 		clear_interruption(tpm_dev);
 | |
| 		enable_irq(tpm_dev->irq);
 | |
| 
 | |
| 		do {
 | |
| 			if (ret == -ERESTARTSYS && freezing(current))
 | |
| 				clear_thread_flag(TIF_SIGPENDING);
 | |
| 
 | |
| 			timeout = stop - jiffies;
 | |
| 			if ((long) timeout <= 0)
 | |
| 				return -1;
 | |
| 
 | |
| 			ret = wait_event_interruptible_timeout(*queue,
 | |
| 						cur_intrs != tpm_dev->intrs,
 | |
| 						timeout);
 | |
| 			clear_interruption(tpm_dev);
 | |
| 			condition = wait_for_tpm_stat_cond(chip, mask,
 | |
| 						check_cancel, &canceled);
 | |
| 			if (ret >= 0 && condition) {
 | |
| 				if (canceled)
 | |
| 					return -ECANCELED;
 | |
| 				return 0;
 | |
| 			}
 | |
| 		} while (ret == -ERESTARTSYS && freezing(current));
 | |
| 
 | |
| 		disable_irq_nosync(tpm_dev->irq);
 | |
| 
 | |
| 	} else {
 | |
| 		do {
 | |
| 			msleep(TPM_TIMEOUT);
 | |
| 			status = chip->ops->status(chip);
 | |
| 			if ((status & mask) == mask)
 | |
| 				return 0;
 | |
| 		} while (time_before(jiffies, stop));
 | |
| 	}
 | |
| 
 | |
| 	return -ETIME;
 | |
| }
 | |
| 
 | |
| static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
 | |
| {
 | |
| 	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
 | |
| 	int size = 0, burstcnt, len, ret;
 | |
| 
 | |
| 	while (size < count &&
 | |
| 	       wait_for_stat(chip,
 | |
| 			     TPM_STS_DATA_AVAIL | TPM_STS_VALID,
 | |
| 			     chip->timeout_c,
 | |
| 			     &tpm_dev->read_queue, true) == 0) {
 | |
| 		burstcnt = get_burstcount(chip);
 | |
| 		if (burstcnt < 0)
 | |
| 			return burstcnt;
 | |
| 		len = min_t(int, burstcnt, count - size);
 | |
| 		ret = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_DATA_FIFO,
 | |
| 					 buf + size, len);
 | |
| 		if (ret < 0)
 | |
| 			return ret;
 | |
| 
 | |
| 		size += len;
 | |
| 	}
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id)
 | |
| {
 | |
| 	struct tpm_chip *chip = dev_id;
 | |
| 	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
 | |
| 
 | |
| 	tpm_dev->intrs++;
 | |
| 	wake_up_interruptible(&tpm_dev->read_queue);
 | |
| 	disable_irq_nosync(tpm_dev->irq);
 | |
| 
 | |
| 	return IRQ_HANDLED;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * send TPM commands through the I2C bus.
 | |
|  */
 | |
| static int st33zp24_send(struct tpm_chip *chip, unsigned char *buf,
 | |
| 			 size_t len)
 | |
| {
 | |
| 	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
 | |
| 	u32 status, i, size, ordinal;
 | |
| 	int burstcnt = 0;
 | |
| 	int ret;
 | |
| 	u8 data;
 | |
| 
 | |
| 	if (len < TPM_HEADER_SIZE)
 | |
| 		return -EBUSY;
 | |
| 
 | |
| 	ret = request_locality(chip);
 | |
| 	if (ret < 0)
 | |
| 		return ret;
 | |
| 
 | |
| 	status = st33zp24_status(chip);
 | |
| 	if ((status & TPM_STS_COMMAND_READY) == 0) {
 | |
| 		st33zp24_cancel(chip);
 | |
| 		if (wait_for_stat
 | |
| 		    (chip, TPM_STS_COMMAND_READY, chip->timeout_b,
 | |
| 		     &tpm_dev->read_queue, false) < 0) {
 | |
| 			ret = -ETIME;
 | |
| 			goto out_err;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	for (i = 0; i < len - 1;) {
 | |
| 		burstcnt = get_burstcount(chip);
 | |
| 		if (burstcnt < 0)
 | |
| 			return burstcnt;
 | |
| 		size = min_t(int, len - i - 1, burstcnt);
 | |
| 		ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_DATA_FIFO,
 | |
| 					 buf + i, size);
 | |
| 		if (ret < 0)
 | |
| 			goto out_err;
 | |
| 
 | |
| 		i += size;
 | |
| 	}
 | |
| 
 | |
| 	status = st33zp24_status(chip);
 | |
| 	if ((status & TPM_STS_DATA_EXPECT) == 0) {
 | |
| 		ret = -EIO;
 | |
| 		goto out_err;
 | |
| 	}
 | |
| 
 | |
| 	ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_DATA_FIFO,
 | |
| 				 buf + len - 1, 1);
 | |
| 	if (ret < 0)
 | |
| 		goto out_err;
 | |
| 
 | |
| 	status = st33zp24_status(chip);
 | |
| 	if ((status & TPM_STS_DATA_EXPECT) != 0) {
 | |
| 		ret = -EIO;
 | |
| 		goto out_err;
 | |
| 	}
 | |
| 
 | |
| 	data = TPM_STS_GO;
 | |
| 	ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_STS, &data, 1);
 | |
| 	if (ret < 0)
 | |
| 		goto out_err;
 | |
| 
 | |
| 	if (chip->flags & TPM_CHIP_FLAG_IRQ) {
 | |
| 		ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
 | |
| 
 | |
| 		ret = wait_for_stat(chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
 | |
| 				tpm_calc_ordinal_duration(chip, ordinal),
 | |
| 				&tpm_dev->read_queue, false);
 | |
| 		if (ret < 0)
 | |
| 			goto out_err;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| out_err:
 | |
| 	st33zp24_cancel(chip);
 | |
| 	release_locality(chip);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int st33zp24_recv(struct tpm_chip *chip, unsigned char *buf,
 | |
| 			    size_t count)
 | |
| {
 | |
| 	int size = 0;
 | |
| 	u32 expected;
 | |
| 
 | |
| 	if (!chip)
 | |
| 		return -EBUSY;
 | |
| 
 | |
| 	if (count < TPM_HEADER_SIZE) {
 | |
| 		size = -EIO;
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	size = recv_data(chip, buf, TPM_HEADER_SIZE);
 | |
| 	if (size < TPM_HEADER_SIZE) {
 | |
| 		dev_err(&chip->dev, "Unable to read header\n");
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	expected = be32_to_cpu(*(__be32 *)(buf + 2));
 | |
| 	if (expected > count || expected < TPM_HEADER_SIZE) {
 | |
| 		size = -EIO;
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	size += recv_data(chip, &buf[TPM_HEADER_SIZE],
 | |
| 			expected - TPM_HEADER_SIZE);
 | |
| 	if (size < expected) {
 | |
| 		dev_err(&chip->dev, "Unable to read remainder of result\n");
 | |
| 		size = -ETIME;
 | |
| 	}
 | |
| 
 | |
| out:
 | |
| 	st33zp24_cancel(chip);
 | |
| 	release_locality(chip);
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| static bool st33zp24_req_canceled(struct tpm_chip *chip, u8 status)
 | |
| {
 | |
| 	return (status == TPM_STS_COMMAND_READY);
 | |
| }
 | |
| 
 | |
| static const struct tpm_class_ops st33zp24_tpm = {
 | |
| 	.flags = TPM_OPS_AUTO_STARTUP,
 | |
| 	.send = st33zp24_send,
 | |
| 	.recv = st33zp24_recv,
 | |
| 	.cancel = st33zp24_cancel,
 | |
| 	.status = st33zp24_status,
 | |
| 	.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
 | |
| 	.req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
 | |
| 	.req_canceled = st33zp24_req_canceled,
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * initialize the TPM device
 | |
|  */
 | |
| int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops,
 | |
| 		   struct device *dev, int irq, int io_lpcpd)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 intmask = 0;
 | |
| 	struct tpm_chip *chip;
 | |
| 	struct st33zp24_dev *tpm_dev;
 | |
| 
 | |
| 	chip = tpmm_chip_alloc(dev, &st33zp24_tpm);
 | |
| 	if (IS_ERR(chip))
 | |
| 		return PTR_ERR(chip);
 | |
| 
 | |
| 	tpm_dev = devm_kzalloc(dev, sizeof(struct st33zp24_dev),
 | |
| 			       GFP_KERNEL);
 | |
| 	if (!tpm_dev)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	tpm_dev->phy_id = phy_id;
 | |
| 	tpm_dev->ops = ops;
 | |
| 	dev_set_drvdata(&chip->dev, tpm_dev);
 | |
| 
 | |
| 	chip->timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
 | |
| 	chip->timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
 | |
| 	chip->timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
 | |
| 	chip->timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
 | |
| 
 | |
| 	tpm_dev->locality = LOCALITY0;
 | |
| 
 | |
| 	if (irq) {
 | |
| 		/* INTERRUPT Setup */
 | |
| 		init_waitqueue_head(&tpm_dev->read_queue);
 | |
| 		tpm_dev->intrs = 0;
 | |
| 
 | |
| 		if (request_locality(chip) != LOCALITY0) {
 | |
| 			ret = -ENODEV;
 | |
| 			goto _tpm_clean_answer;
 | |
| 		}
 | |
| 
 | |
| 		clear_interruption(tpm_dev);
 | |
| 		ret = devm_request_irq(dev, irq, tpm_ioserirq_handler,
 | |
| 				IRQF_TRIGGER_HIGH, "TPM SERIRQ management",
 | |
| 				chip);
 | |
| 		if (ret < 0) {
 | |
| 			dev_err(&chip->dev, "TPM SERIRQ signals %d not available\n",
 | |
| 				irq);
 | |
| 			goto _tpm_clean_answer;
 | |
| 		}
 | |
| 
 | |
| 		intmask |= TPM_INTF_CMD_READY_INT
 | |
| 			|  TPM_INTF_STS_VALID_INT
 | |
| 			|  TPM_INTF_DATA_AVAIL_INT;
 | |
| 
 | |
| 		ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_INT_ENABLE,
 | |
| 					 &intmask, 1);
 | |
| 		if (ret < 0)
 | |
| 			goto _tpm_clean_answer;
 | |
| 
 | |
| 		intmask = TPM_GLOBAL_INT_ENABLE;
 | |
| 		ret = tpm_dev->ops->send(tpm_dev->phy_id, (TPM_INT_ENABLE + 3),
 | |
| 					 &intmask, 1);
 | |
| 		if (ret < 0)
 | |
| 			goto _tpm_clean_answer;
 | |
| 
 | |
| 		tpm_dev->irq = irq;
 | |
| 		chip->flags |= TPM_CHIP_FLAG_IRQ;
 | |
| 
 | |
| 		disable_irq_nosync(tpm_dev->irq);
 | |
| 	}
 | |
| 
 | |
| 	return tpm_chip_register(chip);
 | |
| _tpm_clean_answer:
 | |
| 	dev_info(&chip->dev, "TPM initialization fail\n");
 | |
| 	return ret;
 | |
| }
 | |
| EXPORT_SYMBOL(st33zp24_probe);
 | |
| 
 | |
| void st33zp24_remove(struct tpm_chip *chip)
 | |
| {
 | |
| 	tpm_chip_unregister(chip);
 | |
| }
 | |
| EXPORT_SYMBOL(st33zp24_remove);
 | |
| 
 | |
| #ifdef CONFIG_PM_SLEEP
 | |
| int st33zp24_pm_suspend(struct device *dev)
 | |
| {
 | |
| 	struct tpm_chip *chip = dev_get_drvdata(dev);
 | |
| 	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
 | |
| 
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	if (gpio_is_valid(tpm_dev->io_lpcpd))
 | |
| 		gpio_set_value(tpm_dev->io_lpcpd, 0);
 | |
| 	else
 | |
| 		ret = tpm_pm_suspend(dev);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| EXPORT_SYMBOL(st33zp24_pm_suspend);
 | |
| 
 | |
| int st33zp24_pm_resume(struct device *dev)
 | |
| {
 | |
| 	struct tpm_chip *chip = dev_get_drvdata(dev);
 | |
| 	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	if (gpio_is_valid(tpm_dev->io_lpcpd)) {
 | |
| 		gpio_set_value(tpm_dev->io_lpcpd, 1);
 | |
| 		ret = wait_for_stat(chip,
 | |
| 				TPM_STS_VALID, chip->timeout_b,
 | |
| 				&tpm_dev->read_queue, false);
 | |
| 	} else {
 | |
| 		ret = tpm_pm_resume(dev);
 | |
| 		if (!ret)
 | |
| 			tpm1_do_selftest(chip);
 | |
| 	}
 | |
| 	return ret;
 | |
| }
 | |
| EXPORT_SYMBOL(st33zp24_pm_resume);
 | |
| #endif
 | |
| 
 | |
| MODULE_AUTHOR("TPM support (TPMsupport@list.st.com)");
 | |
| MODULE_DESCRIPTION("ST33ZP24 TPM 1.2 driver");
 | |
| MODULE_VERSION("1.3.0");
 | |
| MODULE_LICENSE("GPL");
 |