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", ®len);
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