18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Description:
48c2ecf20Sopenharmony_ci * Device Driver for the Infineon Technologies
58c2ecf20Sopenharmony_ci * SLD 9630 TT 1.1 and SLB 9635 TT 1.2 Trusted Platform Module
68c2ecf20Sopenharmony_ci * Specifications at www.trustedcomputinggroup.org
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Copyright (C) 2005, Marcel Selhorst <tpmdd@selhorst.net>
98c2ecf20Sopenharmony_ci * Sirrix AG - security technologies <tpmdd@sirrix.com> and
108c2ecf20Sopenharmony_ci * Applied Data Security Group, Ruhr-University Bochum, Germany
118c2ecf20Sopenharmony_ci * Project-Homepage: http://www.trust.rub.de/projects/linux-device-driver-infineon-tpm/
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/init.h>
158c2ecf20Sopenharmony_ci#include <linux/pnp.h>
168c2ecf20Sopenharmony_ci#include "tpm.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci/* Infineon specific definitions */
198c2ecf20Sopenharmony_ci/* maximum number of WTX-packages */
208c2ecf20Sopenharmony_ci#define	TPM_MAX_WTX_PACKAGES 	50
218c2ecf20Sopenharmony_ci/* msleep-Time for WTX-packages */
228c2ecf20Sopenharmony_ci#define	TPM_WTX_MSLEEP_TIME 	20
238c2ecf20Sopenharmony_ci/* msleep-Time --> Interval to check status register */
248c2ecf20Sopenharmony_ci#define	TPM_MSLEEP_TIME 	3
258c2ecf20Sopenharmony_ci/* gives number of max. msleep()-calls before throwing timeout */
268c2ecf20Sopenharmony_ci#define	TPM_MAX_TRIES		5000
278c2ecf20Sopenharmony_ci#define	TPM_INFINEON_DEV_VEN_VALUE	0x15D1
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define TPM_INF_IO_PORT		0x0
308c2ecf20Sopenharmony_ci#define TPM_INF_IO_MEM		0x1
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define TPM_INF_ADDR		0x0
338c2ecf20Sopenharmony_ci#define TPM_INF_DATA		0x1
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistruct tpm_inf_dev {
368c2ecf20Sopenharmony_ci	int iotype;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	void __iomem *mem_base;	/* MMIO ioremap'd addr */
398c2ecf20Sopenharmony_ci	unsigned long map_base;	/* phys MMIO base */
408c2ecf20Sopenharmony_ci	unsigned long map_size;	/* MMIO region size */
418c2ecf20Sopenharmony_ci	unsigned int index_off;	/* index register offset */
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	unsigned int data_regs;	/* Data registers */
448c2ecf20Sopenharmony_ci	unsigned int data_size;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	unsigned int config_port;	/* IO Port config index reg */
478c2ecf20Sopenharmony_ci	unsigned int config_size;
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic struct tpm_inf_dev tpm_dev;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic inline void tpm_data_out(unsigned char data, unsigned char offset)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	if (tpm_dev.iotype == TPM_INF_IO_PORT)
558c2ecf20Sopenharmony_ci		outb(data, tpm_dev.data_regs + offset);
568c2ecf20Sopenharmony_ci	else
578c2ecf20Sopenharmony_ci		writeb(data, tpm_dev.mem_base + tpm_dev.data_regs + offset);
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic inline unsigned char tpm_data_in(unsigned char offset)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	if (tpm_dev.iotype == TPM_INF_IO_PORT)
638c2ecf20Sopenharmony_ci		return inb(tpm_dev.data_regs + offset);
648c2ecf20Sopenharmony_ci	else
658c2ecf20Sopenharmony_ci		return readb(tpm_dev.mem_base + tpm_dev.data_regs + offset);
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic inline void tpm_config_out(unsigned char data, unsigned char offset)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	if (tpm_dev.iotype == TPM_INF_IO_PORT)
718c2ecf20Sopenharmony_ci		outb(data, tpm_dev.config_port + offset);
728c2ecf20Sopenharmony_ci	else
738c2ecf20Sopenharmony_ci		writeb(data, tpm_dev.mem_base + tpm_dev.index_off + offset);
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic inline unsigned char tpm_config_in(unsigned char offset)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	if (tpm_dev.iotype == TPM_INF_IO_PORT)
798c2ecf20Sopenharmony_ci		return inb(tpm_dev.config_port + offset);
808c2ecf20Sopenharmony_ci	else
818c2ecf20Sopenharmony_ci		return readb(tpm_dev.mem_base + tpm_dev.index_off + offset);
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/* TPM header definitions */
858c2ecf20Sopenharmony_cienum infineon_tpm_header {
868c2ecf20Sopenharmony_ci	TPM_VL_VER = 0x01,
878c2ecf20Sopenharmony_ci	TPM_VL_CHANNEL_CONTROL = 0x07,
888c2ecf20Sopenharmony_ci	TPM_VL_CHANNEL_PERSONALISATION = 0x0A,
898c2ecf20Sopenharmony_ci	TPM_VL_CHANNEL_TPM = 0x0B,
908c2ecf20Sopenharmony_ci	TPM_VL_CONTROL = 0x00,
918c2ecf20Sopenharmony_ci	TPM_INF_NAK = 0x15,
928c2ecf20Sopenharmony_ci	TPM_CTRL_WTX = 0x10,
938c2ecf20Sopenharmony_ci	TPM_CTRL_WTX_ABORT = 0x18,
948c2ecf20Sopenharmony_ci	TPM_CTRL_WTX_ABORT_ACK = 0x18,
958c2ecf20Sopenharmony_ci	TPM_CTRL_ERROR = 0x20,
968c2ecf20Sopenharmony_ci	TPM_CTRL_CHAININGACK = 0x40,
978c2ecf20Sopenharmony_ci	TPM_CTRL_CHAINING = 0x80,
988c2ecf20Sopenharmony_ci	TPM_CTRL_DATA = 0x04,
998c2ecf20Sopenharmony_ci	TPM_CTRL_DATA_CHA = 0x84,
1008c2ecf20Sopenharmony_ci	TPM_CTRL_DATA_CHA_ACK = 0xC4
1018c2ecf20Sopenharmony_ci};
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cienum infineon_tpm_register {
1048c2ecf20Sopenharmony_ci	WRFIFO = 0x00,
1058c2ecf20Sopenharmony_ci	RDFIFO = 0x01,
1068c2ecf20Sopenharmony_ci	STAT = 0x02,
1078c2ecf20Sopenharmony_ci	CMD = 0x03
1088c2ecf20Sopenharmony_ci};
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cienum infineon_tpm_command_bits {
1118c2ecf20Sopenharmony_ci	CMD_DIS = 0x00,
1128c2ecf20Sopenharmony_ci	CMD_LP = 0x01,
1138c2ecf20Sopenharmony_ci	CMD_RES = 0x02,
1148c2ecf20Sopenharmony_ci	CMD_IRQC = 0x06
1158c2ecf20Sopenharmony_ci};
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cienum infineon_tpm_status_bits {
1188c2ecf20Sopenharmony_ci	STAT_XFE = 0x00,
1198c2ecf20Sopenharmony_ci	STAT_LPA = 0x01,
1208c2ecf20Sopenharmony_ci	STAT_FOK = 0x02,
1218c2ecf20Sopenharmony_ci	STAT_TOK = 0x03,
1228c2ecf20Sopenharmony_ci	STAT_IRQA = 0x06,
1238c2ecf20Sopenharmony_ci	STAT_RDA = 0x07
1248c2ecf20Sopenharmony_ci};
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci/* some outgoing values */
1278c2ecf20Sopenharmony_cienum infineon_tpm_values {
1288c2ecf20Sopenharmony_ci	CHIP_ID1 = 0x20,
1298c2ecf20Sopenharmony_ci	CHIP_ID2 = 0x21,
1308c2ecf20Sopenharmony_ci	TPM_DAR = 0x30,
1318c2ecf20Sopenharmony_ci	RESET_LP_IRQC_DISABLE = 0x41,
1328c2ecf20Sopenharmony_ci	ENABLE_REGISTER_PAIR = 0x55,
1338c2ecf20Sopenharmony_ci	IOLIMH = 0x60,
1348c2ecf20Sopenharmony_ci	IOLIML = 0x61,
1358c2ecf20Sopenharmony_ci	DISABLE_REGISTER_PAIR = 0xAA,
1368c2ecf20Sopenharmony_ci	IDVENL = 0xF1,
1378c2ecf20Sopenharmony_ci	IDVENH = 0xF2,
1388c2ecf20Sopenharmony_ci	IDPDL = 0xF3,
1398c2ecf20Sopenharmony_ci	IDPDH = 0xF4
1408c2ecf20Sopenharmony_ci};
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic int number_of_wtx;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic int empty_fifo(struct tpm_chip *chip, int clear_wrfifo)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	int status;
1478c2ecf20Sopenharmony_ci	int check = 0;
1488c2ecf20Sopenharmony_ci	int i;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (clear_wrfifo) {
1518c2ecf20Sopenharmony_ci		for (i = 0; i < 4096; i++) {
1528c2ecf20Sopenharmony_ci			status = tpm_data_in(WRFIFO);
1538c2ecf20Sopenharmony_ci			if (status == 0xff) {
1548c2ecf20Sopenharmony_ci				if (check == 5)
1558c2ecf20Sopenharmony_ci					break;
1568c2ecf20Sopenharmony_ci				else
1578c2ecf20Sopenharmony_ci					check++;
1588c2ecf20Sopenharmony_ci			}
1598c2ecf20Sopenharmony_ci		}
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci	/* Note: The values which are currently in the FIFO of the TPM
1628c2ecf20Sopenharmony_ci	   are thrown away since there is no usage for them. Usually,
1638c2ecf20Sopenharmony_ci	   this has nothing to say, since the TPM will give its answer
1648c2ecf20Sopenharmony_ci	   immediately or will be aborted anyway, so the data here is
1658c2ecf20Sopenharmony_ci	   usually garbage and useless.
1668c2ecf20Sopenharmony_ci	   We have to clean this, because the next communication with
1678c2ecf20Sopenharmony_ci	   the TPM would be rubbish, if there is still some old data
1688c2ecf20Sopenharmony_ci	   in the Read FIFO.
1698c2ecf20Sopenharmony_ci	 */
1708c2ecf20Sopenharmony_ci	i = 0;
1718c2ecf20Sopenharmony_ci	do {
1728c2ecf20Sopenharmony_ci		status = tpm_data_in(RDFIFO);
1738c2ecf20Sopenharmony_ci		status = tpm_data_in(STAT);
1748c2ecf20Sopenharmony_ci		i++;
1758c2ecf20Sopenharmony_ci		if (i == TPM_MAX_TRIES)
1768c2ecf20Sopenharmony_ci			return -EIO;
1778c2ecf20Sopenharmony_ci	} while ((status & (1 << STAT_RDA)) != 0);
1788c2ecf20Sopenharmony_ci	return 0;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic int wait(struct tpm_chip *chip, int wait_for_bit)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	int status;
1848c2ecf20Sopenharmony_ci	int i;
1858c2ecf20Sopenharmony_ci	for (i = 0; i < TPM_MAX_TRIES; i++) {
1868c2ecf20Sopenharmony_ci		status = tpm_data_in(STAT);
1878c2ecf20Sopenharmony_ci		/* check the status-register if wait_for_bit is set */
1888c2ecf20Sopenharmony_ci		if (status & 1 << wait_for_bit)
1898c2ecf20Sopenharmony_ci			break;
1908c2ecf20Sopenharmony_ci		tpm_msleep(TPM_MSLEEP_TIME);
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci	if (i == TPM_MAX_TRIES) {	/* timeout occurs */
1938c2ecf20Sopenharmony_ci		if (wait_for_bit == STAT_XFE)
1948c2ecf20Sopenharmony_ci			dev_err(&chip->dev, "Timeout in wait(STAT_XFE)\n");
1958c2ecf20Sopenharmony_ci		if (wait_for_bit == STAT_RDA)
1968c2ecf20Sopenharmony_ci			dev_err(&chip->dev, "Timeout in wait(STAT_RDA)\n");
1978c2ecf20Sopenharmony_ci		return -EIO;
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci	return 0;
2008c2ecf20Sopenharmony_ci};
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic void wait_and_send(struct tpm_chip *chip, u8 sendbyte)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	wait(chip, STAT_XFE);
2058c2ecf20Sopenharmony_ci	tpm_data_out(sendbyte, WRFIFO);
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci    /* Note: WTX means Waiting-Time-Extension. Whenever the TPM needs more
2098c2ecf20Sopenharmony_ci       calculation time, it sends a WTX-package, which has to be acknowledged
2108c2ecf20Sopenharmony_ci       or aborted. This usually occurs if you are hammering the TPM with key
2118c2ecf20Sopenharmony_ci       creation. Set the maximum number of WTX-packages in the definitions
2128c2ecf20Sopenharmony_ci       above, if the number is reached, the waiting-time will be denied
2138c2ecf20Sopenharmony_ci       and the TPM command has to be resend.
2148c2ecf20Sopenharmony_ci     */
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic void tpm_wtx(struct tpm_chip *chip)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	number_of_wtx++;
2198c2ecf20Sopenharmony_ci	dev_info(&chip->dev, "Granting WTX (%02d / %02d)\n",
2208c2ecf20Sopenharmony_ci		 number_of_wtx, TPM_MAX_WTX_PACKAGES);
2218c2ecf20Sopenharmony_ci	wait_and_send(chip, TPM_VL_VER);
2228c2ecf20Sopenharmony_ci	wait_and_send(chip, TPM_CTRL_WTX);
2238c2ecf20Sopenharmony_ci	wait_and_send(chip, 0x00);
2248c2ecf20Sopenharmony_ci	wait_and_send(chip, 0x00);
2258c2ecf20Sopenharmony_ci	tpm_msleep(TPM_WTX_MSLEEP_TIME);
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic void tpm_wtx_abort(struct tpm_chip *chip)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	dev_info(&chip->dev, "Aborting WTX\n");
2318c2ecf20Sopenharmony_ci	wait_and_send(chip, TPM_VL_VER);
2328c2ecf20Sopenharmony_ci	wait_and_send(chip, TPM_CTRL_WTX_ABORT);
2338c2ecf20Sopenharmony_ci	wait_and_send(chip, 0x00);
2348c2ecf20Sopenharmony_ci	wait_and_send(chip, 0x00);
2358c2ecf20Sopenharmony_ci	number_of_wtx = 0;
2368c2ecf20Sopenharmony_ci	tpm_msleep(TPM_WTX_MSLEEP_TIME);
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic int tpm_inf_recv(struct tpm_chip *chip, u8 * buf, size_t count)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	int i;
2428c2ecf20Sopenharmony_ci	int ret;
2438c2ecf20Sopenharmony_ci	u32 size = 0;
2448c2ecf20Sopenharmony_ci	number_of_wtx = 0;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cirecv_begin:
2478c2ecf20Sopenharmony_ci	/* start receiving header */
2488c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++) {
2498c2ecf20Sopenharmony_ci		ret = wait(chip, STAT_RDA);
2508c2ecf20Sopenharmony_ci		if (ret)
2518c2ecf20Sopenharmony_ci			return -EIO;
2528c2ecf20Sopenharmony_ci		buf[i] = tpm_data_in(RDFIFO);
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	if (buf[0] != TPM_VL_VER) {
2568c2ecf20Sopenharmony_ci		dev_err(&chip->dev,
2578c2ecf20Sopenharmony_ci			"Wrong transport protocol implementation!\n");
2588c2ecf20Sopenharmony_ci		return -EIO;
2598c2ecf20Sopenharmony_ci	}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	if (buf[1] == TPM_CTRL_DATA) {
2628c2ecf20Sopenharmony_ci		/* size of the data received */
2638c2ecf20Sopenharmony_ci		size = ((buf[2] << 8) | buf[3]);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci		for (i = 0; i < size; i++) {
2668c2ecf20Sopenharmony_ci			wait(chip, STAT_RDA);
2678c2ecf20Sopenharmony_ci			buf[i] = tpm_data_in(RDFIFO);
2688c2ecf20Sopenharmony_ci		}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci		if ((size == 0x6D00) && (buf[1] == 0x80)) {
2718c2ecf20Sopenharmony_ci			dev_err(&chip->dev, "Error handling on vendor layer!\n");
2728c2ecf20Sopenharmony_ci			return -EIO;
2738c2ecf20Sopenharmony_ci		}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci		for (i = 0; i < size; i++)
2768c2ecf20Sopenharmony_ci			buf[i] = buf[i + 6];
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci		size = size - 6;
2798c2ecf20Sopenharmony_ci		return size;
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	if (buf[1] == TPM_CTRL_WTX) {
2838c2ecf20Sopenharmony_ci		dev_info(&chip->dev, "WTX-package received\n");
2848c2ecf20Sopenharmony_ci		if (number_of_wtx < TPM_MAX_WTX_PACKAGES) {
2858c2ecf20Sopenharmony_ci			tpm_wtx(chip);
2868c2ecf20Sopenharmony_ci			goto recv_begin;
2878c2ecf20Sopenharmony_ci		} else {
2888c2ecf20Sopenharmony_ci			tpm_wtx_abort(chip);
2898c2ecf20Sopenharmony_ci			goto recv_begin;
2908c2ecf20Sopenharmony_ci		}
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	if (buf[1] == TPM_CTRL_WTX_ABORT_ACK) {
2948c2ecf20Sopenharmony_ci		dev_info(&chip->dev, "WTX-abort acknowledged\n");
2958c2ecf20Sopenharmony_ci		return size;
2968c2ecf20Sopenharmony_ci	}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	if (buf[1] == TPM_CTRL_ERROR) {
2998c2ecf20Sopenharmony_ci		dev_err(&chip->dev, "ERROR-package received:\n");
3008c2ecf20Sopenharmony_ci		if (buf[4] == TPM_INF_NAK)
3018c2ecf20Sopenharmony_ci			dev_err(&chip->dev,
3028c2ecf20Sopenharmony_ci				"-> Negative acknowledgement"
3038c2ecf20Sopenharmony_ci				" - retransmit command!\n");
3048c2ecf20Sopenharmony_ci		return -EIO;
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci	return -EIO;
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic int tpm_inf_send(struct tpm_chip *chip, u8 * buf, size_t count)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	int i;
3128c2ecf20Sopenharmony_ci	int ret;
3138c2ecf20Sopenharmony_ci	u8 count_high, count_low, count_4, count_3, count_2, count_1;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	/* Disabling Reset, LP and IRQC */
3168c2ecf20Sopenharmony_ci	tpm_data_out(RESET_LP_IRQC_DISABLE, CMD);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	ret = empty_fifo(chip, 1);
3198c2ecf20Sopenharmony_ci	if (ret) {
3208c2ecf20Sopenharmony_ci		dev_err(&chip->dev, "Timeout while clearing FIFO\n");
3218c2ecf20Sopenharmony_ci		return -EIO;
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	ret = wait(chip, STAT_XFE);
3258c2ecf20Sopenharmony_ci	if (ret)
3268c2ecf20Sopenharmony_ci		return -EIO;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	count_4 = (count & 0xff000000) >> 24;
3298c2ecf20Sopenharmony_ci	count_3 = (count & 0x00ff0000) >> 16;
3308c2ecf20Sopenharmony_ci	count_2 = (count & 0x0000ff00) >> 8;
3318c2ecf20Sopenharmony_ci	count_1 = (count & 0x000000ff);
3328c2ecf20Sopenharmony_ci	count_high = ((count + 6) & 0xffffff00) >> 8;
3338c2ecf20Sopenharmony_ci	count_low = ((count + 6) & 0x000000ff);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	/* Sending Header */
3368c2ecf20Sopenharmony_ci	wait_and_send(chip, TPM_VL_VER);
3378c2ecf20Sopenharmony_ci	wait_and_send(chip, TPM_CTRL_DATA);
3388c2ecf20Sopenharmony_ci	wait_and_send(chip, count_high);
3398c2ecf20Sopenharmony_ci	wait_and_send(chip, count_low);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	/* Sending Data Header */
3428c2ecf20Sopenharmony_ci	wait_and_send(chip, TPM_VL_VER);
3438c2ecf20Sopenharmony_ci	wait_and_send(chip, TPM_VL_CHANNEL_TPM);
3448c2ecf20Sopenharmony_ci	wait_and_send(chip, count_4);
3458c2ecf20Sopenharmony_ci	wait_and_send(chip, count_3);
3468c2ecf20Sopenharmony_ci	wait_and_send(chip, count_2);
3478c2ecf20Sopenharmony_ci	wait_and_send(chip, count_1);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	/* Sending Data */
3508c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
3518c2ecf20Sopenharmony_ci		wait_and_send(chip, buf[i]);
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci	return 0;
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cistatic void tpm_inf_cancel(struct tpm_chip *chip)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	/*
3598c2ecf20Sopenharmony_ci	   Since we are using the legacy mode to communicate
3608c2ecf20Sopenharmony_ci	   with the TPM, we have no cancel functions, but have
3618c2ecf20Sopenharmony_ci	   a workaround for interrupting the TPM through WTX.
3628c2ecf20Sopenharmony_ci	 */
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic u8 tpm_inf_status(struct tpm_chip *chip)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	return tpm_data_in(STAT);
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cistatic const struct tpm_class_ops tpm_inf = {
3718c2ecf20Sopenharmony_ci	.recv = tpm_inf_recv,
3728c2ecf20Sopenharmony_ci	.send = tpm_inf_send,
3738c2ecf20Sopenharmony_ci	.cancel = tpm_inf_cancel,
3748c2ecf20Sopenharmony_ci	.status = tpm_inf_status,
3758c2ecf20Sopenharmony_ci	.req_complete_mask = 0,
3768c2ecf20Sopenharmony_ci	.req_complete_val = 0,
3778c2ecf20Sopenharmony_ci};
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic const struct pnp_device_id tpm_inf_pnp_tbl[] = {
3808c2ecf20Sopenharmony_ci	/* Infineon TPMs */
3818c2ecf20Sopenharmony_ci	{"IFX0101", 0},
3828c2ecf20Sopenharmony_ci	{"IFX0102", 0},
3838c2ecf20Sopenharmony_ci	{"", 0}
3848c2ecf20Sopenharmony_ci};
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pnp, tpm_inf_pnp_tbl);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic int tpm_inf_pnp_probe(struct pnp_dev *dev,
3898c2ecf20Sopenharmony_ci				       const struct pnp_device_id *dev_id)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	int rc = 0;
3928c2ecf20Sopenharmony_ci	u8 iol, ioh;
3938c2ecf20Sopenharmony_ci	int vendorid[2];
3948c2ecf20Sopenharmony_ci	int version[2];
3958c2ecf20Sopenharmony_ci	int productid[2];
3968c2ecf20Sopenharmony_ci	const char *chipname;
3978c2ecf20Sopenharmony_ci	struct tpm_chip *chip;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	/* read IO-ports through PnP */
4008c2ecf20Sopenharmony_ci	if (pnp_port_valid(dev, 0) && pnp_port_valid(dev, 1) &&
4018c2ecf20Sopenharmony_ci	    !(pnp_port_flags(dev, 0) & IORESOURCE_DISABLED)) {
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci		tpm_dev.iotype = TPM_INF_IO_PORT;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci		tpm_dev.config_port = pnp_port_start(dev, 0);
4068c2ecf20Sopenharmony_ci		tpm_dev.config_size = pnp_port_len(dev, 0);
4078c2ecf20Sopenharmony_ci		tpm_dev.data_regs = pnp_port_start(dev, 1);
4088c2ecf20Sopenharmony_ci		tpm_dev.data_size = pnp_port_len(dev, 1);
4098c2ecf20Sopenharmony_ci		if ((tpm_dev.data_size < 4) || (tpm_dev.config_size < 2)) {
4108c2ecf20Sopenharmony_ci			rc = -EINVAL;
4118c2ecf20Sopenharmony_ci			goto err_last;
4128c2ecf20Sopenharmony_ci		}
4138c2ecf20Sopenharmony_ci		dev_info(&dev->dev, "Found %s with ID %s\n",
4148c2ecf20Sopenharmony_ci			 dev->name, dev_id->id);
4158c2ecf20Sopenharmony_ci		if (!((tpm_dev.data_regs >> 8) & 0xff)) {
4168c2ecf20Sopenharmony_ci			rc = -EINVAL;
4178c2ecf20Sopenharmony_ci			goto err_last;
4188c2ecf20Sopenharmony_ci		}
4198c2ecf20Sopenharmony_ci		/* publish my base address and request region */
4208c2ecf20Sopenharmony_ci		if (request_region(tpm_dev.data_regs, tpm_dev.data_size,
4218c2ecf20Sopenharmony_ci				   "tpm_infineon0") == NULL) {
4228c2ecf20Sopenharmony_ci			rc = -EINVAL;
4238c2ecf20Sopenharmony_ci			goto err_last;
4248c2ecf20Sopenharmony_ci		}
4258c2ecf20Sopenharmony_ci		if (request_region(tpm_dev.config_port, tpm_dev.config_size,
4268c2ecf20Sopenharmony_ci				   "tpm_infineon0") == NULL) {
4278c2ecf20Sopenharmony_ci			release_region(tpm_dev.data_regs, tpm_dev.data_size);
4288c2ecf20Sopenharmony_ci			rc = -EINVAL;
4298c2ecf20Sopenharmony_ci			goto err_last;
4308c2ecf20Sopenharmony_ci		}
4318c2ecf20Sopenharmony_ci	} else if (pnp_mem_valid(dev, 0) &&
4328c2ecf20Sopenharmony_ci		   !(pnp_mem_flags(dev, 0) & IORESOURCE_DISABLED)) {
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci		tpm_dev.iotype = TPM_INF_IO_MEM;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci		tpm_dev.map_base = pnp_mem_start(dev, 0);
4378c2ecf20Sopenharmony_ci		tpm_dev.map_size = pnp_mem_len(dev, 0);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci		dev_info(&dev->dev, "Found %s with ID %s\n",
4408c2ecf20Sopenharmony_ci			 dev->name, dev_id->id);
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci		/* publish my base address and request region */
4438c2ecf20Sopenharmony_ci		if (request_mem_region(tpm_dev.map_base, tpm_dev.map_size,
4448c2ecf20Sopenharmony_ci				       "tpm_infineon0") == NULL) {
4458c2ecf20Sopenharmony_ci			rc = -EINVAL;
4468c2ecf20Sopenharmony_ci			goto err_last;
4478c2ecf20Sopenharmony_ci		}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci		tpm_dev.mem_base = ioremap(tpm_dev.map_base, tpm_dev.map_size);
4508c2ecf20Sopenharmony_ci		if (tpm_dev.mem_base == NULL) {
4518c2ecf20Sopenharmony_ci			release_mem_region(tpm_dev.map_base, tpm_dev.map_size);
4528c2ecf20Sopenharmony_ci			rc = -EINVAL;
4538c2ecf20Sopenharmony_ci			goto err_last;
4548c2ecf20Sopenharmony_ci		}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci		/*
4578c2ecf20Sopenharmony_ci		 * The only known MMIO based Infineon TPM system provides
4588c2ecf20Sopenharmony_ci		 * a single large mem region with the device config
4598c2ecf20Sopenharmony_ci		 * registers at the default TPM_ADDR.  The data registers
4608c2ecf20Sopenharmony_ci		 * seem like they could be placed anywhere within the MMIO
4618c2ecf20Sopenharmony_ci		 * region, but lets just put them at zero offset.
4628c2ecf20Sopenharmony_ci		 */
4638c2ecf20Sopenharmony_ci		tpm_dev.index_off = TPM_ADDR;
4648c2ecf20Sopenharmony_ci		tpm_dev.data_regs = 0x0;
4658c2ecf20Sopenharmony_ci	} else {
4668c2ecf20Sopenharmony_ci		rc = -EINVAL;
4678c2ecf20Sopenharmony_ci		goto err_last;
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	/* query chip for its vendor, its version number a.s.o. */
4718c2ecf20Sopenharmony_ci	tpm_config_out(ENABLE_REGISTER_PAIR, TPM_INF_ADDR);
4728c2ecf20Sopenharmony_ci	tpm_config_out(IDVENL, TPM_INF_ADDR);
4738c2ecf20Sopenharmony_ci	vendorid[1] = tpm_config_in(TPM_INF_DATA);
4748c2ecf20Sopenharmony_ci	tpm_config_out(IDVENH, TPM_INF_ADDR);
4758c2ecf20Sopenharmony_ci	vendorid[0] = tpm_config_in(TPM_INF_DATA);
4768c2ecf20Sopenharmony_ci	tpm_config_out(IDPDL, TPM_INF_ADDR);
4778c2ecf20Sopenharmony_ci	productid[1] = tpm_config_in(TPM_INF_DATA);
4788c2ecf20Sopenharmony_ci	tpm_config_out(IDPDH, TPM_INF_ADDR);
4798c2ecf20Sopenharmony_ci	productid[0] = tpm_config_in(TPM_INF_DATA);
4808c2ecf20Sopenharmony_ci	tpm_config_out(CHIP_ID1, TPM_INF_ADDR);
4818c2ecf20Sopenharmony_ci	version[1] = tpm_config_in(TPM_INF_DATA);
4828c2ecf20Sopenharmony_ci	tpm_config_out(CHIP_ID2, TPM_INF_ADDR);
4838c2ecf20Sopenharmony_ci	version[0] = tpm_config_in(TPM_INF_DATA);
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	switch ((productid[0] << 8) | productid[1]) {
4868c2ecf20Sopenharmony_ci	case 6:
4878c2ecf20Sopenharmony_ci		chipname = " (SLD 9630 TT 1.1)";
4888c2ecf20Sopenharmony_ci		break;
4898c2ecf20Sopenharmony_ci	case 11:
4908c2ecf20Sopenharmony_ci		chipname = " (SLB 9635 TT 1.2)";
4918c2ecf20Sopenharmony_ci		break;
4928c2ecf20Sopenharmony_ci	default:
4938c2ecf20Sopenharmony_ci		chipname = " (unknown chip)";
4948c2ecf20Sopenharmony_ci		break;
4958c2ecf20Sopenharmony_ci	}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	if ((vendorid[0] << 8 | vendorid[1]) == (TPM_INFINEON_DEV_VEN_VALUE)) {
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci		/* configure TPM with IO-ports */
5008c2ecf20Sopenharmony_ci		tpm_config_out(IOLIMH, TPM_INF_ADDR);
5018c2ecf20Sopenharmony_ci		tpm_config_out((tpm_dev.data_regs >> 8) & 0xff, TPM_INF_DATA);
5028c2ecf20Sopenharmony_ci		tpm_config_out(IOLIML, TPM_INF_ADDR);
5038c2ecf20Sopenharmony_ci		tpm_config_out((tpm_dev.data_regs & 0xff), TPM_INF_DATA);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci		/* control if IO-ports are set correctly */
5068c2ecf20Sopenharmony_ci		tpm_config_out(IOLIMH, TPM_INF_ADDR);
5078c2ecf20Sopenharmony_ci		ioh = tpm_config_in(TPM_INF_DATA);
5088c2ecf20Sopenharmony_ci		tpm_config_out(IOLIML, TPM_INF_ADDR);
5098c2ecf20Sopenharmony_ci		iol = tpm_config_in(TPM_INF_DATA);
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci		if ((ioh << 8 | iol) != tpm_dev.data_regs) {
5128c2ecf20Sopenharmony_ci			dev_err(&dev->dev,
5138c2ecf20Sopenharmony_ci				"Could not set IO-data registers to 0x%x\n",
5148c2ecf20Sopenharmony_ci				tpm_dev.data_regs);
5158c2ecf20Sopenharmony_ci			rc = -EIO;
5168c2ecf20Sopenharmony_ci			goto err_release_region;
5178c2ecf20Sopenharmony_ci		}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci		/* activate register */
5208c2ecf20Sopenharmony_ci		tpm_config_out(TPM_DAR, TPM_INF_ADDR);
5218c2ecf20Sopenharmony_ci		tpm_config_out(0x01, TPM_INF_DATA);
5228c2ecf20Sopenharmony_ci		tpm_config_out(DISABLE_REGISTER_PAIR, TPM_INF_ADDR);
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci		/* disable RESET, LP and IRQC */
5258c2ecf20Sopenharmony_ci		tpm_data_out(RESET_LP_IRQC_DISABLE, CMD);
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci		/* Finally, we're done, print some infos */
5288c2ecf20Sopenharmony_ci		dev_info(&dev->dev, "TPM found: "
5298c2ecf20Sopenharmony_ci			 "config base 0x%lx, "
5308c2ecf20Sopenharmony_ci			 "data base 0x%lx, "
5318c2ecf20Sopenharmony_ci			 "chip version 0x%02x%02x, "
5328c2ecf20Sopenharmony_ci			 "vendor id 0x%x%x (Infineon), "
5338c2ecf20Sopenharmony_ci			 "product id 0x%02x%02x"
5348c2ecf20Sopenharmony_ci			 "%s\n",
5358c2ecf20Sopenharmony_ci			 tpm_dev.iotype == TPM_INF_IO_PORT ?
5368c2ecf20Sopenharmony_ci			 tpm_dev.config_port :
5378c2ecf20Sopenharmony_ci			 tpm_dev.map_base + tpm_dev.index_off,
5388c2ecf20Sopenharmony_ci			 tpm_dev.iotype == TPM_INF_IO_PORT ?
5398c2ecf20Sopenharmony_ci			 tpm_dev.data_regs :
5408c2ecf20Sopenharmony_ci			 tpm_dev.map_base + tpm_dev.data_regs,
5418c2ecf20Sopenharmony_ci			 version[0], version[1],
5428c2ecf20Sopenharmony_ci			 vendorid[0], vendorid[1],
5438c2ecf20Sopenharmony_ci			 productid[0], productid[1], chipname);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci		chip = tpmm_chip_alloc(&dev->dev, &tpm_inf);
5468c2ecf20Sopenharmony_ci		if (IS_ERR(chip)) {
5478c2ecf20Sopenharmony_ci			rc = PTR_ERR(chip);
5488c2ecf20Sopenharmony_ci			goto err_release_region;
5498c2ecf20Sopenharmony_ci		}
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci		rc = tpm_chip_register(chip);
5528c2ecf20Sopenharmony_ci		if (rc)
5538c2ecf20Sopenharmony_ci			goto err_release_region;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci		return 0;
5568c2ecf20Sopenharmony_ci	} else {
5578c2ecf20Sopenharmony_ci		rc = -ENODEV;
5588c2ecf20Sopenharmony_ci		goto err_release_region;
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cierr_release_region:
5628c2ecf20Sopenharmony_ci	if (tpm_dev.iotype == TPM_INF_IO_PORT) {
5638c2ecf20Sopenharmony_ci		release_region(tpm_dev.data_regs, tpm_dev.data_size);
5648c2ecf20Sopenharmony_ci		release_region(tpm_dev.config_port, tpm_dev.config_size);
5658c2ecf20Sopenharmony_ci	} else {
5668c2ecf20Sopenharmony_ci		iounmap(tpm_dev.mem_base);
5678c2ecf20Sopenharmony_ci		release_mem_region(tpm_dev.map_base, tpm_dev.map_size);
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_cierr_last:
5718c2ecf20Sopenharmony_ci	return rc;
5728c2ecf20Sopenharmony_ci}
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_cistatic void tpm_inf_pnp_remove(struct pnp_dev *dev)
5758c2ecf20Sopenharmony_ci{
5768c2ecf20Sopenharmony_ci	struct tpm_chip *chip = pnp_get_drvdata(dev);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	tpm_chip_unregister(chip);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	if (tpm_dev.iotype == TPM_INF_IO_PORT) {
5818c2ecf20Sopenharmony_ci		release_region(tpm_dev.data_regs, tpm_dev.data_size);
5828c2ecf20Sopenharmony_ci		release_region(tpm_dev.config_port,
5838c2ecf20Sopenharmony_ci			       tpm_dev.config_size);
5848c2ecf20Sopenharmony_ci	} else {
5858c2ecf20Sopenharmony_ci		iounmap(tpm_dev.mem_base);
5868c2ecf20Sopenharmony_ci		release_mem_region(tpm_dev.map_base, tpm_dev.map_size);
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
5918c2ecf20Sopenharmony_cistatic int tpm_inf_resume(struct device *dev)
5928c2ecf20Sopenharmony_ci{
5938c2ecf20Sopenharmony_ci	/* Re-configure TPM after suspending */
5948c2ecf20Sopenharmony_ci	tpm_config_out(ENABLE_REGISTER_PAIR, TPM_INF_ADDR);
5958c2ecf20Sopenharmony_ci	tpm_config_out(IOLIMH, TPM_INF_ADDR);
5968c2ecf20Sopenharmony_ci	tpm_config_out((tpm_dev.data_regs >> 8) & 0xff, TPM_INF_DATA);
5978c2ecf20Sopenharmony_ci	tpm_config_out(IOLIML, TPM_INF_ADDR);
5988c2ecf20Sopenharmony_ci	tpm_config_out((tpm_dev.data_regs & 0xff), TPM_INF_DATA);
5998c2ecf20Sopenharmony_ci	/* activate register */
6008c2ecf20Sopenharmony_ci	tpm_config_out(TPM_DAR, TPM_INF_ADDR);
6018c2ecf20Sopenharmony_ci	tpm_config_out(0x01, TPM_INF_DATA);
6028c2ecf20Sopenharmony_ci	tpm_config_out(DISABLE_REGISTER_PAIR, TPM_INF_ADDR);
6038c2ecf20Sopenharmony_ci	/* disable RESET, LP and IRQC */
6048c2ecf20Sopenharmony_ci	tpm_data_out(RESET_LP_IRQC_DISABLE, CMD);
6058c2ecf20Sopenharmony_ci	return tpm_pm_resume(dev);
6068c2ecf20Sopenharmony_ci}
6078c2ecf20Sopenharmony_ci#endif
6088c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(tpm_inf_pm, tpm_pm_suspend, tpm_inf_resume);
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_cistatic struct pnp_driver tpm_inf_pnp_driver = {
6118c2ecf20Sopenharmony_ci	.name = "tpm_inf_pnp",
6128c2ecf20Sopenharmony_ci	.id_table = tpm_inf_pnp_tbl,
6138c2ecf20Sopenharmony_ci	.probe = tpm_inf_pnp_probe,
6148c2ecf20Sopenharmony_ci	.remove = tpm_inf_pnp_remove,
6158c2ecf20Sopenharmony_ci	.driver = {
6168c2ecf20Sopenharmony_ci		.pm = &tpm_inf_pm,
6178c2ecf20Sopenharmony_ci	}
6188c2ecf20Sopenharmony_ci};
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_cimodule_pnp_driver(tpm_inf_pnp_driver);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marcel Selhorst <tpmdd@sirrix.com>");
6238c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for Infineon TPM SLD 9630 TT 1.1 / SLB 9635 TT 1.2");
6248c2ecf20Sopenharmony_ciMODULE_VERSION("1.9.2");
6258c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
626