1 /*-
2 * Copyright (c) 2023 Juniper Networks, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
23 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include <sys/cdefs.h>
28 #include <sys/types.h>
29 #include <sys/bus.h>
30
31 #include <dev/spibus/spi.h>
32 #include "spibus_if.h"
33 #include "tpm_if.h"
34 #include "tpm20.h"
35
36 #define TPM_BASE_ADDR 0xD40000
37 #define TPM_SPI_HEADER_SIZE 4
38 #define TPM_WAIT_STATES 50
39
40 static void
tpm_insert_wait(device_t dev)41 tpm_insert_wait(device_t dev)
42 {
43 device_t parent = device_get_parent(dev);
44 int wait = TPM_WAIT_STATES;
45 struct spi_command spic = SPI_COMMAND_INITIALIZER;
46
47 uint8_t txb = 0;
48 uint8_t rxb = 0;
49
50 spic.tx_cmd = &txb;
51 spic.rx_cmd = &rxb;
52 spic.tx_cmd_sz = 1;
53 spic.rx_cmd_sz = 1;
54 spic.flags = SPI_FLAG_KEEP_CS;
55 do {
56 SPIBUS_TRANSFER(parent, dev, &spic);
57 } while (--wait > 0 && (rxb & 0x1) == 0);
58 }
59
60 static inline int
tpm_spi_read_n(device_t dev,bus_size_t off,void * buf,size_t size)61 tpm_spi_read_n(device_t dev, bus_size_t off, void *buf, size_t size)
62 {
63 struct spi_command spic = SPI_COMMAND_INITIALIZER;
64 uint8_t tx[4] = {0};
65 uint8_t rx[4] = {0};
66 int err;
67
68 if (size > sizeof(rx))
69 return (EINVAL);
70 off += TPM_BASE_ADDR;
71 tx[0] = 0x80 | (size - 1); /* Write (size) bytes */
72 tx[1] = (off >> 16) & 0xff;
73 tx[2] = (off >> 8) & 0xff;
74 tx[3] = off & 0xff;
75
76 spic.tx_cmd = tx;
77 spic.tx_cmd_sz = sizeof(tx);
78 spic.rx_cmd = rx;
79 spic.rx_cmd_sz = sizeof(tx);
80 spic.flags = SPI_FLAG_KEEP_CS;
81
82 err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &spic);
83
84 if (!(rx[3] & 0x1)) {
85 tpm_insert_wait(dev);
86 }
87 memset(tx, 0, sizeof(tx));
88 spic.tx_cmd_sz = spic.rx_cmd_sz = size;
89 spic.flags = 0;
90 err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &spic);
91 memcpy(buf, &rx[0], size);
92
93 return (err);
94 }
95
96 static inline int
tpm_spi_write_n(device_t dev,bus_size_t off,void * buf,size_t size)97 tpm_spi_write_n(device_t dev, bus_size_t off, void *buf, size_t size)
98 {
99 struct spi_command spic = SPI_COMMAND_INITIALIZER;
100 uint8_t tx[8] = {0};
101 uint8_t rx[8] = {0};
102 int err;
103
104 off += TPM_BASE_ADDR;
105 tx[0] = 0x00 | (size - 1); /* Write (size) bytes */
106 tx[1] = (off >> 16) & 0xff;
107 tx[2] = (off >> 8) & 0xff;
108 tx[3] = off & 0xff;
109
110 memcpy(&tx[4], buf, size);
111
112 spic.tx_cmd = tx;
113 spic.tx_cmd_sz = size + TPM_SPI_HEADER_SIZE;
114 spic.rx_cmd = rx;
115 spic.rx_cmd_sz = size + TPM_SPI_HEADER_SIZE;
116
117 err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &spic);
118
119 return (err);
120 }
121
122 /* Override accessors */
123 static inline uint8_t
spi_read_1(device_t dev,bus_size_t off)124 spi_read_1(device_t dev, bus_size_t off)
125 {
126 uint8_t rx_byte;
127
128 tpm_spi_read_n(dev, off, &rx_byte, 1);
129
130 return (rx_byte);
131 }
132
133 static inline uint32_t
spi_read_4(device_t dev,bus_size_t off)134 spi_read_4(device_t dev, bus_size_t off)
135 {
136 uint32_t rx_word = 0;
137
138 tpm_spi_read_n(dev, off, &rx_word, 4);
139 rx_word = le32toh(rx_word);
140
141 return (rx_word);
142 }
143
144 static inline void
spi_write_1(device_t dev,bus_size_t off,uint8_t val)145 spi_write_1(device_t dev, bus_size_t off, uint8_t val)
146 {
147 tpm_spi_write_n(dev, off, &val, 1);
148 }
149
150 static inline void
spi_write_4(device_t dev,bus_size_t off,uint32_t val)151 spi_write_4(device_t dev, bus_size_t off, uint32_t val)
152 {
153 uint32_t tmp = htole32(val);
154 tpm_spi_write_n(dev, off, &tmp, 4);
155 }
156
157 static device_method_t tpm_spibus_methods[] = {
158 DEVMETHOD(tpm_read_4, spi_read_4),
159 DEVMETHOD(tpm_read_1, spi_read_1),
160 DEVMETHOD(tpm_write_4, spi_write_4),
161 DEVMETHOD(tpm_write_1, spi_write_1),
162 DEVMETHOD_END
163 };
164
165 DEFINE_CLASS_0(tpm_spi, tpm_spi_driver, tpm_spibus_methods,
166 sizeof(struct tpm_sc));
167