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
40static void
41tpm_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
60static inline int
61tpm_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
96static inline int
97tpm_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 */
123static inline uint8_t
124spi_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
133static inline uint32_t
134spi_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
144static inline void
145spi_write_1(device_t dev, bus_size_t off, uint8_t val)
146{
147	tpm_spi_write_n(dev, off, &val, 1);
148}
149
150static inline void
151spi_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
157static 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
165DEFINE_CLASS_0(tpm_spi, tpm_spi_driver, tpm_spibus_methods,
166    sizeof(struct tpm_sc));
167