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