162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * STMicroelectronics TPM Linux driver for TPM ST33ZP24
462306a36Sopenharmony_ci * Copyright (C) 2009 - 2016 STMicroelectronics
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/acpi.h>
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/fs.h>
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/wait.h>
1362306a36Sopenharmony_ci#include <linux/freezer.h>
1462306a36Sopenharmony_ci#include <linux/string.h>
1562306a36Sopenharmony_ci#include <linux/interrupt.h>
1662306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
1762306a36Sopenharmony_ci#include <linux/sched.h>
1862306a36Sopenharmony_ci#include <linux/uaccess.h>
1962306a36Sopenharmony_ci#include <linux/io.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "../tpm.h"
2362306a36Sopenharmony_ci#include "st33zp24.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define TPM_ACCESS			0x0
2662306a36Sopenharmony_ci#define TPM_STS				0x18
2762306a36Sopenharmony_ci#define TPM_DATA_FIFO			0x24
2862306a36Sopenharmony_ci#define TPM_INTF_CAPABILITY		0x14
2962306a36Sopenharmony_ci#define TPM_INT_STATUS			0x10
3062306a36Sopenharmony_ci#define TPM_INT_ENABLE			0x08
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define LOCALITY0			0
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cienum st33zp24_access {
3562306a36Sopenharmony_ci	TPM_ACCESS_VALID = 0x80,
3662306a36Sopenharmony_ci	TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
3762306a36Sopenharmony_ci	TPM_ACCESS_REQUEST_PENDING = 0x04,
3862306a36Sopenharmony_ci	TPM_ACCESS_REQUEST_USE = 0x02,
3962306a36Sopenharmony_ci};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cienum st33zp24_status {
4262306a36Sopenharmony_ci	TPM_STS_VALID = 0x80,
4362306a36Sopenharmony_ci	TPM_STS_COMMAND_READY = 0x40,
4462306a36Sopenharmony_ci	TPM_STS_GO = 0x20,
4562306a36Sopenharmony_ci	TPM_STS_DATA_AVAIL = 0x10,
4662306a36Sopenharmony_ci	TPM_STS_DATA_EXPECT = 0x08,
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cienum st33zp24_int_flags {
5062306a36Sopenharmony_ci	TPM_GLOBAL_INT_ENABLE = 0x80,
5162306a36Sopenharmony_ci	TPM_INTF_CMD_READY_INT = 0x080,
5262306a36Sopenharmony_ci	TPM_INTF_FIFO_AVALAIBLE_INT = 0x040,
5362306a36Sopenharmony_ci	TPM_INTF_WAKE_UP_READY_INT = 0x020,
5462306a36Sopenharmony_ci	TPM_INTF_LOCALITY_CHANGE_INT = 0x004,
5562306a36Sopenharmony_ci	TPM_INTF_STS_VALID_INT = 0x002,
5662306a36Sopenharmony_ci	TPM_INTF_DATA_AVAIL_INT = 0x001,
5762306a36Sopenharmony_ci};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cienum tis_defaults {
6062306a36Sopenharmony_ci	TIS_SHORT_TIMEOUT = 750,
6162306a36Sopenharmony_ci	TIS_LONG_TIMEOUT = 2000,
6262306a36Sopenharmony_ci};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/*
6562306a36Sopenharmony_ci * clear the pending interrupt.
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_cistatic u8 clear_interruption(struct st33zp24_dev *tpm_dev)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	u8 interrupt;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	tpm_dev->ops->recv(tpm_dev->phy_id, TPM_INT_STATUS, &interrupt, 1);
7262306a36Sopenharmony_ci	tpm_dev->ops->send(tpm_dev->phy_id, TPM_INT_STATUS, &interrupt, 1);
7362306a36Sopenharmony_ci	return interrupt;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/*
7762306a36Sopenharmony_ci * cancel the current command execution or set STS to COMMAND READY.
7862306a36Sopenharmony_ci */
7962306a36Sopenharmony_cistatic void st33zp24_cancel(struct tpm_chip *chip)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
8262306a36Sopenharmony_ci	u8 data;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	data = TPM_STS_COMMAND_READY;
8562306a36Sopenharmony_ci	tpm_dev->ops->send(tpm_dev->phy_id, TPM_STS, &data, 1);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/*
8962306a36Sopenharmony_ci * return the TPM_STS register
9062306a36Sopenharmony_ci */
9162306a36Sopenharmony_cistatic u8 st33zp24_status(struct tpm_chip *chip)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
9462306a36Sopenharmony_ci	u8 data;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS, &data, 1);
9762306a36Sopenharmony_ci	return data;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/*
10162306a36Sopenharmony_ci * if the locality is active
10262306a36Sopenharmony_ci */
10362306a36Sopenharmony_cistatic bool check_locality(struct tpm_chip *chip)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
10662306a36Sopenharmony_ci	u8 data;
10762306a36Sopenharmony_ci	u8 status;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_ACCESS, &data, 1);
11062306a36Sopenharmony_ci	if (status && (data &
11162306a36Sopenharmony_ci		(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
11262306a36Sopenharmony_ci		(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
11362306a36Sopenharmony_ci		return true;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return false;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic int request_locality(struct tpm_chip *chip)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
12162306a36Sopenharmony_ci	unsigned long stop;
12262306a36Sopenharmony_ci	long ret;
12362306a36Sopenharmony_ci	u8 data;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (check_locality(chip))
12662306a36Sopenharmony_ci		return tpm_dev->locality;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	data = TPM_ACCESS_REQUEST_USE;
12962306a36Sopenharmony_ci	ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_ACCESS, &data, 1);
13062306a36Sopenharmony_ci	if (ret < 0)
13162306a36Sopenharmony_ci		return ret;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	stop = jiffies + chip->timeout_a;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	/* Request locality is usually effective after the request */
13662306a36Sopenharmony_ci	do {
13762306a36Sopenharmony_ci		if (check_locality(chip))
13862306a36Sopenharmony_ci			return tpm_dev->locality;
13962306a36Sopenharmony_ci		msleep(TPM_TIMEOUT);
14062306a36Sopenharmony_ci	} while (time_before(jiffies, stop));
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/* could not get locality */
14362306a36Sopenharmony_ci	return -EACCES;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic void release_locality(struct tpm_chip *chip)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
14962306a36Sopenharmony_ci	u8 data;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	data = TPM_ACCESS_ACTIVE_LOCALITY;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	tpm_dev->ops->send(tpm_dev->phy_id, TPM_ACCESS, &data, 1);
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci/*
15762306a36Sopenharmony_ci * get_burstcount return the burstcount value
15862306a36Sopenharmony_ci */
15962306a36Sopenharmony_cistatic int get_burstcount(struct tpm_chip *chip)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
16262306a36Sopenharmony_ci	unsigned long stop;
16362306a36Sopenharmony_ci	int burstcnt, status;
16462306a36Sopenharmony_ci	u8 temp;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	stop = jiffies + chip->timeout_d;
16762306a36Sopenharmony_ci	do {
16862306a36Sopenharmony_ci		status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS + 1,
16962306a36Sopenharmony_ci					    &temp, 1);
17062306a36Sopenharmony_ci		if (status < 0)
17162306a36Sopenharmony_ci			return -EBUSY;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci		burstcnt = temp;
17462306a36Sopenharmony_ci		status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS + 2,
17562306a36Sopenharmony_ci					    &temp, 1);
17662306a36Sopenharmony_ci		if (status < 0)
17762306a36Sopenharmony_ci			return -EBUSY;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci		burstcnt |= temp << 8;
18062306a36Sopenharmony_ci		if (burstcnt)
18162306a36Sopenharmony_ci			return burstcnt;
18262306a36Sopenharmony_ci		msleep(TPM_TIMEOUT);
18362306a36Sopenharmony_ci	} while (time_before(jiffies, stop));
18462306a36Sopenharmony_ci	return -EBUSY;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask,
18862306a36Sopenharmony_ci				bool check_cancel, bool *canceled)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	u8 status = chip->ops->status(chip);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	*canceled = false;
19362306a36Sopenharmony_ci	if ((status & mask) == mask)
19462306a36Sopenharmony_ci		return true;
19562306a36Sopenharmony_ci	if (check_cancel && chip->ops->req_canceled(chip, status)) {
19662306a36Sopenharmony_ci		*canceled = true;
19762306a36Sopenharmony_ci		return true;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci	return false;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci/*
20362306a36Sopenharmony_ci * wait for a TPM_STS value
20462306a36Sopenharmony_ci */
20562306a36Sopenharmony_cistatic int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
20662306a36Sopenharmony_ci			wait_queue_head_t *queue, bool check_cancel)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
20962306a36Sopenharmony_ci	unsigned long stop;
21062306a36Sopenharmony_ci	int ret = 0;
21162306a36Sopenharmony_ci	bool canceled = false;
21262306a36Sopenharmony_ci	bool condition;
21362306a36Sopenharmony_ci	u32 cur_intrs;
21462306a36Sopenharmony_ci	u8 status;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/* check current status */
21762306a36Sopenharmony_ci	status = st33zp24_status(chip);
21862306a36Sopenharmony_ci	if ((status & mask) == mask)
21962306a36Sopenharmony_ci		return 0;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	stop = jiffies + timeout;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (chip->flags & TPM_CHIP_FLAG_IRQ) {
22462306a36Sopenharmony_ci		cur_intrs = tpm_dev->intrs;
22562306a36Sopenharmony_ci		clear_interruption(tpm_dev);
22662306a36Sopenharmony_ci		enable_irq(tpm_dev->irq);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci		do {
22962306a36Sopenharmony_ci			if (ret == -ERESTARTSYS && freezing(current))
23062306a36Sopenharmony_ci				clear_thread_flag(TIF_SIGPENDING);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci			timeout = stop - jiffies;
23362306a36Sopenharmony_ci			if ((long) timeout <= 0)
23462306a36Sopenharmony_ci				return -1;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci			ret = wait_event_interruptible_timeout(*queue,
23762306a36Sopenharmony_ci						cur_intrs != tpm_dev->intrs,
23862306a36Sopenharmony_ci						timeout);
23962306a36Sopenharmony_ci			clear_interruption(tpm_dev);
24062306a36Sopenharmony_ci			condition = wait_for_tpm_stat_cond(chip, mask,
24162306a36Sopenharmony_ci						check_cancel, &canceled);
24262306a36Sopenharmony_ci			if (ret >= 0 && condition) {
24362306a36Sopenharmony_ci				if (canceled)
24462306a36Sopenharmony_ci					return -ECANCELED;
24562306a36Sopenharmony_ci				return 0;
24662306a36Sopenharmony_ci			}
24762306a36Sopenharmony_ci		} while (ret == -ERESTARTSYS && freezing(current));
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci		disable_irq_nosync(tpm_dev->irq);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	} else {
25262306a36Sopenharmony_ci		do {
25362306a36Sopenharmony_ci			msleep(TPM_TIMEOUT);
25462306a36Sopenharmony_ci			status = chip->ops->status(chip);
25562306a36Sopenharmony_ci			if ((status & mask) == mask)
25662306a36Sopenharmony_ci				return 0;
25762306a36Sopenharmony_ci		} while (time_before(jiffies, stop));
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	return -ETIME;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
26662306a36Sopenharmony_ci	int size = 0, burstcnt, len, ret;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	while (size < count &&
26962306a36Sopenharmony_ci	       wait_for_stat(chip,
27062306a36Sopenharmony_ci			     TPM_STS_DATA_AVAIL | TPM_STS_VALID,
27162306a36Sopenharmony_ci			     chip->timeout_c,
27262306a36Sopenharmony_ci			     &tpm_dev->read_queue, true) == 0) {
27362306a36Sopenharmony_ci		burstcnt = get_burstcount(chip);
27462306a36Sopenharmony_ci		if (burstcnt < 0)
27562306a36Sopenharmony_ci			return burstcnt;
27662306a36Sopenharmony_ci		len = min_t(int, burstcnt, count - size);
27762306a36Sopenharmony_ci		ret = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_DATA_FIFO,
27862306a36Sopenharmony_ci					 buf + size, len);
27962306a36Sopenharmony_ci		if (ret < 0)
28062306a36Sopenharmony_ci			return ret;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci		size += len;
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci	return size;
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct tpm_chip *chip = dev_id;
29062306a36Sopenharmony_ci	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	tpm_dev->intrs++;
29362306a36Sopenharmony_ci	wake_up_interruptible(&tpm_dev->read_queue);
29462306a36Sopenharmony_ci	disable_irq_nosync(tpm_dev->irq);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	return IRQ_HANDLED;
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci/*
30062306a36Sopenharmony_ci * send TPM commands through the I2C bus.
30162306a36Sopenharmony_ci */
30262306a36Sopenharmony_cistatic int st33zp24_send(struct tpm_chip *chip, unsigned char *buf,
30362306a36Sopenharmony_ci			 size_t len)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
30662306a36Sopenharmony_ci	u32 status, i, size, ordinal;
30762306a36Sopenharmony_ci	int burstcnt = 0;
30862306a36Sopenharmony_ci	int ret;
30962306a36Sopenharmony_ci	u8 data;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	if (len < TPM_HEADER_SIZE)
31262306a36Sopenharmony_ci		return -EBUSY;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	ret = request_locality(chip);
31562306a36Sopenharmony_ci	if (ret < 0)
31662306a36Sopenharmony_ci		return ret;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	status = st33zp24_status(chip);
31962306a36Sopenharmony_ci	if ((status & TPM_STS_COMMAND_READY) == 0) {
32062306a36Sopenharmony_ci		st33zp24_cancel(chip);
32162306a36Sopenharmony_ci		if (wait_for_stat
32262306a36Sopenharmony_ci		    (chip, TPM_STS_COMMAND_READY, chip->timeout_b,
32362306a36Sopenharmony_ci		     &tpm_dev->read_queue, false) < 0) {
32462306a36Sopenharmony_ci			ret = -ETIME;
32562306a36Sopenharmony_ci			goto out_err;
32662306a36Sopenharmony_ci		}
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	for (i = 0; i < len - 1;) {
33062306a36Sopenharmony_ci		burstcnt = get_burstcount(chip);
33162306a36Sopenharmony_ci		if (burstcnt < 0)
33262306a36Sopenharmony_ci			return burstcnt;
33362306a36Sopenharmony_ci		size = min_t(int, len - i - 1, burstcnt);
33462306a36Sopenharmony_ci		ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_DATA_FIFO,
33562306a36Sopenharmony_ci					 buf + i, size);
33662306a36Sopenharmony_ci		if (ret < 0)
33762306a36Sopenharmony_ci			goto out_err;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci		i += size;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	status = st33zp24_status(chip);
34362306a36Sopenharmony_ci	if ((status & TPM_STS_DATA_EXPECT) == 0) {
34462306a36Sopenharmony_ci		ret = -EIO;
34562306a36Sopenharmony_ci		goto out_err;
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_DATA_FIFO,
34962306a36Sopenharmony_ci				 buf + len - 1, 1);
35062306a36Sopenharmony_ci	if (ret < 0)
35162306a36Sopenharmony_ci		goto out_err;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	status = st33zp24_status(chip);
35462306a36Sopenharmony_ci	if ((status & TPM_STS_DATA_EXPECT) != 0) {
35562306a36Sopenharmony_ci		ret = -EIO;
35662306a36Sopenharmony_ci		goto out_err;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	data = TPM_STS_GO;
36062306a36Sopenharmony_ci	ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_STS, &data, 1);
36162306a36Sopenharmony_ci	if (ret < 0)
36262306a36Sopenharmony_ci		goto out_err;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	if (chip->flags & TPM_CHIP_FLAG_IRQ) {
36562306a36Sopenharmony_ci		ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci		ret = wait_for_stat(chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
36862306a36Sopenharmony_ci				tpm_calc_ordinal_duration(chip, ordinal),
36962306a36Sopenharmony_ci				&tpm_dev->read_queue, false);
37062306a36Sopenharmony_ci		if (ret < 0)
37162306a36Sopenharmony_ci			goto out_err;
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	return 0;
37562306a36Sopenharmony_ciout_err:
37662306a36Sopenharmony_ci	st33zp24_cancel(chip);
37762306a36Sopenharmony_ci	release_locality(chip);
37862306a36Sopenharmony_ci	return ret;
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic int st33zp24_recv(struct tpm_chip *chip, unsigned char *buf,
38262306a36Sopenharmony_ci			    size_t count)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	int size = 0;
38562306a36Sopenharmony_ci	u32 expected;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	if (!chip)
38862306a36Sopenharmony_ci		return -EBUSY;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	if (count < TPM_HEADER_SIZE) {
39162306a36Sopenharmony_ci		size = -EIO;
39262306a36Sopenharmony_ci		goto out;
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	size = recv_data(chip, buf, TPM_HEADER_SIZE);
39662306a36Sopenharmony_ci	if (size < TPM_HEADER_SIZE) {
39762306a36Sopenharmony_ci		dev_err(&chip->dev, "Unable to read header\n");
39862306a36Sopenharmony_ci		goto out;
39962306a36Sopenharmony_ci	}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	expected = be32_to_cpu(*(__be32 *)(buf + 2));
40262306a36Sopenharmony_ci	if (expected > count || expected < TPM_HEADER_SIZE) {
40362306a36Sopenharmony_ci		size = -EIO;
40462306a36Sopenharmony_ci		goto out;
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	size += recv_data(chip, &buf[TPM_HEADER_SIZE],
40862306a36Sopenharmony_ci			expected - TPM_HEADER_SIZE);
40962306a36Sopenharmony_ci	if (size < expected) {
41062306a36Sopenharmony_ci		dev_err(&chip->dev, "Unable to read remainder of result\n");
41162306a36Sopenharmony_ci		size = -ETIME;
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ciout:
41562306a36Sopenharmony_ci	st33zp24_cancel(chip);
41662306a36Sopenharmony_ci	release_locality(chip);
41762306a36Sopenharmony_ci	return size;
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_cistatic bool st33zp24_req_canceled(struct tpm_chip *chip, u8 status)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	return (status == TPM_STS_COMMAND_READY);
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_cistatic const struct tpm_class_ops st33zp24_tpm = {
42662306a36Sopenharmony_ci	.flags = TPM_OPS_AUTO_STARTUP,
42762306a36Sopenharmony_ci	.send = st33zp24_send,
42862306a36Sopenharmony_ci	.recv = st33zp24_recv,
42962306a36Sopenharmony_ci	.cancel = st33zp24_cancel,
43062306a36Sopenharmony_ci	.status = st33zp24_status,
43162306a36Sopenharmony_ci	.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
43262306a36Sopenharmony_ci	.req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
43362306a36Sopenharmony_ci	.req_canceled = st33zp24_req_canceled,
43462306a36Sopenharmony_ci};
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_cistatic const struct acpi_gpio_params lpcpd_gpios = { 1, 0, false };
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cistatic const struct acpi_gpio_mapping acpi_st33zp24_gpios[] = {
43962306a36Sopenharmony_ci	{ "lpcpd-gpios", &lpcpd_gpios, 1 },
44062306a36Sopenharmony_ci	{ },
44162306a36Sopenharmony_ci};
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci/*
44462306a36Sopenharmony_ci * initialize the TPM device
44562306a36Sopenharmony_ci */
44662306a36Sopenharmony_ciint st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops,
44762306a36Sopenharmony_ci		   struct device *dev, int irq)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	int ret;
45062306a36Sopenharmony_ci	u8 intmask = 0;
45162306a36Sopenharmony_ci	struct tpm_chip *chip;
45262306a36Sopenharmony_ci	struct st33zp24_dev *tpm_dev;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	chip = tpmm_chip_alloc(dev, &st33zp24_tpm);
45562306a36Sopenharmony_ci	if (IS_ERR(chip))
45662306a36Sopenharmony_ci		return PTR_ERR(chip);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	tpm_dev = devm_kzalloc(dev, sizeof(struct st33zp24_dev),
45962306a36Sopenharmony_ci			       GFP_KERNEL);
46062306a36Sopenharmony_ci	if (!tpm_dev)
46162306a36Sopenharmony_ci		return -ENOMEM;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	tpm_dev->phy_id = phy_id;
46462306a36Sopenharmony_ci	tpm_dev->ops = ops;
46562306a36Sopenharmony_ci	dev_set_drvdata(&chip->dev, tpm_dev);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	chip->timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
46862306a36Sopenharmony_ci	chip->timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
46962306a36Sopenharmony_ci	chip->timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
47062306a36Sopenharmony_ci	chip->timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	tpm_dev->locality = LOCALITY0;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	if (ACPI_COMPANION(dev)) {
47562306a36Sopenharmony_ci		ret = devm_acpi_dev_add_driver_gpios(dev, acpi_st33zp24_gpios);
47662306a36Sopenharmony_ci		if (ret)
47762306a36Sopenharmony_ci			return ret;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	/*
48162306a36Sopenharmony_ci	 * Get LPCPD GPIO. If lpcpd pin is not specified. This is not an
48262306a36Sopenharmony_ci	 * issue as power management can be also managed by TPM specific
48362306a36Sopenharmony_ci	 * commands.
48462306a36Sopenharmony_ci	 */
48562306a36Sopenharmony_ci	tpm_dev->io_lpcpd = devm_gpiod_get_optional(dev, "lpcpd",
48662306a36Sopenharmony_ci						    GPIOD_OUT_HIGH);
48762306a36Sopenharmony_ci	ret = PTR_ERR_OR_ZERO(tpm_dev->io_lpcpd);
48862306a36Sopenharmony_ci	if (ret) {
48962306a36Sopenharmony_ci		dev_err(dev, "failed to request lpcpd gpio: %d\n", ret);
49062306a36Sopenharmony_ci		return ret;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	if (irq) {
49462306a36Sopenharmony_ci		/* INTERRUPT Setup */
49562306a36Sopenharmony_ci		init_waitqueue_head(&tpm_dev->read_queue);
49662306a36Sopenharmony_ci		tpm_dev->intrs = 0;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci		if (request_locality(chip) != LOCALITY0) {
49962306a36Sopenharmony_ci			ret = -ENODEV;
50062306a36Sopenharmony_ci			goto _tpm_clean_answer;
50162306a36Sopenharmony_ci		}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci		clear_interruption(tpm_dev);
50462306a36Sopenharmony_ci		ret = devm_request_irq(dev, irq, tpm_ioserirq_handler,
50562306a36Sopenharmony_ci				IRQF_TRIGGER_HIGH, "TPM SERIRQ management",
50662306a36Sopenharmony_ci				chip);
50762306a36Sopenharmony_ci		if (ret < 0) {
50862306a36Sopenharmony_ci			dev_err(&chip->dev, "TPM SERIRQ signals %d not available\n",
50962306a36Sopenharmony_ci				irq);
51062306a36Sopenharmony_ci			goto _tpm_clean_answer;
51162306a36Sopenharmony_ci		}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci		intmask |= TPM_INTF_CMD_READY_INT
51462306a36Sopenharmony_ci			|  TPM_INTF_STS_VALID_INT
51562306a36Sopenharmony_ci			|  TPM_INTF_DATA_AVAIL_INT;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_INT_ENABLE,
51862306a36Sopenharmony_ci					 &intmask, 1);
51962306a36Sopenharmony_ci		if (ret < 0)
52062306a36Sopenharmony_ci			goto _tpm_clean_answer;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci		intmask = TPM_GLOBAL_INT_ENABLE;
52362306a36Sopenharmony_ci		ret = tpm_dev->ops->send(tpm_dev->phy_id, (TPM_INT_ENABLE + 3),
52462306a36Sopenharmony_ci					 &intmask, 1);
52562306a36Sopenharmony_ci		if (ret < 0)
52662306a36Sopenharmony_ci			goto _tpm_clean_answer;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci		tpm_dev->irq = irq;
52962306a36Sopenharmony_ci		chip->flags |= TPM_CHIP_FLAG_IRQ;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci		disable_irq_nosync(tpm_dev->irq);
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	return tpm_chip_register(chip);
53562306a36Sopenharmony_ci_tpm_clean_answer:
53662306a36Sopenharmony_ci	dev_info(&chip->dev, "TPM initialization fail\n");
53762306a36Sopenharmony_ci	return ret;
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ciEXPORT_SYMBOL(st33zp24_probe);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_civoid st33zp24_remove(struct tpm_chip *chip)
54262306a36Sopenharmony_ci{
54362306a36Sopenharmony_ci	tpm_chip_unregister(chip);
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ciEXPORT_SYMBOL(st33zp24_remove);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
54862306a36Sopenharmony_ciint st33zp24_pm_suspend(struct device *dev)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	struct tpm_chip *chip = dev_get_drvdata(dev);
55162306a36Sopenharmony_ci	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	int ret = 0;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	if (tpm_dev->io_lpcpd)
55662306a36Sopenharmony_ci		gpiod_set_value_cansleep(tpm_dev->io_lpcpd, 0);
55762306a36Sopenharmony_ci	else
55862306a36Sopenharmony_ci		ret = tpm_pm_suspend(dev);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	return ret;
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ciEXPORT_SYMBOL(st33zp24_pm_suspend);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ciint st33zp24_pm_resume(struct device *dev)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	struct tpm_chip *chip = dev_get_drvdata(dev);
56762306a36Sopenharmony_ci	struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
56862306a36Sopenharmony_ci	int ret = 0;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	if (tpm_dev->io_lpcpd) {
57162306a36Sopenharmony_ci		gpiod_set_value_cansleep(tpm_dev->io_lpcpd, 1);
57262306a36Sopenharmony_ci		ret = wait_for_stat(chip,
57362306a36Sopenharmony_ci				TPM_STS_VALID, chip->timeout_b,
57462306a36Sopenharmony_ci				&tpm_dev->read_queue, false);
57562306a36Sopenharmony_ci	} else {
57662306a36Sopenharmony_ci		ret = tpm_pm_resume(dev);
57762306a36Sopenharmony_ci		if (!ret)
57862306a36Sopenharmony_ci			tpm1_do_selftest(chip);
57962306a36Sopenharmony_ci	}
58062306a36Sopenharmony_ci	return ret;
58162306a36Sopenharmony_ci}
58262306a36Sopenharmony_ciEXPORT_SYMBOL(st33zp24_pm_resume);
58362306a36Sopenharmony_ci#endif
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ciMODULE_AUTHOR("TPM support (TPMsupport@list.st.com)");
58662306a36Sopenharmony_ciMODULE_DESCRIPTION("ST33ZP24 TPM 1.2 driver");
58762306a36Sopenharmony_ciMODULE_VERSION("1.3.0");
58862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
589