xref: /linux/drivers/char/tpm/tpm_atmel.h (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1b886d83cSThomas Gleixner /* SPDX-License-Identifier: GPL-2.0-only */
2ad5ea3ccSKylene Jo Hall /*
3ad5ea3ccSKylene Jo Hall  * Copyright (C) 2005 IBM Corporation
4ad5ea3ccSKylene Jo Hall  *
5ad5ea3ccSKylene Jo Hall  * Authors:
6ad5ea3ccSKylene Jo Hall  * Kylene Hall <kjhall@us.ibm.com>
7ad5ea3ccSKylene Jo Hall  *
88e81cc13SKent Yoder  * Maintained by: <tpmdd-devel@lists.sourceforge.net>
9ad5ea3ccSKylene Jo Hall  *
10ad5ea3ccSKylene Jo Hall  * Device driver for TCG/TCPA TPM (trusted platform module).
11ad5ea3ccSKylene Jo Hall  * Specifications at www.trustedcomputinggroup.org
12ad5ea3ccSKylene Jo Hall  *
13ad5ea3ccSKylene Jo Hall  * These difference are required on power because the device must be
14ad5ea3ccSKylene Jo Hall  * discovered through the device tree and iomap must be used to get
15ad5ea3ccSKylene Jo Hall  * around the need for holes in the io_page_mask.  This does not happen
16ad5ea3ccSKylene Jo Hall  * automatically because the tpm is not a normal pci device and lives
17ad5ea3ccSKylene Jo Hall  * under the root node.
18ad5ea3ccSKylene Jo Hall  */
19ad5ea3ccSKylene Jo Hall 
2023d06ff7SJarkko Sakkinen struct tpm_atmel_priv {
2123d06ff7SJarkko Sakkinen 	int region_size;
2223d06ff7SJarkko Sakkinen 	int have_region;
23ee177984SJarkko Sakkinen 	unsigned long base;
244eea703cSChristophe Ricard 	void __iomem *iobase;
2523d06ff7SJarkko Sakkinen };
2623d06ff7SJarkko Sakkinen 
27ad5ea3ccSKylene Jo Hall #ifdef CONFIG_PPC64
28ddf526e9SStephen Rothwell 
29*8edd49ceSRob Herring #include <linux/of.h>
30ddf526e9SStephen Rothwell 
314eea703cSChristophe Ricard #define atmel_getb(priv, offset) readb(priv->iobase + offset)
324eea703cSChristophe Ricard #define atmel_putb(val, priv, offset) writeb(val, priv->iobase + offset)
33ad5ea3ccSKylene Jo Hall #define atmel_request_region request_mem_region
34ad5ea3ccSKylene Jo Hall #define atmel_release_region release_mem_region
3590612b30SKylene Jo Hall 
atmel_put_base_addr(void __iomem * iobase)36e0dd03caSKylene Jo Hall static inline void atmel_put_base_addr(void __iomem *iobase)
37ad5ea3ccSKylene Jo Hall {
38e0dd03caSKylene Jo Hall 	iounmap(iobase);
39ad5ea3ccSKylene Jo Hall }
40ad5ea3ccSKylene Jo Hall 
atmel_get_base_addr(unsigned long * base,int * region_size)41e0dd03caSKylene Jo Hall static void __iomem * atmel_get_base_addr(unsigned long *base, int *region_size)
42ad5ea3ccSKylene Jo Hall {
43ad5ea3ccSKylene Jo Hall 	struct device_node *dn;
44ad5ea3ccSKylene Jo Hall 	unsigned long address, size;
455c339e96SJeremy Kerr 	const unsigned int *reg;
46ad5ea3ccSKylene Jo Hall 	int reglen;
47ad5ea3ccSKylene Jo Hall 	int naddrc;
48ad5ea3ccSKylene Jo Hall 	int nsizec;
49ad5ea3ccSKylene Jo Hall 
50ad5ea3ccSKylene Jo Hall 	dn = of_find_node_by_name(NULL, "tpm");
51ad5ea3ccSKylene Jo Hall 
52ad5ea3ccSKylene Jo Hall 	if (!dn)
5390612b30SKylene Jo Hall 		return NULL;
54ad5ea3ccSKylene Jo Hall 
5555b61fecSStephen Rothwell 	if (!of_device_is_compatible(dn, "AT97SC3201")) {
56ad5ea3ccSKylene Jo Hall 		of_node_put(dn);
5790612b30SKylene Jo Hall 		return NULL;
58ad5ea3ccSKylene Jo Hall 	}
59ad5ea3ccSKylene Jo Hall 
6040cd3a45SStephen Rothwell 	reg = of_get_property(dn, "reg", &reglen);
61a8bda5ddSStephen Rothwell 	naddrc = of_n_addr_cells(dn);
629213feeaSStephen Rothwell 	nsizec = of_n_size_cells(dn);
63ad5ea3ccSKylene Jo Hall 
64ad5ea3ccSKylene Jo Hall 	of_node_put(dn);
65ad5ea3ccSKylene Jo Hall 
66ad5ea3ccSKylene Jo Hall 
67ad5ea3ccSKylene Jo Hall 	if (naddrc == 2)
68ad5ea3ccSKylene Jo Hall 		address = ((unsigned long) reg[0] << 32) | reg[1];
69ad5ea3ccSKylene Jo Hall 	else
70ad5ea3ccSKylene Jo Hall 		address = reg[0];
71ad5ea3ccSKylene Jo Hall 
72ad5ea3ccSKylene Jo Hall 	if (nsizec == 2)
73ad5ea3ccSKylene Jo Hall 		size =
74ad5ea3ccSKylene Jo Hall 		    ((unsigned long) reg[naddrc] << 32) | reg[naddrc + 1];
75ad5ea3ccSKylene Jo Hall 	else
76ad5ea3ccSKylene Jo Hall 		size = reg[naddrc];
77ad5ea3ccSKylene Jo Hall 
78e0dd03caSKylene Jo Hall 	*base = address;
79e0dd03caSKylene Jo Hall 	*region_size = size;
80e0dd03caSKylene Jo Hall 	return ioremap(*base, *region_size);
81ad5ea3ccSKylene Jo Hall }
82ad5ea3ccSKylene Jo Hall #else
83ee177984SJarkko Sakkinen #define atmel_getb(chip, offset) inb(atmel_get_priv(chip)->base + offset)
84ee177984SJarkko Sakkinen #define atmel_putb(val, chip, offset) \
85ee177984SJarkko Sakkinen 	outb(val, atmel_get_priv(chip)->base + offset)
86ad5ea3ccSKylene Jo Hall #define atmel_request_region request_region
87ad5ea3ccSKylene Jo Hall #define atmel_release_region release_region
88ad5ea3ccSKylene Jo Hall /* Atmel definitions */
89ad5ea3ccSKylene Jo Hall enum tpm_atmel_addr {
90ad5ea3ccSKylene Jo Hall 	TPM_ATMEL_BASE_ADDR_LO = 0x08,
91ad5ea3ccSKylene Jo Hall 	TPM_ATMEL_BASE_ADDR_HI = 0x09
92ad5ea3ccSKylene Jo Hall };
93ad5ea3ccSKylene Jo Hall 
tpm_read_index(int base,int index)941d191553SJarkko Sakkinen static inline int tpm_read_index(int base, int index)
951d191553SJarkko Sakkinen {
961d191553SJarkko Sakkinen 	outb(index, base);
971d191553SJarkko Sakkinen 	return inb(base+1) & 0xFF;
981d191553SJarkko Sakkinen }
991d191553SJarkko Sakkinen 
100ad5ea3ccSKylene Jo Hall /* Verify this is a 1.1 Atmel TPM */
atmel_verify_tpm11(void)101ad5ea3ccSKylene Jo Hall static int atmel_verify_tpm11(void)
102ad5ea3ccSKylene Jo Hall {
103ad5ea3ccSKylene Jo Hall 
104ad5ea3ccSKylene Jo Hall 	/* verify that it is an Atmel part */
105ad5ea3ccSKylene Jo Hall 	if (tpm_read_index(TPM_ADDR, 4) != 'A' ||
106ad5ea3ccSKylene Jo Hall 	    tpm_read_index(TPM_ADDR, 5) != 'T' ||
107ad5ea3ccSKylene Jo Hall 	    tpm_read_index(TPM_ADDR, 6) != 'M' ||
108ad5ea3ccSKylene Jo Hall 	    tpm_read_index(TPM_ADDR, 7) != 'L')
109ad5ea3ccSKylene Jo Hall 		return 1;
110ad5ea3ccSKylene Jo Hall 
111ad5ea3ccSKylene Jo Hall 	/* query chip for its version number */
112ad5ea3ccSKylene Jo Hall 	if (tpm_read_index(TPM_ADDR, 0x00) != 1 ||
113ad5ea3ccSKylene Jo Hall 	    tpm_read_index(TPM_ADDR, 0x01) != 1)
114ad5ea3ccSKylene Jo Hall 		return 1;
115ad5ea3ccSKylene Jo Hall 
116ad5ea3ccSKylene Jo Hall 	/* This is an atmel supported part */
117ad5ea3ccSKylene Jo Hall 	return 0;
118ad5ea3ccSKylene Jo Hall }
119ad5ea3ccSKylene Jo Hall 
atmel_put_base_addr(void __iomem * iobase)120e0dd03caSKylene Jo Hall static inline void atmel_put_base_addr(void __iomem *iobase)
121ad5ea3ccSKylene Jo Hall {
122ad5ea3ccSKylene Jo Hall }
123ad5ea3ccSKylene Jo Hall 
124ad5ea3ccSKylene Jo Hall /* Determine where to talk to device */
atmel_get_base_addr(unsigned long * base,int * region_size)125e0dd03caSKylene Jo Hall static void __iomem * atmel_get_base_addr(unsigned long *base, int *region_size)
126ad5ea3ccSKylene Jo Hall {
127ad5ea3ccSKylene Jo Hall 	int lo, hi;
128ad5ea3ccSKylene Jo Hall 
129ad5ea3ccSKylene Jo Hall 	if (atmel_verify_tpm11() != 0)
13090612b30SKylene Jo Hall 		return NULL;
131ad5ea3ccSKylene Jo Hall 
132ad5ea3ccSKylene Jo Hall 	lo = tpm_read_index(TPM_ADDR, TPM_ATMEL_BASE_ADDR_LO);
133ad5ea3ccSKylene Jo Hall 	hi = tpm_read_index(TPM_ADDR, TPM_ATMEL_BASE_ADDR_HI);
134ad5ea3ccSKylene Jo Hall 
135e0dd03caSKylene Jo Hall 	*base = (hi << 8) | lo;
136e0dd03caSKylene Jo Hall 	*region_size = 2;
137ad5ea3ccSKylene Jo Hall 
138e0dd03caSKylene Jo Hall 	return ioport_map(*base, *region_size);
139ad5ea3ccSKylene Jo Hall }
140ad5ea3ccSKylene Jo Hall #endif
141