xref: /openbsd-src/sys/dev/acpi/tpm.c (revision ba5ad174f4b89249eeb2724491bc0ea7e0d4fa0c)
1*ba5ad174Skettenis /* $OpenBSD: tpm.c,v 1.20 2024/05/29 12:21:33 kettenis Exp $ */
214f07311Sjcs 
314f07311Sjcs /*
414f07311Sjcs  * Minimal interface to Trusted Platform Module chips implementing the
514f07311Sjcs  * TPM Interface Spec 1.2, just enough to tell the TPM to save state before
614f07311Sjcs  * a system suspend.
714f07311Sjcs  *
814f07311Sjcs  * Copyright (c) 2008, 2009 Michael Shalayeff
914f07311Sjcs  * Copyright (c) 2009, 2010 Hans-Joerg Hoexer
1014f07311Sjcs  * Copyright (c) 2016 joshua stein <jcs@openbsd.org>
1114f07311Sjcs  * All rights reserved.
1214f07311Sjcs  *
1314f07311Sjcs  * Permission to use, copy, modify, and distribute this software for any
1414f07311Sjcs  * purpose with or without fee is hereby granted, provided that the above
1514f07311Sjcs  * copyright notice and this permission notice appear in all copies.
1614f07311Sjcs  *
1714f07311Sjcs  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1814f07311Sjcs  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1914f07311Sjcs  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
2014f07311Sjcs  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2114f07311Sjcs  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
2214f07311Sjcs  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
2314f07311Sjcs  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2414f07311Sjcs  */
2514f07311Sjcs 
2614f07311Sjcs #include <sys/param.h>
2714f07311Sjcs #include <sys/systm.h>
2814f07311Sjcs #include <sys/device.h>
2914f07311Sjcs #include <sys/malloc.h>
3014f07311Sjcs 
3114f07311Sjcs #include <machine/bus.h>
3214f07311Sjcs #include <machine/apmvar.h>
3314f07311Sjcs 
3414f07311Sjcs #include <dev/acpi/acpireg.h>
3514f07311Sjcs #include <dev/acpi/acpivar.h>
3614f07311Sjcs #include <dev/acpi/acpidev.h>
3714f07311Sjcs #include <dev/acpi/amltypes.h>
3814f07311Sjcs #include <dev/acpi/dsdt.h>
3914f07311Sjcs 
4014f07311Sjcs /* #define TPM_DEBUG */
4114f07311Sjcs 
4214f07311Sjcs #ifdef TPM_DEBUG
4314f07311Sjcs #define DPRINTF(x) printf x
4414f07311Sjcs #else
4514f07311Sjcs #define DPRINTF(x)
4614f07311Sjcs #endif
4714f07311Sjcs 
4814f07311Sjcs #define TPM_BUFSIZ			1024
4914f07311Sjcs #define TPM_HDRSIZE			10
5014f07311Sjcs #define TPM_PARAM_SIZE			0x0001
5114f07311Sjcs 
5214f07311Sjcs #define TPM_ACCESS			0x0000	/* access register */
5314f07311Sjcs #define TPM_ACCESS_ESTABLISHMENT	0x01	/* establishment */
5414f07311Sjcs #define TPM_ACCESS_REQUEST_USE		0x02	/* request using locality */
5514f07311Sjcs #define TPM_ACCESS_REQUEST_PENDING	0x04	/* pending request */
5614f07311Sjcs #define TPM_ACCESS_SEIZE		0x08	/* request locality seize */
5714f07311Sjcs #define TPM_ACCESS_SEIZED		0x10	/* locality has been seized */
5814f07311Sjcs #define TPM_ACCESS_ACTIVE_LOCALITY	0x20	/* locality is active */
5914f07311Sjcs #define TPM_ACCESS_VALID		0x80	/* bits are valid */
6014f07311Sjcs #define TPM_ACCESS_BITS	\
6114f07311Sjcs     "\020\01EST\02REQ\03PEND\04SEIZE\05SEIZED\06ACT\010VALID"
6214f07311Sjcs 
6314f07311Sjcs #define TPM_INTERRUPT_ENABLE		0x0008
6414f07311Sjcs #define TPM_GLOBAL_INT_ENABLE		0x80000000 /* enable ints */
6514f07311Sjcs #define TPM_CMD_READY_INT		0x00000080 /* cmd ready enable */
6614f07311Sjcs #define TPM_INT_EDGE_FALLING		0x00000018
6714f07311Sjcs #define TPM_INT_EDGE_RISING		0x00000010
6814f07311Sjcs #define TPM_INT_LEVEL_LOW		0x00000008
6914f07311Sjcs #define TPM_INT_LEVEL_HIGH		0x00000000
7014f07311Sjcs #define TPM_LOCALITY_CHANGE_INT		0x00000004 /* locality change enable */
7114f07311Sjcs #define TPM_STS_VALID_INT		0x00000002 /* int on TPM_STS_VALID is set */
7214f07311Sjcs #define TPM_DATA_AVAIL_INT		0x00000001 /* int on TPM_STS_DATA_AVAIL is set */
7314f07311Sjcs #define TPM_INTERRUPT_ENABLE_BITS \
7414f07311Sjcs     "\020\040ENA\010RDY\03LOCH\02STSV\01DRDY"
7514f07311Sjcs 
7614f07311Sjcs #define TPM_INT_VECTOR			0x000c	/* 8 bit reg for 4 bit irq vector */
7714f07311Sjcs #define TPM_INT_STATUS			0x0010	/* bits are & 0x87 from TPM_INTERRUPT_ENABLE */
7814f07311Sjcs 
7914f07311Sjcs #define TPM_INTF_CAPABILITIES		0x0014	/* capability register */
8014f07311Sjcs #define TPM_INTF_BURST_COUNT_STATIC	0x0100	/* TPM_STS_BMASK static */
8114f07311Sjcs #define TPM_INTF_CMD_READY_INT		0x0080	/* int on ready supported */
8214f07311Sjcs #define TPM_INTF_INT_EDGE_FALLING	0x0040	/* falling edge ints supported */
8314f07311Sjcs #define TPM_INTF_INT_EDGE_RISING	0x0020	/* rising edge ints supported */
8414f07311Sjcs #define TPM_INTF_INT_LEVEL_LOW		0x0010	/* level-low ints supported */
8514f07311Sjcs #define TPM_INTF_INT_LEVEL_HIGH		0x0008	/* level-high ints supported */
8614f07311Sjcs #define TPM_INTF_LOCALITY_CHANGE_INT	0x0004	/* locality-change int (mb 1) */
8714f07311Sjcs #define TPM_INTF_STS_VALID_INT		0x0002	/* TPM_STS_VALID int supported */
8814f07311Sjcs #define TPM_INTF_DATA_AVAIL_INT		0x0001	/* TPM_STS_DATA_AVAIL int supported (mb 1) */
8914f07311Sjcs #define TPM_CAPSREQ \
9014f07311Sjcs   (TPM_INTF_DATA_AVAIL_INT|TPM_INTF_LOCALITY_CHANGE_INT|TPM_INTF_INT_LEVEL_LOW)
9114f07311Sjcs #define TPM_CAPBITS \
9214f07311Sjcs   "\020\01IDRDY\02ISTSV\03ILOCH\04IHIGH\05ILOW\06IEDGE\07IFALL\010IRDY\011BCST"
9314f07311Sjcs 
9414f07311Sjcs #define TPM_STS				0x0018	   /* status register */
9514f07311Sjcs #define TPM_STS_MASK			0x000000ff /* status bits */
9614f07311Sjcs #define TPM_STS_BMASK			0x00ffff00 /* ro io burst size */
9714f07311Sjcs #define TPM_STS_VALID			0x00000080 /* ro other bits are valid */
9814f07311Sjcs #define TPM_STS_CMD_READY		0x00000040 /* rw chip/signal ready */
9914f07311Sjcs #define TPM_STS_GO			0x00000020 /* wo start the command */
10014f07311Sjcs #define TPM_STS_DATA_AVAIL		0x00000010 /* ro data available */
10114f07311Sjcs #define TPM_STS_DATA_EXPECT		0x00000008 /* ro more data to be written */
10214f07311Sjcs #define TPM_STS_RESP_RETRY		0x00000002 /* wo resend the response */
10314f07311Sjcs #define TPM_STS_BITS	"\020\010VALID\07RDY\06GO\05DRDY\04EXPECT\02RETRY"
10414f07311Sjcs 
10514f07311Sjcs #define TPM_DATA			0x0024
10614f07311Sjcs #define TPM_ID				0x0f00
10714f07311Sjcs #define TPM_REV				0x0f04
10814f07311Sjcs #define TPM_SIZE			0x5000	/* five pages of the above */
10914f07311Sjcs 
11014f07311Sjcs #define TPM_ACCESS_TMO			2000	/* 2sec */
11114f07311Sjcs #define TPM_READY_TMO			2000	/* 2sec */
11214f07311Sjcs #define TPM_READ_TMO			120000	/* 2 minutes */
11314f07311Sjcs #define TPM_BURST_TMO			2000	/* 2sec */
11414f07311Sjcs 
115306068a2Sdv #define TPM2_START_METHOD_TIS		6
116306068a2Sdv #define TPM2_START_METHOD_CRB		7
117306068a2Sdv 
118306068a2Sdv #define TPM_CRB_LOC_STATE		0x0
119306068a2Sdv #define TPM_CRB_LOC_CTRL		0x8
120306068a2Sdv #define TPM_LOC_STS			0xC
121306068a2Sdv #define TPM_CRB_INTF_ID			0x30
122306068a2Sdv #define TPM_CRB_CTRL_EXT		0x38
123306068a2Sdv #define TPM_CRB_CTRL_REQ		0x40
124306068a2Sdv #define TPM_CRB_CTRL_STS		0x44
125306068a2Sdv #define TPM_CRB_CTRL_CANCEL		0x48
126306068a2Sdv #define TPM_CRB_CTRL_START		0x4C
127306068a2Sdv #define TPM_CRB_CTRL_CMD_SIZE		0x58
128306068a2Sdv #define TPM_CRB_CTRL_CMD_LADDR		0x5C
129306068a2Sdv #define TPM_CRB_CTRL_CMD_HADDR		0x60
130306068a2Sdv #define TPM_CRB_CTRL_RSP_SIZE		0x64
131306068a2Sdv #define TPM_CRB_CTRL_RSP_LADDR		0x68
132306068a2Sdv #define TPM_CRB_CTRL_RSP_HADDR		0x6c
133306068a2Sdv #define TPM_CRB_DATA_BUFFER		0x80
134306068a2Sdv 
135306068a2Sdv #define TPM_CRB_LOC_STATE_ESTB		(1 << 0)
136306068a2Sdv #define TPM_CRB_LOC_STATE_ASSIGNED	(1 << 1)
137306068a2Sdv #define TPM_CRB_LOC_ACTIVE_MASK	0x009c
138306068a2Sdv #define TPM_CRB_LOC_VALID		(1 << 7)
139306068a2Sdv 
140306068a2Sdv #define TPM_CRB_LOC_REQUEST		(1 << 0)
141306068a2Sdv #define TPM_CRB_LOC_RELEASE		(1 << 1)
142306068a2Sdv 
143306068a2Sdv #define TPM_CRB_CTRL_REQ_GO_READY	(1 << 0)
144306068a2Sdv #define TPM_CRB_CTRL_REQ_GO_IDLE	(1 << 1)
145306068a2Sdv 
146306068a2Sdv #define TPM_CRB_CTRL_STS_ERR_BIT	(1 << 0)
147306068a2Sdv #define TPM_CRB_CTRL_STS_IDLE_BIT	(1 << 1)
148306068a2Sdv 
149306068a2Sdv #define TPM_CRB_CTRL_CANCEL_CMD		0x1
150306068a2Sdv #define TPM_CRB_CTRL_CANCEL_CLEAR	0x0
151306068a2Sdv 
152306068a2Sdv #define TPM_CRB_CTRL_START_CMD		(1 << 0)
153dcec61dcSmiod #define TPM_CRB_INT_ENABLED_BIT		(1U << 31)
154306068a2Sdv 
155306068a2Sdv #define TPM2_RC_SUCCESS			0x0000
156306068a2Sdv #define TPM2_RC_INITIALIZE		0x0100
157306068a2Sdv #define TPM2_RC_FAILURE			0x0101
158306068a2Sdv #define TPM2_RC_DISABLED		0x0120
159306068a2Sdv #define TPM2_RC_RETRY			0x0922
160306068a2Sdv 
16114f07311Sjcs struct tpm_softc {
16214f07311Sjcs 	struct device		sc_dev;
16314f07311Sjcs 
16414f07311Sjcs 	bus_space_tag_t		sc_bt;
16514f07311Sjcs 	bus_space_handle_t	sc_bh;
166306068a2Sdv 	bus_size_t		sc_bbase;
16714f07311Sjcs 
16814f07311Sjcs 	struct acpi_softc	*sc_acpi;
16914f07311Sjcs 	struct aml_node		*sc_devnode;
17014f07311Sjcs 
17114f07311Sjcs 	uint32_t		sc_devid;
17214f07311Sjcs 	uint32_t		sc_rev;
173306068a2Sdv 
17401a460e6Sderaadt 	int			sc_tpm20;
175306068a2Sdv 	int			sc_tpm_mode;
176306068a2Sdv #define TPM_TIS		0
177306068a2Sdv #define TPM_CRB		1
178306068a2Sdv 	bus_size_t		sc_cmd_off;
179306068a2Sdv 	bus_size_t		sc_rsp_off;
180306068a2Sdv 	size_t			sc_cmd_sz;
181306068a2Sdv 	size_t			sc_rsp_sz;
18214f07311Sjcs 
18314f07311Sjcs 	int			sc_enabled;
18414f07311Sjcs };
18514f07311Sjcs 
18614f07311Sjcs const struct {
18714f07311Sjcs 	uint32_t devid;
18814f07311Sjcs 	char name[32];
18914f07311Sjcs } tpm_devs[] = {
19014f07311Sjcs 	{ 0x000615d1, "Infineon SLD9630 1.1" },
19114f07311Sjcs 	{ 0x000b15d1, "Infineon SLB9635 1.2" },
19214f07311Sjcs 	{ 0x100214e4, "Broadcom BCM0102" },
19314f07311Sjcs 	{ 0x00fe1050, "WEC WPCT200" },
19414f07311Sjcs 	{ 0x687119fa, "SNS SSX35" },
19514f07311Sjcs 	{ 0x2e4d5453, "STM ST19WP18" },
19614f07311Sjcs 	{ 0x32021114, "Atmel 97SC3203" },
19714f07311Sjcs 	{ 0x10408086, "Intel INTC0102" },
19814f07311Sjcs 	{ 0, "" },
19914f07311Sjcs };
20014f07311Sjcs 
20114f07311Sjcs int	tpm_match(struct device *, void *, void *);
20214f07311Sjcs void	tpm_attach(struct device *, struct device *, void *);
20314f07311Sjcs int	tpm_activate(struct device *, int);
20414f07311Sjcs 
20514f07311Sjcs int	tpm_probe(bus_space_tag_t, bus_space_handle_t);
206306068a2Sdv int	tpm_init_tis(struct tpm_softc *);
207306068a2Sdv int	tpm_init_crb(struct tpm_softc *);
208306068a2Sdv int	tpm_read_tis(struct tpm_softc *, void *, int, size_t *, int);
209306068a2Sdv int	tpm_read_crb(struct tpm_softc *, void *, int);
210306068a2Sdv int	tpm_write_tis(struct tpm_softc *, void *, int);
211306068a2Sdv int	tpm_write_crb(struct tpm_softc *, void *, int);
21214f07311Sjcs int	tpm_suspend(struct tpm_softc *);
21314f07311Sjcs int	tpm_resume(struct tpm_softc *);
21414f07311Sjcs 
215306068a2Sdv int	tpm_waitfor(struct tpm_softc *, bus_space_handle_t, uint32_t, uint32_t, int);
216306068a2Sdv int	tpm_waitfor_status(struct tpm_softc *, uint8_t, int);
217306068a2Sdv int	tpm_request_locality_tis(struct tpm_softc *, int);
218306068a2Sdv int	tpm_request_locality_crb(struct tpm_softc *, int);
219306068a2Sdv void	tpm_release_locality_tis(struct tpm_softc *);
220306068a2Sdv void	tpm_release_locality_crb(struct tpm_softc *);
22114f07311Sjcs uint8_t	tpm_status(struct tpm_softc *);
22214f07311Sjcs 
223306068a2Sdv uint32_t tpm2_start_method(struct acpi_softc *);
224306068a2Sdv 
225471aeecfSnaddy const struct cfattach tpm_ca = {
22614f07311Sjcs 	sizeof(struct tpm_softc),
22714f07311Sjcs 	tpm_match,
22814f07311Sjcs 	tpm_attach,
22914f07311Sjcs 	NULL,
23014f07311Sjcs 	tpm_activate
23114f07311Sjcs };
23214f07311Sjcs 
23314f07311Sjcs struct cfdriver tpm_cd = {
234620cc1d5Sderaadt 	NULL, "tpm", DV_DULL, CD_SKIPHIBERNATE	/* XXX */
23514f07311Sjcs };
23614f07311Sjcs 
23714f07311Sjcs const char *tpm_hids[] = {
23814f07311Sjcs 	"PNP0C31",
23914f07311Sjcs 	"ATM1200",
24014f07311Sjcs 	"IFX0102",
24114f07311Sjcs 	"BCM0101",
24214f07311Sjcs 	"BCM0102",
24314f07311Sjcs 	"NSC1200",
24414f07311Sjcs 	"ICO0102",
24501a460e6Sderaadt 	"MSFT0101",
246128e94b2Smlarkin 	NULL
24714f07311Sjcs };
24814f07311Sjcs 
24914f07311Sjcs int
tpm_match(struct device * parent,void * match,void * aux)25014f07311Sjcs tpm_match(struct device *parent, void *match, void *aux)
25114f07311Sjcs {
25214f07311Sjcs 	struct acpi_attach_args	*aa = aux;
25314f07311Sjcs 	struct cfdata		*cf = match;
25414f07311Sjcs 
25557ec0946Skettenis 	if (aa->aaa_naddr < 1)
25657ec0946Skettenis 		return 0;
25714f07311Sjcs 	return (acpi_matchhids(aa, tpm_hids, cf->cf_driver->cd_name));
25814f07311Sjcs }
25914f07311Sjcs 
26014f07311Sjcs void
tpm_attach(struct device * parent,struct device * self,void * aux)26114f07311Sjcs tpm_attach(struct device *parent, struct device *self, void *aux)
26214f07311Sjcs {
26314f07311Sjcs 	struct tpm_softc	*sc = (struct tpm_softc *)self;
2649f1f78b7Skettenis 	struct acpi_attach_args *aaa = aux;
2659f1f78b7Skettenis 	int64_t			sta;
266306068a2Sdv 	uint32_t		start_method;
26714f07311Sjcs 
26814f07311Sjcs 	sc->sc_acpi = (struct acpi_softc *)parent;
2699f1f78b7Skettenis 	sc->sc_devnode = aaa->aaa_node;
27014f07311Sjcs 	sc->sc_enabled = 0;
271306068a2Sdv 	sc->sc_tpm_mode = TPM_TIS;
27214f07311Sjcs 
2739f1f78b7Skettenis 	printf(" %s", sc->sc_devnode->name);
27414f07311Sjcs 
27501a460e6Sderaadt 	if (strcmp(aaa->aaa_dev, "MSFT0101") == 0 ||
276306068a2Sdv 	    strcmp(aaa->aaa_cdev, "MSFT0101") == 0) {
27701a460e6Sderaadt 		sc->sc_tpm20 = 1;
278306068a2Sdv 		/* Identify if using 1.2 TIS or 2.0's CRB methods */
279306068a2Sdv 		start_method = tpm2_start_method(sc->sc_acpi);
280306068a2Sdv 		switch (start_method) {
281306068a2Sdv 		case TPM2_START_METHOD_TIS:
282306068a2Sdv 			/* Already default */
283306068a2Sdv 			break;
284306068a2Sdv 		case TPM2_START_METHOD_CRB:
285306068a2Sdv 			sc->sc_tpm_mode = TPM_CRB;
286306068a2Sdv 			break;
287306068a2Sdv 		default:
2885d99c75fSderaadt 			printf(": unsupported TPM2 start method %d\n", start_method);
289306068a2Sdv 			return;
290306068a2Sdv 		}
291306068a2Sdv 	}
292306068a2Sdv 
293306068a2Sdv 	printf(" %s (%s)", sc->sc_tpm20 ? "2.0" : "1.2",
294306068a2Sdv 	    sc->sc_tpm_mode == TPM_TIS ? "TIS" : "CRB");
29501a460e6Sderaadt 
2969f1f78b7Skettenis 	sta = acpi_getsta(sc->sc_acpi, sc->sc_devnode);
2979f1f78b7Skettenis 	if ((sta & (STA_PRESENT | STA_ENABLED | STA_DEV_OK)) !=
29814f07311Sjcs 	    (STA_PRESENT | STA_ENABLED | STA_DEV_OK)) {
2999f1f78b7Skettenis 		printf(": not enabled\n");
30014f07311Sjcs 		return;
30114f07311Sjcs 	}
30214f07311Sjcs 
3039f1f78b7Skettenis 	printf(" addr 0x%llx/0x%llx", aaa->aaa_addr[0], aaa->aaa_size[0]);
304306068a2Sdv 	sc->sc_bbase = aaa->aaa_addr[0];
30514f07311Sjcs 
3069f1f78b7Skettenis 	sc->sc_bt = aaa->aaa_bst[0];
3079f1f78b7Skettenis 	if (bus_space_map(sc->sc_bt, aaa->aaa_addr[0], aaa->aaa_size[0],
3089f1f78b7Skettenis 	    0, &sc->sc_bh)) {
3099f1f78b7Skettenis 		printf(": can't map registers\n");
31014f07311Sjcs 		return;
31114f07311Sjcs 	}
31214f07311Sjcs 
313306068a2Sdv 	if (sc->sc_tpm_mode == TPM_TIS) {
31414f07311Sjcs 		if (!tpm_probe(sc->sc_bt, sc->sc_bh)) {
3159f1f78b7Skettenis 			printf(": probe failed\n");
31614f07311Sjcs 			return;
31714f07311Sjcs 		}
31814f07311Sjcs 
319306068a2Sdv 		if (tpm_init_tis(sc) != 0) {
3209f1f78b7Skettenis 			printf(": init failed\n");
32114f07311Sjcs 			return;
32214f07311Sjcs 		}
323306068a2Sdv 	} else {
324306068a2Sdv 		if (tpm_init_crb(sc) != 0) {
325306068a2Sdv 			printf(": init failed\n");
326306068a2Sdv 			return;
327306068a2Sdv 		}
328306068a2Sdv 	}
32914f07311Sjcs 
330c984dbbaStedu 	printf("\n");
33114f07311Sjcs 	sc->sc_enabled = 1;
33214f07311Sjcs }
33314f07311Sjcs 
33414f07311Sjcs int
tpm_activate(struct device * self,int act)33514f07311Sjcs tpm_activate(struct device *self, int act)
33614f07311Sjcs {
33714f07311Sjcs 	struct tpm_softc	*sc = (struct tpm_softc *)self;
33814f07311Sjcs 
33914f07311Sjcs 	switch (act) {
34014f07311Sjcs 	case DVACT_SUSPEND:
34114f07311Sjcs 		if (!sc->sc_enabled) {
34214f07311Sjcs 			DPRINTF(("%s: suspend, but not enabled\n",
34314f07311Sjcs 			    sc->sc_dev.dv_xname));
34414f07311Sjcs 			return 0;
34514f07311Sjcs 		}
34614f07311Sjcs 		tpm_suspend(sc);
34714f07311Sjcs 		break;
34814f07311Sjcs 
34914f07311Sjcs 	case DVACT_WAKEUP:
35014f07311Sjcs 		if (!sc->sc_enabled) {
35114f07311Sjcs 			DPRINTF(("%s: wakeup, but not enabled\n",
35214f07311Sjcs 			    sc->sc_dev.dv_xname));
35314f07311Sjcs 			return 0;
35414f07311Sjcs 		}
35514f07311Sjcs 		tpm_resume(sc);
35614f07311Sjcs 		break;
35714f07311Sjcs 	}
35814f07311Sjcs 
35914f07311Sjcs 	return 0;
36014f07311Sjcs }
36114f07311Sjcs 
36214f07311Sjcs int
tpm_suspend(struct tpm_softc * sc)36314f07311Sjcs tpm_suspend(struct tpm_softc *sc)
36414f07311Sjcs {
36501a460e6Sderaadt 	uint8_t command1[] = {
36614f07311Sjcs 	    0, 0xc1,		/* TPM_TAG_RQU_COMMAND */
36714f07311Sjcs 	    0, 0, 0, 10,	/* Length in bytes */
36814f07311Sjcs 	    0, 0, 0, 0x98	/* TPM_ORD_SaveStates */
36914f07311Sjcs 	};
37001a460e6Sderaadt 	uint8_t command2[] = {
37101a460e6Sderaadt 	    0x80, 0x01,		/* TPM_ST_COMMAND_TAG */
37201a460e6Sderaadt 	    0, 0, 0, 12,	/* Length in bytes */
37301a460e6Sderaadt 	    0, 0, 0x01, 0x45,	/* TPM_CC_Shutdown */
37401a460e6Sderaadt 	    0x00, 0x01
37501a460e6Sderaadt 	};
37601a460e6Sderaadt 	uint8_t *command;
37701a460e6Sderaadt 	size_t commandlen;
37814f07311Sjcs 
379*ba5ad174Skettenis 	if (sc->sc_acpi->sc_state == ACPI_STATE_S0)
380*ba5ad174Skettenis 		return 0;
381*ba5ad174Skettenis 
38214f07311Sjcs 	DPRINTF(("%s: saving state preparing for suspend\n",
38314f07311Sjcs 	    sc->sc_dev.dv_xname));
38414f07311Sjcs 
38501a460e6Sderaadt 	if (sc->sc_tpm20) {
38601a460e6Sderaadt 		command = command2;
38701a460e6Sderaadt 		commandlen = sizeof(command2);
38801a460e6Sderaadt 	} else {
38901a460e6Sderaadt 		command = command1;
39001a460e6Sderaadt 		commandlen = sizeof(command1);
39101a460e6Sderaadt 	}
39201a460e6Sderaadt 
39314f07311Sjcs 	/*
39414f07311Sjcs 	 * Tell the chip to save its state so the BIOS can then restore it upon
39514f07311Sjcs 	 * resume.
39614f07311Sjcs 	 */
397306068a2Sdv 	if (sc->sc_tpm_mode == TPM_TIS) {
398306068a2Sdv 		tpm_write_tis(sc, command, commandlen);
399306068a2Sdv 		memset(command, 0, commandlen);
400306068a2Sdv 		tpm_read_tis(sc, command, commandlen, NULL, TPM_HDRSIZE);
401306068a2Sdv 	} else {
402306068a2Sdv 		tpm_write_crb(sc, command, commandlen);
403306068a2Sdv 		memset(command, 0, commandlen);
404306068a2Sdv 		tpm_read_crb(sc, command, commandlen);
405306068a2Sdv 	}
40614f07311Sjcs 	return 0;
40714f07311Sjcs }
40814f07311Sjcs 
40914f07311Sjcs int
tpm_resume(struct tpm_softc * sc)41014f07311Sjcs tpm_resume(struct tpm_softc *sc)
41114f07311Sjcs {
41214f07311Sjcs 	/*
41314f07311Sjcs 	 * TODO: The BIOS should have restored the chip's state for us already,
41414f07311Sjcs 	 * but we should tell the chip to do a self-test here (according to the
41514f07311Sjcs 	 * Linux driver).
41614f07311Sjcs 	 */
41714f07311Sjcs 
41814f07311Sjcs 	DPRINTF(("%s: resume\n", sc->sc_dev.dv_xname));
41914f07311Sjcs 	return 0;
42014f07311Sjcs }
42114f07311Sjcs 
422306068a2Sdv uint32_t
tpm2_start_method(struct acpi_softc * sc)423306068a2Sdv tpm2_start_method(struct acpi_softc *sc)
424306068a2Sdv {
425306068a2Sdv 	struct acpi_q *entry;
426306068a2Sdv 	struct acpi_tpm2 *p_tpm2 = NULL;
427306068a2Sdv 
428306068a2Sdv 	SIMPLEQ_FOREACH(entry, &sc->sc_tables, q_next) {
429306068a2Sdv 		if (memcmp(entry->q_table, TPM2_SIG,
430306068a2Sdv 		    sizeof(TPM2_SIG) - 1) == 0) {
431306068a2Sdv 			p_tpm2 = entry->q_table;
432306068a2Sdv 			break;
433306068a2Sdv 		}
434306068a2Sdv 	}
435306068a2Sdv 
436306068a2Sdv 	if (!p_tpm2) {
437306068a2Sdv 		DPRINTF((", no TPM2 table"));
438306068a2Sdv 		return 0;
439306068a2Sdv 	}
440306068a2Sdv 
441306068a2Sdv 	return p_tpm2->start_method;
442306068a2Sdv }
443306068a2Sdv 
44414f07311Sjcs int
tpm_probe(bus_space_tag_t bt,bus_space_handle_t bh)44514f07311Sjcs tpm_probe(bus_space_tag_t bt, bus_space_handle_t bh)
44614f07311Sjcs {
44714f07311Sjcs 	uint32_t r;
44814f07311Sjcs 	int tries = 10000;
44914f07311Sjcs 
45014f07311Sjcs 	/* wait for chip to settle */
45114f07311Sjcs 	while (tries--) {
45214f07311Sjcs 		if (bus_space_read_1(bt, bh, TPM_ACCESS) & TPM_ACCESS_VALID)
45314f07311Sjcs 			break;
45414f07311Sjcs 		else if (!tries) {
45514f07311Sjcs 			printf(": timed out waiting for validity\n");
45614f07311Sjcs 			return 1;
45714f07311Sjcs 		}
45814f07311Sjcs 
45914f07311Sjcs 		DELAY(10);
46014f07311Sjcs 	}
46114f07311Sjcs 
46214f07311Sjcs 	r = bus_space_read_4(bt, bh, TPM_INTF_CAPABILITIES);
46314f07311Sjcs 	if (r == 0xffffffff)
46414f07311Sjcs 		return 0;
46514f07311Sjcs 
46614f07311Sjcs 	return 1;
46714f07311Sjcs }
46814f07311Sjcs 
46914f07311Sjcs int
tpm_init_tis(struct tpm_softc * sc)470306068a2Sdv tpm_init_tis(struct tpm_softc *sc)
47114f07311Sjcs {
47214f07311Sjcs 	uint32_t r, intmask;
47314f07311Sjcs 	int i;
47414f07311Sjcs 
47514f07311Sjcs 	r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INTF_CAPABILITIES);
47614f07311Sjcs 	if ((r & TPM_CAPSREQ) != TPM_CAPSREQ ||
47714f07311Sjcs 	    !(r & (TPM_INTF_INT_EDGE_RISING | TPM_INTF_INT_LEVEL_LOW))) {
47814f07311Sjcs 		DPRINTF((": caps too low (caps=%b)\n", r, TPM_CAPBITS));
47914f07311Sjcs 		return 0;
48014f07311Sjcs 	}
48114f07311Sjcs 
48214f07311Sjcs 	/* ack and disable all interrupts, we'll be using polling only */
48314f07311Sjcs 	intmask = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE);
48414f07311Sjcs 	intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT |
48514f07311Sjcs 	    TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT;
48614f07311Sjcs 	intmask &= ~TPM_GLOBAL_INT_ENABLE;
48714f07311Sjcs 	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE, intmask);
48814f07311Sjcs 
489306068a2Sdv 	if (tpm_request_locality_tis(sc, 0)) {
49014f07311Sjcs 		printf(", requesting locality failed\n");
49114f07311Sjcs 		return 1;
49214f07311Sjcs 	}
49314f07311Sjcs 
49414f07311Sjcs 	sc->sc_devid = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_ID);
49514f07311Sjcs 	sc->sc_rev = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_REV);
49614f07311Sjcs 
49714f07311Sjcs 	for (i = 0; tpm_devs[i].devid; i++)
49814f07311Sjcs 		if (tpm_devs[i].devid == sc->sc_devid)
49914f07311Sjcs 			break;
50014f07311Sjcs 
50114f07311Sjcs 	if (tpm_devs[i].devid)
502e0256b2bSderaadt 		printf(", %s rev 0x%x", tpm_devs[i].name, sc->sc_rev);
50314f07311Sjcs 	else
504e0256b2bSderaadt 		printf(", device 0x%08x rev 0x%x", sc->sc_devid, sc->sc_rev);
50514f07311Sjcs 
50614f07311Sjcs 	return 0;
50714f07311Sjcs }
50814f07311Sjcs 
50914f07311Sjcs int
tpm_init_crb(struct tpm_softc * sc)510306068a2Sdv tpm_init_crb(struct tpm_softc *sc)
511306068a2Sdv {
512306068a2Sdv 	uint32_t intmask;
513306068a2Sdv 	int i;
514306068a2Sdv 
515306068a2Sdv 	if (tpm_request_locality_crb(sc, 0)) {
516306068a2Sdv 		printf(", request locality failed\n");
517306068a2Sdv 		return 1;
518306068a2Sdv 	}
519306068a2Sdv 
520306068a2Sdv 	/* ack and disable all interrupts, we'll be using polling only */
521306068a2Sdv 	intmask = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE);
522306068a2Sdv 	intmask &= ~TPM_CRB_INT_ENABLED_BIT;
523306068a2Sdv 	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE, intmask);
524306068a2Sdv 
525306068a2Sdv 	/* Identify command and response registers and sizes */
526306068a2Sdv 	sc->sc_cmd_off = bus_space_read_4(sc->sc_bt, sc->sc_bh,
527306068a2Sdv 	    TPM_CRB_CTRL_CMD_LADDR);
528306068a2Sdv 	sc->sc_cmd_off |= ((uint64_t) bus_space_read_4(sc->sc_bt, sc->sc_bh,
529306068a2Sdv 	    TPM_CRB_CTRL_CMD_HADDR) << 32);
530306068a2Sdv 	sc->sc_cmd_sz = bus_space_read_4(sc->sc_bt, sc->sc_bh,
531306068a2Sdv 	    TPM_CRB_CTRL_CMD_SIZE);
532306068a2Sdv 
533306068a2Sdv 	sc->sc_rsp_off = bus_space_read_4(sc->sc_bt, sc->sc_bh,
534306068a2Sdv 	    TPM_CRB_CTRL_RSP_LADDR);
535306068a2Sdv 	sc->sc_rsp_off |= ((uint64_t) bus_space_read_4(sc->sc_bt, sc->sc_bh,
536306068a2Sdv 	    TPM_CRB_CTRL_RSP_HADDR) << 32);
537306068a2Sdv 	sc->sc_rsp_sz = bus_space_read_4(sc->sc_bt, sc->sc_bh,
538306068a2Sdv 	    TPM_CRB_CTRL_RSP_SIZE);
539306068a2Sdv 
540306068a2Sdv 	DPRINTF((", cmd @ 0x%lx, %ld, rsp @ 0x%lx, %ld", sc->sc_cmd_off,
541306068a2Sdv 	    sc->sc_cmd_sz, sc->sc_rsp_off, sc->sc_rsp_sz));
542306068a2Sdv 
543306068a2Sdv 	sc->sc_cmd_off = sc->sc_cmd_off - sc->sc_bbase;
544306068a2Sdv 	sc->sc_rsp_off = sc->sc_rsp_off - sc->sc_bbase;
545306068a2Sdv 
546306068a2Sdv 	tpm_release_locality_crb(sc);
547306068a2Sdv 
548306068a2Sdv 	/* If it's a unified buffer, the sizes must be the same. */
549306068a2Sdv 	if (sc->sc_cmd_off == sc->sc_rsp_off) {
550306068a2Sdv 		if (sc->sc_cmd_sz != sc->sc_rsp_sz) {
551306068a2Sdv 			printf(", invalid buffer sizes\n");
552306068a2Sdv 			return 1;
553306068a2Sdv 		}
554306068a2Sdv 	}
555306068a2Sdv 
556306068a2Sdv 	for (i = 0; tpm_devs[i].devid; i++)
557306068a2Sdv 		if (tpm_devs[i].devid == sc->sc_devid)
558306068a2Sdv 			break;
559306068a2Sdv 
560306068a2Sdv 	if (tpm_devs[i].devid)
561306068a2Sdv 		printf(", %s rev 0x%x", tpm_devs[i].name, sc->sc_rev);
562306068a2Sdv 	else
563306068a2Sdv 		printf(", device 0x%08x rev 0x%x", sc->sc_devid, sc->sc_rev);
564306068a2Sdv 
565306068a2Sdv 	return 0;
566306068a2Sdv }
567306068a2Sdv 
568306068a2Sdv int
tpm_request_locality_tis(struct tpm_softc * sc,int l)569306068a2Sdv tpm_request_locality_tis(struct tpm_softc *sc, int l)
57014f07311Sjcs {
57114f07311Sjcs 	uint32_t r;
57214f07311Sjcs 	int to;
57314f07311Sjcs 
57414f07311Sjcs 	if (l != 0)
57514f07311Sjcs 		return EINVAL;
57614f07311Sjcs 
57714f07311Sjcs 	if ((bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS) &
57814f07311Sjcs 	    (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) ==
57914f07311Sjcs 	    (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY))
58014f07311Sjcs 		return 0;
58114f07311Sjcs 
58214f07311Sjcs 	bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS,
58314f07311Sjcs 	    TPM_ACCESS_REQUEST_USE);
58414f07311Sjcs 
585a9e30698Scheloha 	to = TPM_ACCESS_TMO * 100;	/* steps of 10 microseconds */
58614f07311Sjcs 
58714f07311Sjcs 	while ((r = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS) &
58814f07311Sjcs 	    (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) !=
58914f07311Sjcs 	    (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY) && to--) {
59014f07311Sjcs 		DELAY(10);
59114f07311Sjcs 	}
59214f07311Sjcs 
59314f07311Sjcs 	if ((r & (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) !=
59414f07311Sjcs 	    (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) {
59514f07311Sjcs 		DPRINTF(("%s: %s: access %b\n", sc->sc_dev.dv_xname, __func__,
59614f07311Sjcs 		    r, TPM_ACCESS_BITS));
59714f07311Sjcs 		return EBUSY;
59814f07311Sjcs 	}
59914f07311Sjcs 
60014f07311Sjcs 	return 0;
60114f07311Sjcs }
60214f07311Sjcs 
603306068a2Sdv int
tpm_request_locality_crb(struct tpm_softc * sc,int l)604306068a2Sdv tpm_request_locality_crb(struct tpm_softc *sc, int l)
605306068a2Sdv {
606306068a2Sdv 	uint32_t r, mask;
607306068a2Sdv 	int to;
608306068a2Sdv 
609306068a2Sdv 	if (l != 0)
610306068a2Sdv 		return EINVAL;
611306068a2Sdv 
612306068a2Sdv 	r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_CRB_LOC_CTRL);
613306068a2Sdv 	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_CRB_LOC_CTRL,
614306068a2Sdv 	    r | TPM_CRB_LOC_REQUEST);
615306068a2Sdv 
616306068a2Sdv 	to = TPM_ACCESS_TMO * 200;
617306068a2Sdv 	mask = TPM_CRB_LOC_STATE_ASSIGNED | TPM_CRB_LOC_VALID;
618306068a2Sdv 
619306068a2Sdv 	r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_CRB_LOC_STATE);
620306068a2Sdv 	while ((r & mask) != mask && to--) {
621306068a2Sdv 		DELAY(10);
622306068a2Sdv 		r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_CRB_LOC_STATE);
623306068a2Sdv 	}
624306068a2Sdv 
625306068a2Sdv 	if ((r & mask) != mask) {
626306068a2Sdv 		printf(", CRB loc FAILED");
627306068a2Sdv 		return EBUSY;
628306068a2Sdv 	}
629306068a2Sdv 
630306068a2Sdv 	return 0;
631306068a2Sdv }
632306068a2Sdv 
63314f07311Sjcs void
tpm_release_locality_tis(struct tpm_softc * sc)634306068a2Sdv tpm_release_locality_tis(struct tpm_softc *sc)
63514f07311Sjcs {
63614f07311Sjcs 	if ((bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS) &
63714f07311Sjcs 	    (TPM_ACCESS_REQUEST_PENDING|TPM_ACCESS_VALID)) ==
63814f07311Sjcs 	    (TPM_ACCESS_REQUEST_PENDING|TPM_ACCESS_VALID)) {
63914f07311Sjcs 		DPRINTF(("%s: releasing locality\n", sc->sc_dev.dv_xname));
64014f07311Sjcs 		bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS,
64114f07311Sjcs 		    TPM_ACCESS_ACTIVE_LOCALITY);
64214f07311Sjcs 	}
64314f07311Sjcs }
64414f07311Sjcs 
645306068a2Sdv void
tpm_release_locality_crb(struct tpm_softc * sc)646306068a2Sdv tpm_release_locality_crb(struct tpm_softc *sc)
647306068a2Sdv {
648306068a2Sdv 	uint32_t r;
649306068a2Sdv 
650306068a2Sdv 	r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_CRB_LOC_CTRL);
651306068a2Sdv 	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_CRB_LOC_CTRL,
652306068a2Sdv 	    r | TPM_CRB_LOC_RELEASE);
653306068a2Sdv }
654306068a2Sdv 
65514f07311Sjcs int
tpm_getburst(struct tpm_softc * sc)65614f07311Sjcs tpm_getburst(struct tpm_softc *sc)
65714f07311Sjcs {
65814f07311Sjcs 	int burst, burst2, to;
65914f07311Sjcs 
660a9e30698Scheloha 	to = TPM_BURST_TMO * 100;	/* steps of 10 microseconds */
66114f07311Sjcs 
66214f07311Sjcs 	burst = 0;
66314f07311Sjcs 	while (burst == 0 && to--) {
66414f07311Sjcs 		/*
66514f07311Sjcs 		 * Burst count has to be read from bits 8 to 23 without
66614f07311Sjcs 		 * touching any other bits, eg. the actual status bits 0 to 7.
66714f07311Sjcs 		 */
66814f07311Sjcs 		burst = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_STS + 1);
66914f07311Sjcs 		DPRINTF(("%s: %s: read1(0x%x): 0x%x\n", sc->sc_dev.dv_xname,
67014f07311Sjcs 		    __func__, TPM_STS + 1, burst));
67114f07311Sjcs 		burst2 = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_STS + 2);
67214f07311Sjcs 		DPRINTF(("%s: %s: read1(0x%x): 0x%x\n", sc->sc_dev.dv_xname,
67314f07311Sjcs 		    __func__, TPM_STS + 2, burst2));
67414f07311Sjcs 		burst |= burst2 << 8;
67514f07311Sjcs 		if (burst)
67614f07311Sjcs 			return burst;
67714f07311Sjcs 
67814f07311Sjcs 		DELAY(10);
67914f07311Sjcs 	}
68014f07311Sjcs 
68114f07311Sjcs 	DPRINTF(("%s: getburst timed out\n", sc->sc_dev.dv_xname));
68214f07311Sjcs 
68314f07311Sjcs 	return 0;
68414f07311Sjcs }
68514f07311Sjcs 
68614f07311Sjcs uint8_t
tpm_status(struct tpm_softc * sc)68714f07311Sjcs tpm_status(struct tpm_softc *sc)
68814f07311Sjcs {
68914f07311Sjcs 	return bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_STS) & TPM_STS_MASK;
69014f07311Sjcs }
69114f07311Sjcs 
69214f07311Sjcs int
tpm_waitfor(struct tpm_softc * sc,bus_size_t offset,uint32_t mask,uint32_t val,int msecs)693306068a2Sdv tpm_waitfor(struct tpm_softc *sc, bus_size_t offset, uint32_t mask,
694306068a2Sdv     uint32_t val, int msecs)
695306068a2Sdv {
696306068a2Sdv 	int usecs;
697306068a2Sdv 	uint32_t r;
698306068a2Sdv 
699306068a2Sdv 	usecs = msecs * 1000;
700306068a2Sdv 
701306068a2Sdv 	r = bus_space_read_4(sc->sc_bt, sc->sc_bh, offset);
702306068a2Sdv 	if ((r & mask) == val)
703306068a2Sdv 		return 0;
704306068a2Sdv 
705306068a2Sdv 	while (usecs > 0) {
706306068a2Sdv 		r = bus_space_read_4(sc->sc_bt, sc->sc_bh, offset);
707306068a2Sdv 		if ((r & mask) == val)
708306068a2Sdv 			return 0;
709306068a2Sdv 		DELAY(1);
710306068a2Sdv 		usecs--;
711306068a2Sdv 	}
712306068a2Sdv 
713306068a2Sdv 	DPRINTF(("%s: %s: timed out, status 0x%x != 0x%x\n",
714306068a2Sdv 		    sc->sc_dev.dv_xname, __func__, r, mask));
715306068a2Sdv 	return ETIMEDOUT;
716306068a2Sdv }
717306068a2Sdv 
718306068a2Sdv int
tpm_waitfor_status(struct tpm_softc * sc,uint8_t mask,int msecs)719306068a2Sdv tpm_waitfor_status(struct tpm_softc *sc, uint8_t mask, int msecs)
72014f07311Sjcs {
721a9e30698Scheloha 	int usecs;
72214f07311Sjcs 	uint8_t status;
72314f07311Sjcs 
724a9e30698Scheloha 	usecs = msecs * 1000;
725a9e30698Scheloha 
72614f07311Sjcs 	while (((status = tpm_status(sc)) & mask) != mask) {
727a9e30698Scheloha 		if (usecs == 0) {
72814f07311Sjcs 			DPRINTF(("%s: %s: timed out, status 0x%x != 0x%x\n",
72914f07311Sjcs 			    sc->sc_dev.dv_xname, __func__, status, mask));
73014f07311Sjcs 			return status;
73114f07311Sjcs 		}
73214f07311Sjcs 
733a9e30698Scheloha 		usecs--;
73414f07311Sjcs 		DELAY(1);
73514f07311Sjcs 	}
73614f07311Sjcs 
73714f07311Sjcs 	return 0;
73814f07311Sjcs }
73914f07311Sjcs 
74014f07311Sjcs int
tpm_read_tis(struct tpm_softc * sc,void * buf,int len,size_t * count,int flags)741306068a2Sdv tpm_read_tis(struct tpm_softc *sc, void *buf, int len, size_t *count,
74214f07311Sjcs     int flags)
74314f07311Sjcs {
74414f07311Sjcs 	uint8_t *p = buf;
74514f07311Sjcs 	uint8_t c;
74614f07311Sjcs 	size_t cnt;
74714f07311Sjcs 	int rv, n, bcnt;
74814f07311Sjcs 
74914f07311Sjcs 	DPRINTF(("%s: %s %d:", sc->sc_dev.dv_xname, __func__, len));
75014f07311Sjcs 
75114f07311Sjcs 	cnt = 0;
75214f07311Sjcs 	while (len > 0) {
753306068a2Sdv 		if ((rv = tpm_waitfor_status(sc,
754306068a2Sdv 		    TPM_STS_DATA_AVAIL | TPM_STS_VALID, TPM_READ_TMO)))
75514f07311Sjcs 			return rv;
75614f07311Sjcs 
75714f07311Sjcs 		bcnt = tpm_getburst(sc);
75814f07311Sjcs 		n = MIN(len, bcnt);
75914f07311Sjcs 
76014f07311Sjcs 		for (; n--; len--) {
76114f07311Sjcs 			c = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_DATA);
76214f07311Sjcs 			DPRINTF((" %02x", c));
76314f07311Sjcs 			*p++ = c;
76414f07311Sjcs 			cnt++;
76514f07311Sjcs 		}
76614f07311Sjcs 
76714f07311Sjcs 		if ((flags & TPM_PARAM_SIZE) == 0 && cnt >= 6)
76814f07311Sjcs 			break;
76914f07311Sjcs 	}
77014f07311Sjcs 
77114f07311Sjcs 	DPRINTF(("\n"));
77214f07311Sjcs 
77314f07311Sjcs 	if (count)
77414f07311Sjcs 		*count = cnt;
77514f07311Sjcs 
77614f07311Sjcs 	return 0;
77714f07311Sjcs }
77814f07311Sjcs 
77914f07311Sjcs int
tpm_read_crb(struct tpm_softc * sc,void * buf,int len)780306068a2Sdv tpm_read_crb(struct tpm_softc *sc, void *buf, int len)
781306068a2Sdv {
782306068a2Sdv 	uint8_t *p = buf;
783306068a2Sdv 	uint32_t sz = 0, mask, rc;
784306068a2Sdv 	size_t count = 0;
785306068a2Sdv 	int r;
786306068a2Sdv 
787306068a2Sdv 	DPRINTF(("%s: %s %d:", sc->sc_dev.dv_xname, __func__, len));
788306068a2Sdv 
789306068a2Sdv 	if (len < TPM_HDRSIZE) {
790306068a2Sdv 		printf("%s: %s buf len too small\n", sc->sc_dev.dv_xname,
791306068a2Sdv 		    __func__);
792306068a2Sdv 		return EINVAL;
793306068a2Sdv 	}
794306068a2Sdv 
795306068a2Sdv 	while (count < TPM_HDRSIZE) {
796306068a2Sdv 		*p = bus_space_read_1(sc->sc_bt, sc->sc_bh,
797306068a2Sdv 		    sc->sc_rsp_off + count);
798306068a2Sdv 		DPRINTF((" %02x", *p));
799306068a2Sdv 		count++;
800306068a2Sdv 		p++;
801306068a2Sdv 	}
802306068a2Sdv 	DPRINTF(("\n"));
803306068a2Sdv 
804306068a2Sdv 	/* Response length is bytes 2-5 in the response header. */
805306068a2Sdv 	p = buf;
806306068a2Sdv 	sz = be32toh(*(uint32_t *) (p + 2));
807306068a2Sdv 	if (sz < TPM_HDRSIZE || sz > sc->sc_rsp_sz) {
808306068a2Sdv 		printf("%s: invalid response size %d\n",
809306068a2Sdv 		    sc->sc_dev.dv_xname, sz);
810306068a2Sdv 		return EIO;
811306068a2Sdv 	}
812306068a2Sdv 	if (sz > len)
813306068a2Sdv 		printf("%s: response size too large, truncated to %d\n",
814306068a2Sdv 		    sc->sc_dev.dv_xname, len);
815306068a2Sdv 
816306068a2Sdv 	/* Response code is bytes 6-9. */
817306068a2Sdv 	rc = be32toh(*(uint32_t *) (p + 6));
818306068a2Sdv 	if (rc != TPM2_RC_SUCCESS) {
819306068a2Sdv 		printf("%s: command failed (0x%04x)\n", sc->sc_dev.dv_xname,
820306068a2Sdv 		    rc);
821306068a2Sdv 		/* Nothing we can do on failure. Still try to idle the tpm. */
822306068a2Sdv 	}
823306068a2Sdv 
824306068a2Sdv 	/* Tell the device to go idle. */
825306068a2Sdv 	r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_CRB_CTRL_REQ);
826306068a2Sdv 	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_CRB_CTRL_REQ,
827306068a2Sdv 	    r | TPM_CRB_CTRL_REQ_GO_IDLE);
828306068a2Sdv 
829306068a2Sdv 	mask = TPM_CRB_CTRL_STS_IDLE_BIT;
830306068a2Sdv 	if (tpm_waitfor(sc, TPM_CRB_CTRL_STS, mask, mask, 200)) {
831306068a2Sdv 		printf("%s: failed to transition to idle state after read\n",
832306068a2Sdv 		    sc->sc_dev.dv_xname);
833306068a2Sdv 	}
834306068a2Sdv 
835306068a2Sdv 	tpm_release_locality_crb(sc);
836306068a2Sdv 
837306068a2Sdv 	DPRINTF(("%s: %s completed\n", sc->sc_dev.dv_xname, __func__));
838306068a2Sdv 	return 0;
839306068a2Sdv }
840306068a2Sdv 
841306068a2Sdv int
tpm_write_tis(struct tpm_softc * sc,void * buf,int len)842306068a2Sdv tpm_write_tis(struct tpm_softc *sc, void *buf, int len)
84314f07311Sjcs {
84414f07311Sjcs 	uint8_t *p = buf;
84514f07311Sjcs 	uint8_t status;
84614f07311Sjcs 	size_t count = 0;
84714f07311Sjcs 	int rv, r;
84814f07311Sjcs 
849306068a2Sdv 	if ((rv = tpm_request_locality_tis(sc, 0)) != 0)
85014f07311Sjcs 		return rv;
85114f07311Sjcs 
85214f07311Sjcs 	DPRINTF(("%s: %s %d:", sc->sc_dev.dv_xname, __func__, len));
85314f07311Sjcs 	for (r = 0; r < len; r++)
85414f07311Sjcs 		DPRINTF((" %02x", (uint8_t)(*(p + r))));
85514f07311Sjcs 	DPRINTF(("\n"));
85614f07311Sjcs 
85714f07311Sjcs 	/* read status */
85814f07311Sjcs 	status = tpm_status(sc);
85914f07311Sjcs 	if ((status & TPM_STS_CMD_READY) == 0) {
86014f07311Sjcs 		/* abort! */
86114f07311Sjcs 		bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_STS,
86214f07311Sjcs 		    TPM_STS_CMD_READY);
863306068a2Sdv 		if ((rv = tpm_waitfor_status(sc, TPM_STS_CMD_READY,
864306068a2Sdv 		    TPM_READ_TMO))) {
86514f07311Sjcs 			DPRINTF(("%s: failed waiting for ready after abort "
86614f07311Sjcs 			    "(0x%x)\n", sc->sc_dev.dv_xname, rv));
86714f07311Sjcs 			return rv;
86814f07311Sjcs 		}
86914f07311Sjcs 	}
87014f07311Sjcs 
87114f07311Sjcs 	while (count < len - 1) {
87214f07311Sjcs 		for (r = tpm_getburst(sc); r > 0 && count < len - 1; r--) {
87314f07311Sjcs 			DPRINTF(("%s: %s: write1(0x%x, 0x%x)\n",
87414f07311Sjcs 			    sc->sc_dev.dv_xname, __func__, TPM_DATA, *p));
87514f07311Sjcs 			bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_DATA, *p++);
87614f07311Sjcs 			count++;
87714f07311Sjcs 		}
878306068a2Sdv 		if ((rv = tpm_waitfor_status(sc, TPM_STS_VALID | TPM_STS_DATA_EXPECT,
87914f07311Sjcs 		    TPM_READ_TMO))) {
88014f07311Sjcs 			DPRINTF(("%s: %s: failed waiting for next byte (%d)\n",
88114f07311Sjcs 			    sc->sc_dev.dv_xname, __func__, rv));
88214f07311Sjcs 			return rv;
88314f07311Sjcs 		}
88414f07311Sjcs 	}
88514f07311Sjcs 
88614f07311Sjcs 	DPRINTF(("%s: %s: write1(0x%x, 0x%x)\n", sc->sc_dev.dv_xname, __func__,
88714f07311Sjcs 	    TPM_DATA, *p));
88814f07311Sjcs 	bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_DATA, *p);
88914f07311Sjcs 	count++;
89014f07311Sjcs 
891306068a2Sdv 	if ((rv = tpm_waitfor_status(sc, TPM_STS_VALID, TPM_READ_TMO))) {
89214f07311Sjcs 		DPRINTF(("%s: %s: failed after last byte (%d)\n",
89314f07311Sjcs 		    sc->sc_dev.dv_xname, __func__, rv));
89414f07311Sjcs 		return rv;
89514f07311Sjcs 	}
89614f07311Sjcs 
89714f07311Sjcs 	if ((status = tpm_status(sc)) & TPM_STS_DATA_EXPECT) {
89814f07311Sjcs 		DPRINTF(("%s: %s: final status still expecting data: %b\n",
89914f07311Sjcs 		    sc->sc_dev.dv_xname, __func__, status, TPM_STS_BITS));
90014f07311Sjcs 		return status;
90114f07311Sjcs 	}
90214f07311Sjcs 
90314f07311Sjcs 	DPRINTF(("%s: final status after write: %b\n", sc->sc_dev.dv_xname,
90414f07311Sjcs 	    status, TPM_STS_BITS));
90514f07311Sjcs 
90614f07311Sjcs 	/* XXX: are we ever sending non-command data? */
90714f07311Sjcs 	bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_STS, TPM_STS_GO);
90814f07311Sjcs 
90914f07311Sjcs 	return 0;
91014f07311Sjcs }
911306068a2Sdv 
912306068a2Sdv int
tpm_write_crb(struct tpm_softc * sc,void * buf,int len)913306068a2Sdv tpm_write_crb(struct tpm_softc *sc, void *buf, int len)
914306068a2Sdv {
915306068a2Sdv 	uint8_t *p = buf;
916306068a2Sdv 	size_t count = 0;
917306068a2Sdv 	uint32_t r, mask;
918306068a2Sdv 
919306068a2Sdv 	if (len > sc->sc_cmd_sz) {
920306068a2Sdv 		printf("%s: requested write length larger than cmd buffer\n",
921306068a2Sdv 		    sc->sc_dev.dv_xname);
922306068a2Sdv 		return EINVAL;
923306068a2Sdv 	}
924306068a2Sdv 
925306068a2Sdv 	if (bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_CRB_CTRL_STS)
926306068a2Sdv 	    & TPM_CRB_CTRL_STS_ERR_BIT) {
927306068a2Sdv 		printf("%s: device error bit set\n", sc->sc_dev.dv_xname);
928306068a2Sdv 		return EIO;
929306068a2Sdv 	}
930306068a2Sdv 
931306068a2Sdv 	if (tpm_request_locality_crb(sc, 0)) {
932306068a2Sdv 		printf("%s: failed to acquire locality\n", sc->sc_dev.dv_xname);
933306068a2Sdv 		return EIO;
934306068a2Sdv 	}
935306068a2Sdv 
936306068a2Sdv 	/* Clear cancellation bit */
937306068a2Sdv 	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_CRB_CTRL_CANCEL,
938306068a2Sdv 	    TPM_CRB_CTRL_CANCEL_CLEAR);
939306068a2Sdv 
940306068a2Sdv 	/* Toggle to idle state (if needed) and then to ready */
941306068a2Sdv 	r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_CRB_CTRL_STS);
942306068a2Sdv 	if(!(r & TPM_CRB_CTRL_STS_IDLE_BIT)) {
943306068a2Sdv 		printf("%s: asking device to idle\n", sc->sc_dev.dv_xname);
944306068a2Sdv 		r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_CRB_CTRL_REQ);
945306068a2Sdv 		bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_CRB_CTRL_REQ,
946306068a2Sdv 		    r | TPM_CRB_CTRL_REQ_GO_IDLE);
947306068a2Sdv 
948306068a2Sdv 		mask = TPM_CRB_CTRL_STS_IDLE_BIT;
949306068a2Sdv 		if (tpm_waitfor(sc, TPM_CRB_CTRL_STS, mask, mask, 200)) {
950306068a2Sdv 			printf("%s: failed to transition to idle state before "
951306068a2Sdv 			    "write\n", sc->sc_dev.dv_xname);
952306068a2Sdv 			return EIO;
953306068a2Sdv 		}
954306068a2Sdv 	}
955306068a2Sdv 	r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_CRB_CTRL_REQ);
956306068a2Sdv 	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_CRB_CTRL_REQ,
957306068a2Sdv  	    r | TPM_CRB_CTRL_REQ_GO_READY);
958306068a2Sdv 	mask = TPM_CRB_CTRL_REQ_GO_READY;
959306068a2Sdv 	if (tpm_waitfor(sc, TPM_CRB_CTRL_STS, mask, !mask, 200)) {
960306068a2Sdv 		printf("%s: failed to transition to ready state\n",
961306068a2Sdv 		    sc->sc_dev.dv_xname);
962306068a2Sdv 		return EIO;
963306068a2Sdv 	}
964306068a2Sdv 
965306068a2Sdv 	/* Write the command */
966306068a2Sdv 	DPRINTF(("%s: %s %d:", sc->sc_dev.dv_xname, __func__, len));
967306068a2Sdv 	while (count < len) {
968306068a2Sdv 		DPRINTF((" %02x", (uint8_t)(*p)));
969306068a2Sdv 		bus_space_write_1(sc->sc_bt, sc->sc_bh, sc->sc_cmd_off + count,
970306068a2Sdv 		    *p++);
971306068a2Sdv 		count++;
972306068a2Sdv 	}
973306068a2Sdv 	DPRINTF(("\n"));
974306068a2Sdv 	bus_space_barrier(sc->sc_bt, sc->sc_bh, sc->sc_cmd_off, len,
975306068a2Sdv 	    BUS_SPACE_BARRIER_WRITE);
976306068a2Sdv 	DPRINTF(("%s: %s wrote %lu bytes\n", sc->sc_dev.dv_xname, __func__,
977306068a2Sdv 	    count));
978306068a2Sdv 
979306068a2Sdv 	/* Send the Start Command request */
980306068a2Sdv 	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_CRB_CTRL_START,
981306068a2Sdv 	    TPM_CRB_CTRL_START_CMD);
982306068a2Sdv 	bus_space_barrier(sc->sc_bt, sc->sc_bh, TPM_CRB_CTRL_START, 4,
983306068a2Sdv 	    BUS_SPACE_BARRIER_WRITE);
984306068a2Sdv 
985306068a2Sdv 	/* Check if command was processed */
986306068a2Sdv 	mask = ~0;
987306068a2Sdv 	if (tpm_waitfor(sc, TPM_CRB_CTRL_START, mask, ~mask, 200)) {
988306068a2Sdv 		printf("%s: timeout waiting for device to process command\n",
989306068a2Sdv 		    sc->sc_dev.dv_xname);
990306068a2Sdv 		return EIO;
991306068a2Sdv 	}
992306068a2Sdv 
993306068a2Sdv 	return 0;
994306068a2Sdv }
995