18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2005 IBM Corporation 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: 68c2ecf20Sopenharmony_ci * Kylene Hall <kjhall@us.ibm.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Maintained by: <tpmdd-devel@lists.sourceforge.net> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Device driver for TCG/TCPA TPM (trusted platform module). 118c2ecf20Sopenharmony_ci * Specifications at www.trustedcomputinggroup.org 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * These difference are required on power because the device must be 148c2ecf20Sopenharmony_ci * discovered through the device tree and iomap must be used to get 158c2ecf20Sopenharmony_ci * around the need for holes in the io_page_mask. This does not happen 168c2ecf20Sopenharmony_ci * automatically because the tpm is not a normal pci device and lives 178c2ecf20Sopenharmony_ci * under the root node. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct tpm_atmel_priv { 218c2ecf20Sopenharmony_ci int region_size; 228c2ecf20Sopenharmony_ci int have_region; 238c2ecf20Sopenharmony_ci unsigned long base; 248c2ecf20Sopenharmony_ci void __iomem *iobase; 258c2ecf20Sopenharmony_ci}; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <asm/prom.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define atmel_getb(priv, offset) readb(priv->iobase + offset) 328c2ecf20Sopenharmony_ci#define atmel_putb(val, priv, offset) writeb(val, priv->iobase + offset) 338c2ecf20Sopenharmony_ci#define atmel_request_region request_mem_region 348c2ecf20Sopenharmony_ci#define atmel_release_region release_mem_region 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic inline void atmel_put_base_addr(void __iomem *iobase) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci iounmap(iobase); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic void __iomem * atmel_get_base_addr(unsigned long *base, int *region_size) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct device_node *dn; 448c2ecf20Sopenharmony_ci unsigned long address, size; 458c2ecf20Sopenharmony_ci const unsigned int *reg; 468c2ecf20Sopenharmony_ci int reglen; 478c2ecf20Sopenharmony_ci int naddrc; 488c2ecf20Sopenharmony_ci int nsizec; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci dn = of_find_node_by_name(NULL, "tpm"); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (!dn) 538c2ecf20Sopenharmony_ci return NULL; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (!of_device_is_compatible(dn, "AT97SC3201")) { 568c2ecf20Sopenharmony_ci of_node_put(dn); 578c2ecf20Sopenharmony_ci return NULL; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci reg = of_get_property(dn, "reg", ®len); 618c2ecf20Sopenharmony_ci naddrc = of_n_addr_cells(dn); 628c2ecf20Sopenharmony_ci nsizec = of_n_size_cells(dn); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci of_node_put(dn); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (naddrc == 2) 688c2ecf20Sopenharmony_ci address = ((unsigned long) reg[0] << 32) | reg[1]; 698c2ecf20Sopenharmony_ci else 708c2ecf20Sopenharmony_ci address = reg[0]; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (nsizec == 2) 738c2ecf20Sopenharmony_ci size = 748c2ecf20Sopenharmony_ci ((unsigned long) reg[naddrc] << 32) | reg[naddrc + 1]; 758c2ecf20Sopenharmony_ci else 768c2ecf20Sopenharmony_ci size = reg[naddrc]; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci *base = address; 798c2ecf20Sopenharmony_ci *region_size = size; 808c2ecf20Sopenharmony_ci return ioremap(*base, *region_size); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci#else 838c2ecf20Sopenharmony_ci#define atmel_getb(chip, offset) inb(atmel_get_priv(chip)->base + offset) 848c2ecf20Sopenharmony_ci#define atmel_putb(val, chip, offset) \ 858c2ecf20Sopenharmony_ci outb(val, atmel_get_priv(chip)->base + offset) 868c2ecf20Sopenharmony_ci#define atmel_request_region request_region 878c2ecf20Sopenharmony_ci#define atmel_release_region release_region 888c2ecf20Sopenharmony_ci/* Atmel definitions */ 898c2ecf20Sopenharmony_cienum tpm_atmel_addr { 908c2ecf20Sopenharmony_ci TPM_ATMEL_BASE_ADDR_LO = 0x08, 918c2ecf20Sopenharmony_ci TPM_ATMEL_BASE_ADDR_HI = 0x09 928c2ecf20Sopenharmony_ci}; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic inline int tpm_read_index(int base, int index) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci outb(index, base); 978c2ecf20Sopenharmony_ci return inb(base+1) & 0xFF; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* Verify this is a 1.1 Atmel TPM */ 1018c2ecf20Sopenharmony_cistatic int atmel_verify_tpm11(void) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* verify that it is an Atmel part */ 1058c2ecf20Sopenharmony_ci if (tpm_read_index(TPM_ADDR, 4) != 'A' || 1068c2ecf20Sopenharmony_ci tpm_read_index(TPM_ADDR, 5) != 'T' || 1078c2ecf20Sopenharmony_ci tpm_read_index(TPM_ADDR, 6) != 'M' || 1088c2ecf20Sopenharmony_ci tpm_read_index(TPM_ADDR, 7) != 'L') 1098c2ecf20Sopenharmony_ci return 1; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* query chip for its version number */ 1128c2ecf20Sopenharmony_ci if (tpm_read_index(TPM_ADDR, 0x00) != 1 || 1138c2ecf20Sopenharmony_ci tpm_read_index(TPM_ADDR, 0x01) != 1) 1148c2ecf20Sopenharmony_ci return 1; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* This is an atmel supported part */ 1178c2ecf20Sopenharmony_ci return 0; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic inline void atmel_put_base_addr(void __iomem *iobase) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* Determine where to talk to device */ 1258c2ecf20Sopenharmony_cistatic void __iomem * atmel_get_base_addr(unsigned long *base, int *region_size) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci int lo, hi; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (atmel_verify_tpm11() != 0) 1308c2ecf20Sopenharmony_ci return NULL; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci lo = tpm_read_index(TPM_ADDR, TPM_ATMEL_BASE_ADDR_LO); 1338c2ecf20Sopenharmony_ci hi = tpm_read_index(TPM_ADDR, TPM_ATMEL_BASE_ADDR_HI); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci *base = (hi << 8) | lo; 1368c2ecf20Sopenharmony_ci *region_size = 2; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return ioport_map(*base, *region_size); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci#endif 141