1211201Stakawata/*
2211201Stakawata * Copyright (c) 2008, 2009 Michael Shalayeff
3211201Stakawata * Copyright (c) 2009, 2010 Hans-Joerg Hoexer
4211201Stakawata * All rights reserved.
5211201Stakawata *
6211201Stakawata * Permission to use, copy, modify, and distribute this software for any
7211201Stakawata * purpose with or without fee is hereby granted, provided that the above
8211201Stakawata * copyright notice and this permission notice appear in all copies.
9211201Stakawata *
10211201Stakawata * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11211201Stakawata * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12211201Stakawata * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13211201Stakawata * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14211201Stakawata * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
15211201Stakawata * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
16211201Stakawata * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17211201Stakawata */
18211201Stakawata
19211201Stakawata/* #define	TPM_DEBUG */
20211201Stakawata
21211201Stakawata#include <sys/cdefs.h>
22211201Stakawata__FBSDID("$FreeBSD$");
23211201Stakawata
24211201Stakawata#include <sys/param.h>
25211201Stakawata#include <sys/systm.h>
26211201Stakawata#include <sys/kernel.h>
27211201Stakawata#include <sys/malloc.h>
28211201Stakawata#include <sys/proc.h>
29211201Stakawata
30211201Stakawata#ifdef __FreeBSD__
31211201Stakawata#include <sys/module.h>
32211201Stakawata#include <sys/conf.h>
33211201Stakawata#include <sys/uio.h>
34211201Stakawata#include <sys/bus.h>
35211201Stakawata
36211201Stakawata#include <machine/bus.h>
37211201Stakawata#include <sys/rman.h>
38211201Stakawata#include <machine/resource.h>
39211201Stakawata
40211201Stakawata#include <machine/md_var.h>
41211201Stakawata
42211201Stakawata#include <isa/isareg.h>
43211201Stakawata#include <isa/isavar.h>
44211201Stakawata#else
45211201Stakawata#include <sys/device.h>
46211201Stakawata
47211201Stakawata#include <machine/cpu.h>
48211201Stakawata#include <machine/bus.h>
49211201Stakawata#include <machine/intr.h>
50211201Stakawata#include <machine/conf.h>
51211201Stakawata
52211201Stakawata#include <dev/isa/isareg.h>
53211201Stakawata#include <dev/isa/isavar.h>
54211201Stakawata#endif
55211201Stakawata#include <dev/tpm/tpmvar.h>
56211201Stakawata
57211201Stakawata#ifndef __FreeBSD__
58211201Stakawata/* XXX horrible hack for tcsd (-lpthread) workaround on OpenBSD */
59211201Stakawata#undef PCATCH
60211201Stakawata#define PCATCH	0
61211201Stakawata#endif
62211201Stakawata
63211201Stakawata#define	TPM_BUFSIZ	1024
64211201Stakawata
65211201Stakawata#define TPM_HDRSIZE	10
66211201Stakawata
67211201Stakawata#define TPM_PARAM_SIZE	0x0001
68211201Stakawata
69211201Stakawata#ifdef __FreeBSD__
70211201Stakawata#define IRQUNK	-1
71211201Stakawata#endif
72211201Stakawata
73211201Stakawata#define	TPM_ACCESS			0x0000	/* acess register */
74211201Stakawata#define	TPM_ACCESS_ESTABLISHMENT	0x01	/* establishment */
75211201Stakawata#define	TPM_ACCESS_REQUEST_USE		0x02	/* request using locality */
76211201Stakawata#define	TPM_ACCESS_REQUEST_PENDING	0x04	/* pending request */
77211201Stakawata#define	TPM_ACCESS_SEIZE		0x08	/* request locality seize */
78211201Stakawata#define	TPM_ACCESS_SEIZED		0x10	/* locality has been seized */
79211201Stakawata#define	TPM_ACCESS_ACTIVE_LOCALITY	0x20	/* locality is active */
80211201Stakawata#define	TPM_ACCESS_VALID		0x80	/* bits are valid */
81211201Stakawata#define	TPM_ACCESS_BITS	\
82211201Stakawata    "\020\01EST\02REQ\03PEND\04SEIZE\05SEIZED\06ACT\010VALID"
83211201Stakawata
84211201Stakawata#define	TPM_INTERRUPT_ENABLE	0x0008
85211201Stakawata#define	TPM_GLOBAL_INT_ENABLE	0x80000000	/* enable ints */
86211201Stakawata#define	TPM_CMD_READY_INT	0x00000080	/* cmd ready enable */
87211201Stakawata#define	TPM_INT_EDGE_FALLING	0x00000018
88211201Stakawata#define	TPM_INT_EDGE_RISING	0x00000010
89211201Stakawata#define	TPM_INT_LEVEL_LOW	0x00000008
90211201Stakawata#define	TPM_INT_LEVEL_HIGH	0x00000000
91211201Stakawata#define	TPM_LOCALITY_CHANGE_INT	0x00000004	/* locality change enable */
92211201Stakawata#define	TPM_STS_VALID_INT	0x00000002	/* int on TPM_STS_VALID is set */
93211201Stakawata#define	TPM_DATA_AVAIL_INT	0x00000001	/* int on TPM_STS_DATA_AVAIL is set */
94211201Stakawata#define	TPM_INTERRUPT_ENABLE_BITS \
95211201Stakawata    "\020\040ENA\010RDY\03LOCH\02STSV\01DRDY"
96211201Stakawata
97211201Stakawata#define	TPM_INT_VECTOR		0x000c	/* 8 bit reg for 4 bit irq vector */
98211201Stakawata#define	TPM_INT_STATUS		0x0010	/* bits are & 0x87 from TPM_INTERRUPT_ENABLE */
99211201Stakawata
100211201Stakawata#define	TPM_INTF_CAPABILITIES		0x0014	/* capability register */
101211201Stakawata#define	TPM_INTF_BURST_COUNT_STATIC	0x0100	/* TPM_STS_BMASK static */
102211201Stakawata#define	TPM_INTF_CMD_READY_INT		0x0080	/* int on ready supported */
103211201Stakawata#define	TPM_INTF_INT_EDGE_FALLING	0x0040	/* falling edge ints supported */
104211201Stakawata#define	TPM_INTF_INT_EDGE_RISING	0x0020	/* rising edge ints supported */
105211201Stakawata#define	TPM_INTF_INT_LEVEL_LOW		0x0010	/* level-low ints supported */
106211201Stakawata#define	TPM_INTF_INT_LEVEL_HIGH		0x0008	/* level-high ints supported */
107211201Stakawata#define	TPM_INTF_LOCALITY_CHANGE_INT	0x0004	/* locality-change int (mb 1) */
108211201Stakawata#define	TPM_INTF_STS_VALID_INT		0x0002	/* TPM_STS_VALID int supported */
109211201Stakawata#define	TPM_INTF_DATA_AVAIL_INT		0x0001	/* TPM_STS_DATA_AVAIL int supported (mb 1) */
110211201Stakawata#define	TPM_CAPSREQ \
111211201Stakawata  (TPM_INTF_DATA_AVAIL_INT|TPM_INTF_LOCALITY_CHANGE_INT|TPM_INTF_INT_LEVEL_LOW)
112211201Stakawata#define	TPM_CAPBITS \
113211201Stakawata  "\020\01IDRDY\02ISTSV\03ILOCH\04IHIGH\05ILOW\06IEDGE\07IFALL\010IRDY\011BCST"
114211201Stakawata
115211201Stakawata#define	TPM_STS			0x0018		/* status register */
116211201Stakawata#define TPM_STS_MASK		0x000000ff	/* status bits */
117211201Stakawata#define	TPM_STS_BMASK		0x00ffff00	/* ro io burst size */
118211201Stakawata#define	TPM_STS_VALID		0x00000080	/* ro other bits are valid */
119211201Stakawata#define	TPM_STS_CMD_READY	0x00000040	/* rw chip/signal ready */
120211201Stakawata#define	TPM_STS_GO		0x00000020	/* wo start the command */
121211201Stakawata#define	TPM_STS_DATA_AVAIL	0x00000010	/* ro data available */
122211201Stakawata#define	TPM_STS_DATA_EXPECT	0x00000008	/* ro more data to be written */
123211201Stakawata#define	TPM_STS_RESP_RETRY	0x00000002	/* wo resend the response */
124211201Stakawata#define	TPM_STS_BITS	"\020\010VALID\07RDY\06GO\05DRDY\04EXPECT\02RETRY"
125211201Stakawata
126211201Stakawata#define	TPM_DATA	0x0024
127211201Stakawata#define	TPM_ID		0x0f00
128211201Stakawata#define	TPM_REV		0x0f04
129211201Stakawata#define	TPM_SIZE	0x5000		/* five pages of the above */
130211201Stakawata
131211201Stakawata#define	TPM_ACCESS_TMO	2000		/* 2sec */
132211201Stakawata#define	TPM_READY_TMO	2000		/* 2sec */
133211201Stakawata#define	TPM_READ_TMO	120000		/* 2 minutes */
134211201Stakawata#define TPM_BURST_TMO	2000		/* 2sec */
135211201Stakawata
136211201Stakawata#define	TPM_LEGACY_BUSY	0x01
137211201Stakawata#define	TPM_LEGACY_ABRT	0x01
138211201Stakawata#define	TPM_LEGACY_DA	0x02
139211201Stakawata#define	TPM_LEGACY_RE	0x04
140211201Stakawata#define	TPM_LEGACY_LAST	0x04
141211201Stakawata#define	TPM_LEGACY_BITS	"\020\01BUSY\2DA\3RE\4LAST"
142211201Stakawata#define	TPM_LEGACY_TMO		(2*60)	/* sec */
143211201Stakawata#define	TPM_LEGACY_SLEEP	5	/* ticks */
144211201Stakawata#define	TPM_LEGACY_DELAY	100
145211201Stakawata
146211201Stakawata/* Set when enabling legacy interface in host bridge. */
147211201Stakawataint tpm_enabled;
148211201Stakawata
149211201Stakawata
150211201Stakawata#ifdef __FreeBSD__
151211201Stakawata#define	TPMSOFTC(dev) \
152211201Stakawata	((struct tpm_softc *)dev->si_drv1)
153211201Stakawata
154211201Stakawatad_open_t	tpmopen;
155211201Stakawatad_close_t	tpmclose;
156211201Stakawatad_read_t	tpmread;
157211201Stakawatad_write_t	tpmwrite;
158211201Stakawatad_ioctl_t	tpmioctl;
159211201Stakawata
160211201Stakawatastatic struct cdevsw tpm_cdevsw = {
161211201Stakawata	.d_version =	D_VERSION,
162211201Stakawata	.d_flags =	D_NEEDGIANT,
163211201Stakawata	.d_open =	tpmopen,
164211201Stakawata	.d_close =	tpmclose,
165211201Stakawata	.d_read =	tpmread,
166211201Stakawata	.d_write =	tpmwrite,
167211201Stakawata	.d_ioctl =	tpmioctl,
168211201Stakawata	.d_name =	"tpm",
169211201Stakawata};
170211201Stakawata#else
171211201Stakawata#define	TPMSOFTC(dev) \
172211201Stakawata    (struct tpm_softc *)device_lookup(&tpm_cd, minor(dev))
173211201Stakawata
174211201Stakawatastruct cfdriver tpm_cd = {
175211201Stakawata	NULL, "tpm", DV_DULL
176211201Stakawata};
177211201Stakawata
178211201Stakawataint	tpm_match(struct device *, void *, void *);
179211201Stakawatavoid	tpm_attach(struct device *, struct device *, void *);
180211201Stakawata
181211201Stakawatastruct cfattach tpm_ca = {
182211201Stakawata	sizeof(struct tpm_softc), tpm_match, tpm_attach
183211201Stakawata};
184211201Stakawata#endif
185211201Stakawata
186211201Stakawataconst struct {
187211201Stakawata	u_int32_t devid;
188211201Stakawata	char name[32];
189211201Stakawata	int flags;
190211201Stakawata#define TPM_DEV_NOINTS	0x0001
191211201Stakawata} tpm_devs[] = {
192211201Stakawata	{ 0x000615d1, "IFX SLD 9630 TT 1.1", 0 },
193211201Stakawata	{ 0x000b15d1, "IFX SLB 9635 TT 1.2", 0 },
194211201Stakawata	{ 0x100214e4, "Broadcom BCM0102", TPM_DEV_NOINTS },
195211201Stakawata	{ 0x00fe1050, "WEC WPCT200", 0 },
196211201Stakawata	{ 0x687119fa, "SNS SSX35", 0 },
197211201Stakawata	{ 0x2e4d5453, "STM ST19WP18", 0 },
198211201Stakawata	{ 0x32021114, "ATML 97SC3203", TPM_DEV_NOINTS },
199211201Stakawata	{ 0x10408086, "INTEL INTC0102", 0 },
200211201Stakawata	{ 0, "", TPM_DEV_NOINTS },
201211201Stakawata};
202211201Stakawata
203211201Stakawataint tpm_tis12_irqinit(struct tpm_softc *, int, int);
204211201Stakawataint tpm_tis12_init(struct tpm_softc *, int, const char *);
205211201Stakawataint tpm_tis12_start(struct tpm_softc *, int);
206211201Stakawataint tpm_tis12_read(struct tpm_softc *, void *, int, size_t *, int);
207211201Stakawataint tpm_tis12_write(struct tpm_softc *, void *, int);
208211201Stakawataint tpm_tis12_end(struct tpm_softc *, int, int);
209211201Stakawata
210211201Stakawata#ifdef __FreeBSD__
211211201Stakawatavoid tpm_intr(void *);
212211201Stakawata#else
213211201Stakawataint tpm_intr(void *);
214211201Stakawatavoid tpm_powerhook(int, void *);
215211201Stakawataint tpm_suspend(struct tpm_softc *, int);
216211201Stakawataint tpm_resume(struct tpm_softc *, int);
217211201Stakawata#endif
218211201Stakawata
219211201Stakawataint tpm_waitfor_poll(struct tpm_softc *, u_int8_t, int, void *);
220211201Stakawataint tpm_waitfor_int(struct tpm_softc *, u_int8_t, int, void *, int);
221211201Stakawataint tpm_waitfor(struct tpm_softc *, u_int8_t, int, void *);
222211201Stakawataint tpm_request_locality(struct tpm_softc *, int);
223211201Stakawataint tpm_getburst(struct tpm_softc *);
224211201Stakawatau_int8_t tpm_status(struct tpm_softc *);
225211201Stakawataint tpm_tmotohz(int);
226211201Stakawata
227211201Stakawataint tpm_legacy_probe(bus_space_tag_t, bus_addr_t);
228211201Stakawataint tpm_legacy_init(struct tpm_softc *, int, const char *);
229211201Stakawataint tpm_legacy_start(struct tpm_softc *, int);
230211201Stakawataint tpm_legacy_read(struct tpm_softc *, void *, int, size_t *, int);
231211201Stakawataint tpm_legacy_write(struct tpm_softc *, void *, int);
232211201Stakawataint tpm_legacy_end(struct tpm_softc *, int, int);
233211201Stakawata
234211201Stakawata#ifdef __FreeBSD__
235211201Stakawata
236211201Stakawata/*
237211201Stakawata * FreeBSD specific code for probing and attaching TPM to device tree.
238211201Stakawata */
239211201Stakawata#if 0
240211201Stakawatastatic void
241211201Stakawatatpm_identify(driver_t *driver, device_t parent)
242211201Stakawata{
243211201Stakawata	BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "tpm", 0);
244211201Stakawata}
245211201Stakawata#endif
246211201Stakawata
247211201Stakawata
248211201Stakawataint
249211201Stakawatatpm_attach(device_t dev)
250211201Stakawata{
251211201Stakawata	struct tpm_softc *sc = device_get_softc(dev);
252211201Stakawata	int irq;
253211201Stakawata
254211201Stakawata	sc->mem_rid = 0;
255211201Stakawata	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
256211201Stakawata	    RF_ACTIVE);
257211201Stakawata	if (sc->mem_res == NULL)
258211201Stakawata		return ENXIO;
259211201Stakawata
260211201Stakawata	sc->sc_bt = rman_get_bustag(sc->mem_res);
261211201Stakawata	sc->sc_bh = rman_get_bushandle(sc->mem_res);
262211201Stakawata
263211201Stakawata	sc->irq_rid = 0;
264211201Stakawata	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid,
265211201Stakawata	    RF_ACTIVE | RF_SHAREABLE);
266211201Stakawata	if (sc->irq_res != NULL)
267211201Stakawata		irq = rman_get_start(sc->irq_res);
268211201Stakawata	else
269211201Stakawata		irq = IRQUNK;
270211201Stakawata
271211201Stakawata	/* In case PnP probe this may contain some initialization. */
272211201Stakawata	tpm_tis12_probe(sc->sc_bt, sc->sc_bh);
273211201Stakawata
274211201Stakawata	if (tpm_legacy_probe(sc->sc_bt, sc->sc_bh)) {
275211201Stakawata		sc->sc_init = tpm_legacy_init;
276211201Stakawata		sc->sc_start = tpm_legacy_start;
277211201Stakawata		sc->sc_read = tpm_legacy_read;
278211201Stakawata		sc->sc_write = tpm_legacy_write;
279211201Stakawata		sc->sc_end = tpm_legacy_end;
280211201Stakawata	} else {
281211201Stakawata		sc->sc_init = tpm_tis12_init;
282211201Stakawata		sc->sc_start = tpm_tis12_start;
283211201Stakawata		sc->sc_read = tpm_tis12_read;
284211201Stakawata		sc->sc_write = tpm_tis12_write;
285211201Stakawata		sc->sc_end = tpm_tis12_end;
286211201Stakawata	}
287211201Stakawata
288211201Stakawata	printf("%s", device_get_name(dev));
289211201Stakawata	if ((sc->sc_init)(sc, irq, "tpm")) {
290211201Stakawata		tpm_detach(dev);
291211201Stakawata		return ENXIO;
292211201Stakawata	}
293211201Stakawata
294211201Stakawata	if (sc->sc_init == tpm_tis12_init && sc->irq_res != NULL &&
295211201Stakawata	    bus_setup_intr(dev, sc->irq_res, INTR_TYPE_TTY, NULL,
296211201Stakawata	    tpm_intr, sc, &sc->intr_cookie) != 0) {
297211201Stakawata		tpm_detach(dev);
298211201Stakawata		printf(": cannot establish interrupt\n");
299211201Stakawata		return 1;
300211201Stakawata	}
301211201Stakawata
302211201Stakawata	sc->sc_cdev = make_dev(&tpm_cdevsw, device_get_unit(dev),
303211201Stakawata			    UID_ROOT, GID_WHEEL, 0600, "tpm");
304211201Stakawata	sc->sc_cdev->si_drv1 = sc;
305211201Stakawata
306211201Stakawata	return 0;
307211201Stakawata}
308211201Stakawata
309211201Stakawataint
310211201Stakawatatpm_detach(device_t dev)
311211201Stakawata{
312211201Stakawata	struct tpm_softc * sc = device_get_softc(dev);
313211201Stakawata
314211201Stakawata	if(sc->intr_cookie){
315211201Stakawata		bus_teardown_intr(dev, sc->irq_res, sc->intr_cookie);
316211201Stakawata	}
317211201Stakawata
318211201Stakawata	if(sc->mem_res){
319211201Stakawata		bus_release_resource(dev, SYS_RES_MEMORY,
320211201Stakawata				     sc->mem_rid, sc->mem_res);
321211201Stakawata	}
322211201Stakawata
323211201Stakawata	if(sc->irq_res){
324211201Stakawata		bus_release_resource(dev, SYS_RES_IRQ,
325211201Stakawata				     sc->irq_rid, sc->irq_res);
326211201Stakawata	}
327211201Stakawata	if(sc->sc_cdev){
328211201Stakawata		destroy_dev(sc->sc_cdev);
329211201Stakawata	}
330211201Stakawata
331211201Stakawata	return 0;
332211201Stakawata}
333211201Stakawata
334211201Stakawata
335211201Stakawata#else
336211201Stakawata/*
337211201Stakawata * OpenBSD specific code for probing and attaching TPM to device tree.
338211201Stakawata */
339211201Stakawataint
340211201Stakawatatpm_match(struct device *parent, void *match, void *aux)
341211201Stakawata{
342211201Stakawata	struct isa_attach_args *ia = aux;
343211201Stakawata	struct cfdata *cf = match;
344211201Stakawata	bus_space_tag_t bt = ia->ia_memt;
345211201Stakawata	bus_space_handle_t bh;
346211201Stakawata	int rv;
347211201Stakawata
348211201Stakawata	/* There can be only one. */
349211201Stakawata	if (cf->cf_unit)
350211201Stakawata		return 0;
351211201Stakawata
352211201Stakawata	if (tpm_legacy_probe(ia->ia_iot, ia->ia_iobase)) {
353211201Stakawata		ia->ia_iosize = 2;
354211201Stakawata		return 1;
355211201Stakawata	}
356211201Stakawata
357211201Stakawata	if (ia->ia_maddr == -1)
358211201Stakawata		return 0;
359211201Stakawata
360211201Stakawata	if (bus_space_map(bt, ia->ia_maddr, TPM_SIZE, 0, &bh))
361211201Stakawata		return 0;
362211201Stakawata
363211201Stakawata	if ((rv = tpm_tis12_probe(bt, bh))) {
364211201Stakawata		ia->ia_iosize = 0;
365211201Stakawata		ia->ia_msize = TPM_SIZE;
366211201Stakawata	}
367211201Stakawata
368211201Stakawata	bus_space_unmap(bt, bh, TPM_SIZE);
369211201Stakawata	return rv;
370211201Stakawata}
371211201Stakawata
372211201Stakawatavoid
373211201Stakawatatpm_attach(struct device *parent, struct device *self, void *aux)
374211201Stakawata{
375211201Stakawata	struct tpm_softc *sc = (struct tpm_softc *)self;
376211201Stakawata	struct isa_attach_args *ia = aux;
377211201Stakawata	bus_addr_t iobase;
378211201Stakawata	bus_size_t size;
379211201Stakawata	int rv;
380211201Stakawata
381211201Stakawata	if (tpm_legacy_probe(ia->ia_iot, ia->ia_iobase)) {
382211201Stakawata		sc->sc_bt = ia->ia_iot;
383211201Stakawata		iobase = ia->ia_iobase;
384211201Stakawata		size = ia->ia_iosize;
385211201Stakawata		sc->sc_batm = ia->ia_iot;
386211201Stakawata		sc->sc_init = tpm_legacy_init;
387211201Stakawata		sc->sc_start = tpm_legacy_start;
388211201Stakawata		sc->sc_read = tpm_legacy_read;
389211201Stakawata		sc->sc_write = tpm_legacy_write;
390211201Stakawata		sc->sc_end = tpm_legacy_end;
391211201Stakawata	} else {
392211201Stakawata		sc->sc_bt = ia->ia_memt;
393211201Stakawata		iobase = ia->ia_maddr;
394211201Stakawata		size = TPM_SIZE;
395211201Stakawata		sc->sc_init = tpm_tis12_init;
396211201Stakawata		sc->sc_start = tpm_tis12_start;
397211201Stakawata		sc->sc_read = tpm_tis12_read;
398211201Stakawata		sc->sc_write = tpm_tis12_write;
399211201Stakawata		sc->sc_end = tpm_tis12_end;
400211201Stakawata	}
401211201Stakawata
402211201Stakawata	if (bus_space_map(sc->sc_bt, iobase, size, 0, &sc->sc_bh)) {
403211201Stakawata		printf(": cannot map registers\n");
404211201Stakawata		return;
405211201Stakawata	}
406211201Stakawata
407211201Stakawata	if ((rv = (sc->sc_init)(sc, ia->ia_irq, sc->sc_dev.dv_xname))) {
408211201Stakawata		bus_space_unmap(sc->sc_bt, sc->sc_bh, size);
409211201Stakawata		return;
410211201Stakawata	}
411211201Stakawata
412211201Stakawata	/*
413211201Stakawata	 * Only setup interrupt handler when we have a vector and the
414211201Stakawata	 * chip is TIS 1.2 compliant.
415211201Stakawata	 */
416211201Stakawata	if (sc->sc_init == tpm_tis12_init && ia->ia_irq != IRQUNK &&
417211201Stakawata	    (sc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq, IST_EDGE,
418211201Stakawata	    IPL_TTY, tpm_intr, sc, sc->sc_dev.dv_xname)) == NULL) {
419211201Stakawata		bus_space_unmap(sc->sc_bt, sc->sc_bh, TPM_SIZE);
420211201Stakawata		printf("%s: cannot establish interrupt\n",
421211201Stakawata		    sc->sc_dev.dv_xname);
422211201Stakawata		return;
423211201Stakawata	}
424211201Stakawata
425211201Stakawata#ifdef __FreeBSD__
426211201Stakawata	sc->sc_suspend = 0;
427211201Stakawata#else
428211201Stakawata	sc->sc_suspend = PWR_RESUME;
429211201Stakawata	sc->sc_powerhook = powerhook_establish(tpm_powerhook, sc);
430211201Stakawata#endif
431211201Stakawata}
432211201Stakawata#endif
433211201Stakawata
434211201Stakawata/* Probe TPM using TIS 1.2 interface. */
435211201Stakawataint
436211201Stakawatatpm_tis12_probe(bus_space_tag_t bt, bus_space_handle_t bh)
437211201Stakawata{
438211201Stakawata	u_int32_t r;
439211201Stakawata	u_int8_t save, reg;
440211201Stakawata
441211201Stakawata	r = bus_space_read_4(bt, bh, TPM_INTF_CAPABILITIES);
442211201Stakawata	if (r == 0xffffffff)
443211201Stakawata		return 0;
444211201Stakawata
445211201Stakawata#ifdef TPM_DEBUG
446211201Stakawata	printf("tpm: caps=%b\n", r, TPM_CAPBITS);
447211201Stakawata#endif
448211201Stakawata	if ((r & TPM_CAPSREQ) != TPM_CAPSREQ ||
449211201Stakawata	    !(r & (TPM_INTF_INT_EDGE_RISING | TPM_INTF_INT_LEVEL_LOW))) {
450211201Stakawata#ifdef TPM_DEBUG
451211201Stakawata		printf("tpm: caps too low (caps=%b)\n", r, TPM_CAPBITS);
452211201Stakawata#endif
453211201Stakawata		return 0;
454211201Stakawata	}
455211201Stakawata
456211201Stakawata	save = bus_space_read_1(bt, bh, TPM_ACCESS);
457211201Stakawata	bus_space_write_1(bt, bh, TPM_ACCESS, TPM_ACCESS_REQUEST_USE);
458211201Stakawata	reg = bus_space_read_1(bt, bh, TPM_ACCESS);
459211201Stakawata	if ((reg & TPM_ACCESS_VALID) && (reg & TPM_ACCESS_ACTIVE_LOCALITY) &&
460211201Stakawata	    bus_space_read_4(bt, bh, TPM_ID) != 0xffffffff)
461211201Stakawata		return 1;
462211201Stakawata
463211201Stakawata	bus_space_write_1(bt, bh, TPM_ACCESS, save);
464211201Stakawata	return 0;
465211201Stakawata}
466211201Stakawata
467211201Stakawata/*
468211201Stakawata * Setup interrupt vector if one is provided and interrupts are know to
469211201Stakawata * work on that particular chip.
470211201Stakawata */
471211201Stakawataint
472211201Stakawatatpm_tis12_irqinit(struct tpm_softc *sc, int irq, int idx)
473211201Stakawata{
474211201Stakawata	u_int32_t r;
475211201Stakawata
476211201Stakawata	if ((irq == IRQUNK) || (tpm_devs[idx].flags & TPM_DEV_NOINTS)) {
477211201Stakawata		sc->sc_vector = IRQUNK;
478211201Stakawata		return 0;
479211201Stakawata	}
480211201Stakawata
481211201Stakawata	/* Ack and disable all interrupts. */
482211201Stakawata	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE,
483211201Stakawata	    bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE) &
484211201Stakawata	    ~TPM_GLOBAL_INT_ENABLE);
485211201Stakawata	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INT_STATUS,
486211201Stakawata	    bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INT_STATUS));
487211201Stakawata
488211201Stakawata	/* Program interrupt vector. */
489211201Stakawata	bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_INT_VECTOR, irq);
490211201Stakawata	sc->sc_vector = irq;
491211201Stakawata
492211201Stakawata	/* Program interrupt type. */
493211201Stakawata	if (sc->sc_capabilities & TPM_INTF_INT_EDGE_RISING)
494211201Stakawata		r = TPM_INT_EDGE_RISING;
495211201Stakawata	else if (sc->sc_capabilities & TPM_INTF_INT_LEVEL_HIGH)
496211201Stakawata		r = TPM_INT_LEVEL_HIGH;
497211201Stakawata	else
498211201Stakawata		r = TPM_INT_LEVEL_LOW;
499211201Stakawata	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE, r);
500211201Stakawata
501211201Stakawata	return 0;
502211201Stakawata}
503211201Stakawata
504211201Stakawata/* Setup TPM using TIS 1.2 interface. */
505211201Stakawataint
506211201Stakawatatpm_tis12_init(struct tpm_softc *sc, int irq, const char *name)
507211201Stakawata{
508211201Stakawata	u_int32_t r;
509211201Stakawata	int i;
510211201Stakawata
511211201Stakawata	r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INTF_CAPABILITIES);
512211201Stakawata#ifdef TPM_DEBUG
513211201Stakawata	printf(" caps=%b ", r, TPM_CAPBITS);
514211201Stakawata#endif
515211201Stakawata	if ((r & TPM_CAPSREQ) != TPM_CAPSREQ ||
516211201Stakawata	    !(r & (TPM_INTF_INT_EDGE_RISING | TPM_INTF_INT_LEVEL_LOW))) {
517211201Stakawata		printf(": capabilities too low (caps=%b)\n", r, TPM_CAPBITS);
518211201Stakawata		return 1;
519211201Stakawata	}
520211201Stakawata	sc->sc_capabilities = r;
521211201Stakawata
522211201Stakawata	sc->sc_devid = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_ID);
523211201Stakawata	sc->sc_rev = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_REV);
524211201Stakawata
525211201Stakawata	for (i = 0; tpm_devs[i].devid; i++)
526211201Stakawata		if (tpm_devs[i].devid == sc->sc_devid)
527211201Stakawata			break;
528211201Stakawata
529211201Stakawata	if (tpm_devs[i].devid)
530211201Stakawata		printf(": %s rev 0x%x\n", tpm_devs[i].name, sc->sc_rev);
531211201Stakawata	else
532211201Stakawata		printf(": device 0x%08x rev 0x%x\n", sc->sc_devid, sc->sc_rev);
533211201Stakawata
534211201Stakawata	if (tpm_tis12_irqinit(sc, irq, i))
535211201Stakawata		return 1;
536211201Stakawata
537211201Stakawata	if (tpm_request_locality(sc, 0))
538211201Stakawata		return 1;
539211201Stakawata
540211201Stakawata	/* Abort whatever it thought it was doing. */
541211201Stakawata	bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_STS, TPM_STS_CMD_READY);
542211201Stakawata
543211201Stakawata	return 0;
544211201Stakawata}
545211201Stakawata
546211201Stakawataint
547211201Stakawatatpm_request_locality(struct tpm_softc *sc, int l)
548211201Stakawata{
549211201Stakawata	u_int32_t r;
550211201Stakawata	int to, rv;
551211201Stakawata
552211201Stakawata	if (l != 0)
553211201Stakawata		return EINVAL;
554211201Stakawata
555211201Stakawata	if ((bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS) &
556211201Stakawata	    (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) ==
557211201Stakawata	    (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY))
558211201Stakawata		return 0;
559211201Stakawata
560211201Stakawata	bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS,
561211201Stakawata	    TPM_ACCESS_REQUEST_USE);
562211201Stakawata
563211201Stakawata	to = tpm_tmotohz(TPM_ACCESS_TMO);
564211201Stakawata
565211201Stakawata	while ((r = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS) &
566211201Stakawata	    (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) !=
567211201Stakawata	    (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY) && to--) {
568211201Stakawata		rv = tsleep(sc->sc_init, PRIBIO | PCATCH, "tpm_locality", 1);
569211201Stakawata		if (rv &&  rv != EWOULDBLOCK) {
570211201Stakawata#ifdef TPM_DEBUG
571211201Stakawata			printf("tpm_request_locality: interrupted %d\n", rv);
572211201Stakawata#endif
573211201Stakawata			return rv;
574211201Stakawata		}
575211201Stakawata	}
576211201Stakawata
577211201Stakawata	if ((r & (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) !=
578211201Stakawata	    (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) {
579211201Stakawata#ifdef TPM_DEBUG
580211201Stakawata		printf("tpm_request_locality: access %b\n", r, TPM_ACCESS_BITS);
581211201Stakawata#endif
582211201Stakawata		return EBUSY;
583211201Stakawata	}
584211201Stakawata
585211201Stakawata	return 0;
586211201Stakawata}
587211201Stakawata
588211201Stakawataint
589211201Stakawatatpm_getburst(struct tpm_softc *sc)
590211201Stakawata{
591211201Stakawata	int burst, to, rv;
592211201Stakawata
593211201Stakawata	to = tpm_tmotohz(TPM_BURST_TMO);
594211201Stakawata
595211201Stakawata	burst = 0;
596211201Stakawata	while (burst == 0 && to--) {
597211201Stakawata		/*
598211201Stakawata		 * Burst count has to be read from bits 8 to 23 without
599211201Stakawata		 * touching any other bits, eg. the actual status bits 0
600211201Stakawata		 * to 7.
601211201Stakawata		 */
602211201Stakawata		burst = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_STS + 1);
603211201Stakawata		burst |= bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_STS + 2)
604211201Stakawata		    << 8;
605211201Stakawata#ifdef TPM_DEBUG
606211201Stakawata		printf("tpm_getburst: read %d\n", burst);
607211201Stakawata#endif
608211201Stakawata		if (burst)
609211201Stakawata			return burst;
610211201Stakawata
611211201Stakawata		rv = tsleep(sc, PRIBIO | PCATCH, "tpm_getburst", 1);
612211201Stakawata		if (rv && rv != EWOULDBLOCK) {
613211201Stakawata			return 0;
614211201Stakawata		}
615211201Stakawata	}
616211201Stakawata
617211201Stakawata	return 0;
618211201Stakawata}
619211201Stakawata
620211201Stakawatau_int8_t
621211201Stakawatatpm_status(struct tpm_softc *sc)
622211201Stakawata{
623211201Stakawata	u_int8_t status;
624211201Stakawata
625211201Stakawata	status = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_STS) &
626211201Stakawata	    TPM_STS_MASK;
627211201Stakawata
628211201Stakawata	return status;
629211201Stakawata}
630211201Stakawata
631211201Stakawataint
632211201Stakawatatpm_tmotohz(int tmo)
633211201Stakawata{
634211201Stakawata	struct timeval tv;
635211201Stakawata
636211201Stakawata	tv.tv_sec = tmo / 1000;
637211201Stakawata	tv.tv_usec = 1000 * (tmo % 1000);
638211201Stakawata
639211201Stakawata	return tvtohz(&tv);
640211201Stakawata}
641211201Stakawata
642211201Stakawata/* Save TPM state on suspend. */
643211201Stakawataint
644211201Stakawata#ifdef __FreeBSD__
645211201Stakawatatpm_suspend(device_t dev)
646211201Stakawata#else
647211201Stakawatatpm_suspend(struct tpm_softc *sc, int why)
648211201Stakawata#endif
649211201Stakawata{
650211201Stakawata#ifdef __FreeBSD__
651211201Stakawata	struct tpm_softc *sc = device_get_softc(dev);
652211201Stakawata	int why = 1;
653211201Stakawata#endif
654211201Stakawata	u_int8_t command[] = {
655211201Stakawata	    0, 193,		/* TPM_TAG_RQU_COMMAND */
656211201Stakawata	    0, 0, 0, 10,	/* Length in bytes */
657211201Stakawata	    0, 0, 0, 156	/* TPM_ORD_SaveStates */
658211201Stakawata	};
659211201Stakawata
660211201Stakawata	/*
661211201Stakawata	 * Power down:  We have to issue the SaveStates command.
662211201Stakawata	 */
663211201Stakawata	sc->sc_write(sc, &command, sizeof(command));
664211201Stakawata	sc->sc_read(sc, &command, sizeof(command), NULL, TPM_HDRSIZE);
665211201Stakawata#ifdef TPM_DEBUG
666211201Stakawata	printf("tpm_suspend: power down: %d -> %d\n", sc->sc_suspend, why);
667211201Stakawata#endif
668211201Stakawata	sc->sc_suspend = why;
669211201Stakawata
670211201Stakawata	return 0;
671211201Stakawata}
672211201Stakawata
673211201Stakawata/*
674211201Stakawata * Handle resume event.  Actually nothing to do as the BIOS is supposed
675211201Stakawata * to restore the previously saved state.
676211201Stakawata */
677211201Stakawataint
678211201Stakawata#ifdef __FreeBSD__
679211201Stakawatatpm_resume(device_t dev)
680211201Stakawata#else
681211201Stakawatatpm_resume(struct tpm_softc *sc, int why)
682211201Stakawata#endif
683211201Stakawata{
684211201Stakawata#ifdef __FreeBSD__
685211201Stakawata	struct tpm_softc *sc = device_get_softc(dev);
686211201Stakawata	int why = 0;
687211201Stakawata#endif
688211201Stakawata#ifdef TPM_DEBUG
689211201Stakawata	printf("tpm_resume: resume: %d -> %d\n", sc->sc_suspend, why);
690211201Stakawata#endif
691211201Stakawata	sc->sc_suspend = why;
692211201Stakawata
693211201Stakawata	return 0;
694211201Stakawata}
695211201Stakawata
696211201Stakawata/* Dispatch suspend and resume events. */
697211201Stakawata#ifndef __FreeBSD__
698211201Stakawatavoid
699211201Stakawatatpm_powerhook(int why, void *self)
700211201Stakawata{
701211201Stakawata	struct tpm_softc *sc = (struct tpm_softc *)self;
702211201Stakawata
703211201Stakawata	if (why != PWR_RESUME)
704211201Stakawata		tpm_suspend(sc, why);
705211201Stakawata	else
706211201Stakawata		tpm_resume(sc, why);
707211201Stakawata}
708211201Stakawata#endif	/* !__FreeBSD__ */
709211201Stakawata
710211201Stakawata/* Wait for given status bits using polling. */
711211201Stakawataint
712211201Stakawatatpm_waitfor_poll(struct tpm_softc *sc, u_int8_t mask, int tmo, void *c)
713211201Stakawata{
714211201Stakawata	int rv;
715211201Stakawata
716211201Stakawata	/*
717211201Stakawata	 * Poll until either the requested condition or a time out is
718211201Stakawata	 * met.
719211201Stakawata	 */
720211201Stakawata	while (((sc->sc_stat = tpm_status(sc)) & mask) != mask && tmo--) {
721211201Stakawata		rv = tsleep(c, PRIBIO | PCATCH, "tpm_poll", 1);
722211201Stakawata		if (rv && rv != EWOULDBLOCK) {
723211201Stakawata#ifdef TPM_DEBUG
724211201Stakawata			printf("tpm_waitfor_poll: interrupted %d\n", rv);
725211201Stakawata#endif
726211201Stakawata			return rv;
727211201Stakawata		}
728211201Stakawata	}
729211201Stakawata
730211201Stakawata	return 0;
731211201Stakawata}
732211201Stakawata
733211201Stakawata/* Wait for given status bits using interrupts. */
734211201Stakawataint
735211201Stakawatatpm_waitfor_int(struct tpm_softc *sc, u_int8_t mask, int tmo, void *c,
736211201Stakawata    int inttype)
737211201Stakawata{
738211201Stakawata	int rv, to;
739211201Stakawata
740211201Stakawata	/* Poll and return when condition is already met. */
741211201Stakawata	sc->sc_stat = tpm_status(sc);
742211201Stakawata	if ((sc->sc_stat & mask) == mask)
743211201Stakawata		return 0;
744211201Stakawata
745211201Stakawata	/*
746211201Stakawata	 * Enable interrupt on tpm chip.  Note that interrupts on our
747211201Stakawata	 * level (SPL_TTY) are disabled (see tpm{read,write} et al) and
748211201Stakawata	 * will not be delivered to the cpu until we call tsleep(9) below.
749211201Stakawata	 */
750211201Stakawata	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE,
751211201Stakawata	    bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE) |
752211201Stakawata	    inttype);
753211201Stakawata	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE,
754211201Stakawata	    bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE) |
755211201Stakawata	    TPM_GLOBAL_INT_ENABLE);
756211201Stakawata
757211201Stakawata	/*
758211201Stakawata	 * Poll once more to remedy the race between previous polling
759211201Stakawata	 * and enabling interrupts on the tpm chip.
760211201Stakawata	 */
761211201Stakawata	sc->sc_stat = tpm_status(sc);
762211201Stakawata	if ((sc->sc_stat & mask) == mask) {
763211201Stakawata		rv = 0;
764211201Stakawata		goto out;
765211201Stakawata	}
766211201Stakawata
767211201Stakawata	to = tpm_tmotohz(tmo);
768211201Stakawata#ifdef TPM_DEBUG
769211201Stakawata	printf("tpm_waitfor_int: sleeping for %d ticks on %p\n", to, c);
770211201Stakawata#endif
771211201Stakawata	/*
772211201Stakawata	 * tsleep(9) enables interrupts on the cpu and returns after
773211201Stakawata	 * wake up with interrupts disabled again.  Note that interrupts
774211201Stakawata	 * generated by the tpm chip while being at SPL_TTY are not lost
775211201Stakawata	 * but held and delivered as soon as the cpu goes below SPL_TTY.
776211201Stakawata	 */
777211201Stakawata	rv = tsleep(c, PRIBIO | PCATCH, "tpm_intr", to);
778211201Stakawata
779211201Stakawata	sc->sc_stat = tpm_status(sc);
780211201Stakawata#ifdef TPM_DEBUG
781211201Stakawata	printf("tpm_waitfor_int: woke up with rv %d stat %b\n", rv,
782211201Stakawata	    sc->sc_stat, TPM_STS_BITS);
783211201Stakawata#endif
784211201Stakawata	if ((sc->sc_stat & mask) == mask)
785211201Stakawata		rv = 0;
786211201Stakawata
787211201Stakawata	/* Disable interrupts on tpm chip again. */
788211201Stakawataout:	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE,
789211201Stakawata	    bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE) &
790211201Stakawata	    ~TPM_GLOBAL_INT_ENABLE);
791211201Stakawata	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE,
792211201Stakawata	    bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE) &
793211201Stakawata	    ~inttype);
794211201Stakawata
795211201Stakawata	return rv;
796211201Stakawata}
797211201Stakawata
798211201Stakawata/*
799211201Stakawata * Wait on given status bits, uses interrupts where possible, otherwise polls.
800211201Stakawata */
801211201Stakawataint
802211201Stakawatatpm_waitfor(struct tpm_softc *sc, u_int8_t b0, int tmo, void *c)
803211201Stakawata{
804211201Stakawata	u_int8_t b;
805211201Stakawata	int re, to, rv;
806211201Stakawata
807211201Stakawata#ifdef TPM_DEBUG
808211201Stakawata	printf("tpm_waitfor: b0 %b\n", b0, TPM_STS_BITS);
809211201Stakawata#endif
810211201Stakawata
811211201Stakawata	/*
812211201Stakawata	 * If possible, use interrupts, otherwise poll.
813211201Stakawata	 *
814211201Stakawata	 * We use interrupts for TPM_STS_VALID and TPM_STS_DATA_AVAIL (if
815211201Stakawata	 * the tpm chips supports them) as waiting for those can take
816211201Stakawata	 * really long.  The other TPM_STS* are not needed very often
817211201Stakawata	 * so we do not support them.
818211201Stakawata	 */
819211201Stakawata	if (sc->sc_vector != IRQUNK) {
820211201Stakawata		b = b0;
821211201Stakawata
822211201Stakawata		/*
823211201Stakawata		 * Wait for data ready.  This interrupt only occures
824211201Stakawata		 * when both TPM_STS_VALID and TPM_STS_DATA_AVAIL are asserted.
825211201Stakawata		 * Thus we don't have to bother with TPM_STS_VALID
826211201Stakawata		 * separately and can just return.
827211201Stakawata		 *
828211201Stakawata		 * This only holds for interrupts!  When using polling
829211201Stakawata		 * both flags have to be waited for, see below.
830211201Stakawata		 */
831211201Stakawata		if ((b & TPM_STS_DATA_AVAIL) && (sc->sc_capabilities &
832211201Stakawata		    TPM_INTF_DATA_AVAIL_INT))
833211201Stakawata			return tpm_waitfor_int(sc, b, tmo, c,
834211201Stakawata			    TPM_DATA_AVAIL_INT);
835211201Stakawata
836211201Stakawata		/* Wait for status valid bit. */
837211201Stakawata		if ((b & TPM_STS_VALID) && (sc->sc_capabilities &
838211201Stakawata		    TPM_INTF_STS_VALID_INT)) {
839211201Stakawata			rv = tpm_waitfor_int(sc, b, tmo, c, TPM_STS_VALID_INT);
840211201Stakawata			if (rv != 0)
841211201Stakawata				return rv;
842211201Stakawata			else
843211201Stakawata				b = b0 & ~TPM_STS_VALID;
844211201Stakawata		}
845211201Stakawata
846211201Stakawata		/*
847211201Stakawata		 * When all flags are taken care of, return.  Otherwise
848211201Stakawata		 * use polling for eg. TPM_STS_CMD_READY.
849211201Stakawata		 */
850211201Stakawata		if (b == 0)
851211201Stakawata			return 0;
852211201Stakawata	}
853211201Stakawata
854211201Stakawata	re = 3;
855211201Stakawatarestart:
856211201Stakawata	/*
857211201Stakawata	 * If requested wait for TPM_STS_VALID before dealing with
858211201Stakawata	 * any other flag.  Eg. when both TPM_STS_DATA_AVAIL and TPM_STS_VALID
859211201Stakawata	 * are requested, wait for the latter first.
860211201Stakawata	 */
861211201Stakawata	b = b0;
862211201Stakawata	if (b0 & TPM_STS_VALID)
863211201Stakawata		b = TPM_STS_VALID;
864211201Stakawata
865211201Stakawata	to = tpm_tmotohz(tmo);
866211201Stakawataagain:
867211201Stakawata	if ((rv = tpm_waitfor_poll(sc, b, to, c)) != 0)
868211201Stakawata		return rv;
869211201Stakawata
870211201Stakawata	if ((b & sc->sc_stat) == TPM_STS_VALID) {
871211201Stakawata		/* Now wait for other flags. */
872211201Stakawata		b = b0 & ~TPM_STS_VALID;
873211201Stakawata		to++;
874211201Stakawata		goto again;
875211201Stakawata	}
876211201Stakawata
877211201Stakawata	if ((sc->sc_stat & b) != b) {
878211201Stakawata#ifdef TPM_DEBUG
879211201Stakawata		printf("tpm_waitfor: timeout: stat=%b b=%b\n",
880211201Stakawata		    sc->sc_stat, TPM_STS_BITS, b, TPM_STS_BITS);
881211201Stakawata#endif
882211201Stakawata		if (re-- && (b0 & TPM_STS_VALID)) {
883211201Stakawata			bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_STS,
884211201Stakawata			    TPM_STS_RESP_RETRY);
885211201Stakawata			goto restart;
886211201Stakawata		}
887211201Stakawata		return EIO;
888211201Stakawata	}
889211201Stakawata
890211201Stakawata	return 0;
891211201Stakawata}
892211201Stakawata
893211201Stakawata/* Start transaction. */
894211201Stakawataint
895211201Stakawatatpm_tis12_start(struct tpm_softc *sc, int flag)
896211201Stakawata{
897211201Stakawata	int rv;
898211201Stakawata
899211201Stakawata	if (flag == UIO_READ) {
900211201Stakawata		rv = tpm_waitfor(sc, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
901211201Stakawata		    TPM_READ_TMO, sc->sc_read);
902211201Stakawata		return rv;
903211201Stakawata	}
904211201Stakawata
905211201Stakawata	/* Own our (0th) locality. */
906211201Stakawata	if ((rv = tpm_request_locality(sc, 0)) != 0)
907211201Stakawata		return rv;
908211201Stakawata
909211201Stakawata	sc->sc_stat = tpm_status(sc);
910211201Stakawata	if (sc->sc_stat & TPM_STS_CMD_READY) {
911211201Stakawata#ifdef TPM_DEBUG
912211201Stakawata		printf("tpm_tis12_start: UIO_WRITE status %b\n", sc->sc_stat,
913211201Stakawata		   TPM_STS_BITS);
914211201Stakawata#endif
915211201Stakawata		return 0;
916211201Stakawata	}
917211201Stakawata
918211201Stakawata#ifdef TPM_DEBUG
919211201Stakawata	printf("tpm_tis12_start: UIO_WRITE readying chip\n");
920211201Stakawata#endif
921211201Stakawata
922211201Stakawata	/* Abort previous and restart. */
923211201Stakawata	bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_STS, TPM_STS_CMD_READY);
924211201Stakawata	if ((rv = tpm_waitfor(sc, TPM_STS_CMD_READY, TPM_READY_TMO,
925211201Stakawata	    sc->sc_write))) {
926211201Stakawata#ifdef TPM_DEBUG
927211201Stakawata		printf("tpm_tis12_start: UIO_WRITE readying failed %d\n", rv);
928211201Stakawata#endif
929211201Stakawata		return rv;
930211201Stakawata	}
931211201Stakawata
932211201Stakawata#ifdef TPM_DEBUG
933211201Stakawata	printf("tpm_tis12_start: UIO_WRITE readying done\n");
934211201Stakawata#endif
935211201Stakawata
936211201Stakawata	return 0;
937211201Stakawata}
938211201Stakawata
939211201Stakawataint
940211201Stakawatatpm_tis12_read(struct tpm_softc *sc, void *buf, int len, size_t *count,
941211201Stakawata    int flags)
942211201Stakawata{
943211201Stakawata	u_int8_t *p = buf;
944211201Stakawata	size_t cnt;
945211201Stakawata	int rv, n, bcnt;
946211201Stakawata
947211201Stakawata#ifdef TPM_DEBUG
948211201Stakawata	printf("tpm_tis12_read: len %d\n", len);
949211201Stakawata#endif
950211201Stakawata	cnt = 0;
951211201Stakawata	while (len > 0) {
952211201Stakawata		if ((rv = tpm_waitfor(sc, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
953211201Stakawata		    TPM_READ_TMO, sc->sc_read)))
954211201Stakawata			return rv;
955211201Stakawata
956211201Stakawata		bcnt = tpm_getburst(sc);
957211201Stakawata		n = MIN(len, bcnt);
958211201Stakawata#ifdef TPM_DEBUG
959211201Stakawata		printf("tpm_tis12_read: fetching %d, burst is %d\n", n, bcnt);
960211201Stakawata#endif
961211201Stakawata		for (; n--; len--) {
962211201Stakawata			*p++ = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_DATA);
963211201Stakawata			cnt++;
964211201Stakawata		}
965211201Stakawata
966211201Stakawata		if ((flags & TPM_PARAM_SIZE) == 0 && cnt >= 6)
967211201Stakawata			break;
968211201Stakawata	}
969211201Stakawata#ifdef TPM_DEBUG
970211201Stakawata	printf("tpm_tis12_read: read %zd bytes, len %d\n", cnt, len);
971211201Stakawata#endif
972211201Stakawata
973211201Stakawata	if (count)
974211201Stakawata		*count = cnt;
975211201Stakawata
976211201Stakawata	return 0;
977211201Stakawata}
978211201Stakawata
979211201Stakawataint
980211201Stakawatatpm_tis12_write(struct tpm_softc *sc, void *buf, int len)
981211201Stakawata{
982211201Stakawata	u_int8_t *p = buf;
983211201Stakawata	size_t cnt;
984211201Stakawata	int rv, r;
985211201Stakawata
986211201Stakawata#ifdef TPM_DEBUG
987211201Stakawata	printf("tpm_tis12_write: sc %p buf %p len %d\n", sc, buf, len);
988211201Stakawata#endif
989211201Stakawata
990211201Stakawata	if ((rv = tpm_request_locality(sc, 0)) != 0)
991211201Stakawata		return rv;
992211201Stakawata
993211201Stakawata	cnt = 0;
994211201Stakawata	while (cnt < len - 1) {
995211201Stakawata		for (r = tpm_getburst(sc); r > 0 && cnt < len - 1; r--) {
996211201Stakawata			bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_DATA, *p++);
997211201Stakawata			cnt++;
998211201Stakawata		}
999211201Stakawata		if ((rv = tpm_waitfor(sc, TPM_STS_VALID, TPM_READ_TMO, sc))) {
1000211201Stakawata#ifdef TPM_DEBUG
1001211201Stakawata			printf("tpm_tis12_write: failed burst rv %d\n", rv);
1002211201Stakawata#endif
1003211201Stakawata			return rv;
1004211201Stakawata		}
1005211201Stakawata		sc->sc_stat = tpm_status(sc);
1006211201Stakawata		if (!(sc->sc_stat & TPM_STS_DATA_EXPECT)) {
1007211201Stakawata#ifdef TPM_DEBUG
1008211201Stakawata			printf("tpm_tis12_write: failed rv %d stat=%b\n", rv,
1009211201Stakawata			    sc->sc_stat, TPM_STS_BITS);
1010211201Stakawata#endif
1011211201Stakawata			return EIO;
1012211201Stakawata		}
1013211201Stakawata	}
1014211201Stakawata
1015211201Stakawata	bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_DATA, *p++);
1016211201Stakawata	cnt++;
1017211201Stakawata
1018211201Stakawata	if ((rv = tpm_waitfor(sc, TPM_STS_VALID, TPM_READ_TMO, sc))) {
1019211201Stakawata#ifdef TPM_DEBUG
1020211201Stakawata		printf("tpm_tis12_write: failed last byte rv %d\n", rv);
1021211201Stakawata#endif
1022211201Stakawata		return rv;
1023211201Stakawata	}
1024211201Stakawata	if ((sc->sc_stat & TPM_STS_DATA_EXPECT) != 0) {
1025211201Stakawata#ifdef TPM_DEBUG
1026211201Stakawata		printf("tpm_tis12_write: failed rv %d stat=%b\n", rv,
1027211201Stakawata		    sc->sc_stat, TPM_STS_BITS);
1028211201Stakawata#endif
1029211201Stakawata		return EIO;
1030211201Stakawata	}
1031211201Stakawata
1032211201Stakawata#ifdef TPM_DEBUG
1033211201Stakawata	printf("tpm_tis12_write: wrote %d byte\n", cnt);
1034211201Stakawata#endif
1035211201Stakawata
1036211201Stakawata	return 0;
1037211201Stakawata}
1038211201Stakawata
1039211201Stakawata/* Finish transaction. */
1040211201Stakawataint
1041211201Stakawatatpm_tis12_end(struct tpm_softc *sc, int flag, int err)
1042211201Stakawata{
1043211201Stakawata	int rv = 0;
1044211201Stakawata
1045211201Stakawata	if (flag == UIO_READ) {
1046211201Stakawata		if ((rv = tpm_waitfor(sc, TPM_STS_VALID, TPM_READ_TMO,
1047211201Stakawata		    sc->sc_read)))
1048211201Stakawata			return rv;
1049211201Stakawata
1050211201Stakawata		/* Still more data? */
1051211201Stakawata		sc->sc_stat = tpm_status(sc);
1052211201Stakawata		if (!err && ((sc->sc_stat & TPM_STS_DATA_AVAIL) == TPM_STS_DATA_AVAIL)) {
1053211201Stakawata#ifdef TPM_DEBUG
1054211201Stakawata			printf("tpm_tis12_end: read failed stat=%b\n",
1055211201Stakawata			    sc->sc_stat, TPM_STS_BITS);
1056211201Stakawata#endif
1057211201Stakawata			rv = EIO;
1058211201Stakawata		}
1059211201Stakawata
1060211201Stakawata		bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_STS,
1061211201Stakawata		    TPM_STS_CMD_READY);
1062211201Stakawata
1063211201Stakawata		/* Release our (0th) locality. */
1064211201Stakawata		bus_space_write_1(sc->sc_bt, sc->sc_bh,TPM_ACCESS,
1065211201Stakawata		    TPM_ACCESS_ACTIVE_LOCALITY);
1066211201Stakawata	} else {
1067211201Stakawata		/* Hungry for more? */
1068211201Stakawata		sc->sc_stat = tpm_status(sc);
1069211201Stakawata		if (!err && (sc->sc_stat & TPM_STS_DATA_EXPECT)) {
1070211201Stakawata#ifdef TPM_DEBUG
1071211201Stakawata			printf("tpm_tis12_end: write failed stat=%b\n",
1072211201Stakawata			    sc->sc_stat, TPM_STS_BITS);
1073211201Stakawata#endif
1074211201Stakawata			rv = EIO;
1075211201Stakawata		}
1076211201Stakawata
1077211201Stakawata		bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_STS,
1078211201Stakawata		    err ? TPM_STS_CMD_READY : TPM_STS_GO);
1079211201Stakawata	}
1080211201Stakawata
1081211201Stakawata	return rv;
1082211201Stakawata}
1083211201Stakawata
1084211201Stakawata#ifdef __FreeBSD__
1085211201Stakawatavoid
1086211201Stakawata#else
1087211201Stakawataint
1088211201Stakawata#endif
1089211201Stakawatatpm_intr(void *v)
1090211201Stakawata{
1091211201Stakawata	struct tpm_softc *sc = v;
1092211201Stakawata	u_int32_t r;
1093211201Stakawata#ifdef TPM_DEBUG
1094211201Stakawata	static int cnt = 0;
1095211201Stakawata#endif
1096211201Stakawata
1097211201Stakawata	r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INT_STATUS);
1098211201Stakawata#ifdef TPM_DEBUG
1099211201Stakawata	if (r != 0)
1100211201Stakawata		printf("tpm_intr: int=%b (%d)\n", r, TPM_INTERRUPT_ENABLE_BITS,
1101211201Stakawata		    cnt);
1102211201Stakawata	else
1103211201Stakawata		cnt++;
1104211201Stakawata#endif
1105211201Stakawata	if (!(r & (TPM_CMD_READY_INT | TPM_LOCALITY_CHANGE_INT |
1106211201Stakawata	    TPM_STS_VALID_INT | TPM_DATA_AVAIL_INT)))
1107211201Stakawata#ifdef __FreeBSD__
1108211201Stakawata		return;
1109211201Stakawata#else
1110211201Stakawata		return 0;
1111211201Stakawata#endif
1112211201Stakawata	if (r & TPM_STS_VALID_INT)
1113211201Stakawata		wakeup(sc);
1114211201Stakawata
1115211201Stakawata	if (r & TPM_CMD_READY_INT)
1116211201Stakawata		wakeup(sc->sc_write);
1117211201Stakawata
1118211201Stakawata	if (r & TPM_DATA_AVAIL_INT)
1119211201Stakawata		wakeup(sc->sc_read);
1120211201Stakawata
1121211201Stakawata	if (r & TPM_LOCALITY_CHANGE_INT)
1122211201Stakawata		wakeup(sc->sc_init);
1123211201Stakawata
1124211201Stakawata	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INT_STATUS, r);
1125211201Stakawata
1126211201Stakawata#ifdef __FreeBSD__
1127211201Stakawata	return;
1128211201Stakawata#else
1129211201Stakawata	return 1;
1130211201Stakawata#endif
1131211201Stakawata}
1132211201Stakawata
1133211201Stakawata/* Read single byte using legacy interface. */
1134211201Stakawatastatic inline u_int8_t
1135211201Stakawatatpm_legacy_in(bus_space_tag_t iot, bus_space_handle_t ioh, int reg)
1136211201Stakawata{
1137211201Stakawata	bus_space_write_1(iot, ioh, 0, reg);
1138211201Stakawata	return bus_space_read_1(iot, ioh, 1);
1139211201Stakawata}
1140211201Stakawata
1141211201Stakawata/* Write single byte using legacy interface. */
1142211201Stakawatastatic inline void
1143211201Stakawatatpm_legacy_out(bus_space_tag_t iot, bus_space_handle_t ioh, int reg, u_int8_t v)
1144211201Stakawata{
1145211201Stakawata	bus_space_write_1(iot, ioh, 0, reg);
1146211201Stakawata	bus_space_write_1(iot, ioh, 1, v);
1147211201Stakawata}
1148211201Stakawata
1149211201Stakawata/* Probe for TPM using legacy interface. */
1150211201Stakawataint
1151211201Stakawatatpm_legacy_probe(bus_space_tag_t iot, bus_addr_t iobase)
1152211201Stakawata{
1153211201Stakawata	bus_space_handle_t ioh;
1154211201Stakawata	u_int8_t r, v;
1155211201Stakawata	int i, rv = 0;
1156211201Stakawata	char id[8];
1157211201Stakawata
1158211201Stakawata	if (!tpm_enabled || iobase == -1)
1159211201Stakawata		return 0;
1160211201Stakawata
1161211201Stakawata	if (bus_space_map(iot, iobase, 2, 0, &ioh))
1162211201Stakawata		return 0;
1163211201Stakawata
1164211201Stakawata	v = bus_space_read_1(iot, ioh, 0);
1165211201Stakawata	if (v == 0xff) {
1166211201Stakawata		bus_space_unmap(iot, ioh, 2);
1167211201Stakawata		return 0;
1168211201Stakawata	}
1169211201Stakawata	r = bus_space_read_1(iot, ioh, 1);
1170211201Stakawata
1171211201Stakawata	for (i = sizeof(id); i--; )
1172211201Stakawata		id[i] = tpm_legacy_in(iot, ioh, TPM_ID + i);
1173211201Stakawata
1174211201Stakawata#ifdef TPM_DEBUG
1175211201Stakawata	printf("tpm_legacy_probe %.4s %d.%d.%d.%d\n",
1176211201Stakawata	    &id[4], id[0], id[1], id[2], id[3]);
1177211201Stakawata#endif
1178211201Stakawata	/*
1179211201Stakawata	 * The only chips using the legacy interface we are aware of are
1180211201Stakawata	 * by Atmel.  For other chips more signature would have to be added.
1181211201Stakawata	 */
1182211201Stakawata	if (!bcmp(&id[4], "ATML", 4))
1183211201Stakawata		rv = 1;
1184211201Stakawata
1185211201Stakawata	if (!rv) {
1186211201Stakawata		bus_space_write_1(iot, ioh, r, 1);
1187211201Stakawata		bus_space_write_1(iot, ioh, v, 0);
1188211201Stakawata	}
1189211201Stakawata	bus_space_unmap(iot, ioh, 2);
1190211201Stakawata
1191211201Stakawata	return rv;
1192211201Stakawata}
1193211201Stakawata
1194211201Stakawata/* Setup TPM using legacy interface. */
1195211201Stakawataint
1196211201Stakawatatpm_legacy_init(struct tpm_softc *sc, int irq, const char *name)
1197211201Stakawata{
1198211201Stakawata	char id[8];
1199211201Stakawata	u_int8_t ioh, iol;
1200211201Stakawata	int i;
1201211201Stakawata
1202211201Stakawata	if ((i = bus_space_map(sc->sc_batm, tpm_enabled, 2, 0, &sc->sc_bahm))) {
1203211201Stakawata		printf(": cannot map tpm registers (%d)\n", i);
1204211201Stakawata		tpm_enabled = 0;
1205211201Stakawata		return 1;
1206211201Stakawata	}
1207211201Stakawata
1208211201Stakawata	for (i = sizeof(id); i--; )
1209211201Stakawata		id[i] = tpm_legacy_in(sc->sc_bt, sc->sc_bh, TPM_ID + i);
1210211201Stakawata
1211211201Stakawata	printf(": %.4s %d.%d @0x%x\n", &id[4], id[0], id[1], tpm_enabled);
1212211201Stakawata	iol = tpm_enabled & 0xff;
1213211201Stakawata	ioh = tpm_enabled >> 16;
1214211201Stakawata	tpm_enabled = 0;
1215211201Stakawata
1216211201Stakawata	return 0;
1217211201Stakawata}
1218211201Stakawata
1219211201Stakawata/* Start transaction. */
1220211201Stakawataint
1221211201Stakawatatpm_legacy_start(struct tpm_softc *sc, int flag)
1222211201Stakawata{
1223211201Stakawata	struct timeval tv;
1224211201Stakawata	u_int8_t bits, r;
1225211201Stakawata	int to, rv;
1226211201Stakawata
1227211201Stakawata	bits = flag == UIO_READ ? TPM_LEGACY_DA : 0;
1228211201Stakawata	tv.tv_sec = TPM_LEGACY_TMO;
1229211201Stakawata	tv.tv_usec = 0;
1230211201Stakawata	to = tvtohz(&tv) / TPM_LEGACY_SLEEP;
1231211201Stakawata	while (((r = bus_space_read_1(sc->sc_batm, sc->sc_bahm, 1)) &
1232211201Stakawata	    (TPM_LEGACY_BUSY|bits)) != bits && to--) {
1233211201Stakawata		rv = tsleep(sc, PRIBIO | PCATCH, "legacy_tpm_start",
1234211201Stakawata		    TPM_LEGACY_SLEEP);
1235211201Stakawata		if (rv && rv != EWOULDBLOCK)
1236211201Stakawata			return rv;
1237211201Stakawata	}
1238211201Stakawata
1239211201Stakawata#if defined(TPM_DEBUG) && !defined(__FreeBSD__)
1240211201Stakawata	printf("%s: bits %b\n", sc->sc_dev.dv_xname, r, TPM_LEGACY_BITS);
1241211201Stakawata#endif
1242211201Stakawata	if ((r & (TPM_LEGACY_BUSY|bits)) != bits)
1243211201Stakawata		return EIO;
1244211201Stakawata
1245211201Stakawata	return 0;
1246211201Stakawata}
1247211201Stakawata
1248211201Stakawataint
1249211201Stakawatatpm_legacy_read(struct tpm_softc *sc, void *buf, int len, size_t *count,
1250211201Stakawata    int flags)
1251211201Stakawata{
1252211201Stakawata	u_int8_t *p;
1253211201Stakawata	size_t cnt;
1254211201Stakawata	int to, rv;
1255211201Stakawata
1256211201Stakawata	cnt = rv = 0;
1257211201Stakawata	for (p = buf; !rv && len > 0; len--) {
1258211201Stakawata		for (to = 1000;
1259211201Stakawata		    !(bus_space_read_1(sc->sc_batm, sc->sc_bahm, 1) &
1260211201Stakawata		    TPM_LEGACY_DA); DELAY(1))
1261211201Stakawata			if (!to--)
1262211201Stakawata				return EIO;
1263211201Stakawata
1264211201Stakawata		DELAY(TPM_LEGACY_DELAY);
1265211201Stakawata		*p++ = bus_space_read_1(sc->sc_batm, sc->sc_bahm, 0);
1266211201Stakawata		cnt++;
1267211201Stakawata	}
1268211201Stakawata
1269211201Stakawata	*count = cnt;
1270211201Stakawata	return 0;
1271211201Stakawata}
1272211201Stakawata
1273211201Stakawataint
1274211201Stakawatatpm_legacy_write(struct tpm_softc *sc, void *buf, int len)
1275211201Stakawata{
1276211201Stakawata	u_int8_t *p;
1277211201Stakawata	int n;
1278211201Stakawata
1279211201Stakawata	for (p = buf, n = len; n--; DELAY(TPM_LEGACY_DELAY)) {
1280211201Stakawata		if (!n && len != TPM_BUFSIZ) {
1281211201Stakawata			bus_space_write_1(sc->sc_batm, sc->sc_bahm, 1,
1282211201Stakawata			    TPM_LEGACY_LAST);
1283211201Stakawata			DELAY(TPM_LEGACY_DELAY);
1284211201Stakawata		}
1285211201Stakawata		bus_space_write_1(sc->sc_batm, sc->sc_bahm, 0, *p++);
1286211201Stakawata	}
1287211201Stakawata
1288211201Stakawata	return 0;
1289211201Stakawata}
1290211201Stakawata
1291211201Stakawata/* Finish transaction. */
1292211201Stakawataint
1293211201Stakawatatpm_legacy_end(struct tpm_softc *sc, int flag, int rv)
1294211201Stakawata{
1295211201Stakawata	struct timeval tv;
1296211201Stakawata	u_int8_t r;
1297211201Stakawata	int to;
1298211201Stakawata
1299211201Stakawata	if (rv || flag == UIO_READ)
1300211201Stakawata		bus_space_write_1(sc->sc_batm, sc->sc_bahm, 1, TPM_LEGACY_ABRT);
1301211201Stakawata	else {
1302211201Stakawata		tv.tv_sec = TPM_LEGACY_TMO;
1303211201Stakawata		tv.tv_usec = 0;
1304211201Stakawata		to = tvtohz(&tv) / TPM_LEGACY_SLEEP;
1305211201Stakawata		while(((r = bus_space_read_1(sc->sc_batm, sc->sc_bahm, 1)) &
1306211201Stakawata		    TPM_LEGACY_BUSY) && to--) {
1307211201Stakawata			rv = tsleep(sc, PRIBIO | PCATCH, "legacy_tpm_end",
1308211201Stakawata			    TPM_LEGACY_SLEEP);
1309211201Stakawata			if (rv && rv != EWOULDBLOCK)
1310211201Stakawata				return rv;
1311211201Stakawata		}
1312211201Stakawata
1313211201Stakawata#if defined(TPM_DEBUG) && !defined(__FreeBSD__)
1314211201Stakawata		printf("%s: bits %b\n", sc->sc_dev.dv_xname, r, TPM_LEGACY_BITS);
1315211201Stakawata#endif
1316211201Stakawata		if (r & TPM_LEGACY_BUSY)
1317211201Stakawata			return EIO;
1318211201Stakawata
1319211201Stakawata		if (r & TPM_LEGACY_RE)
1320211201Stakawata			return EIO;	/* XXX Retry the loop? */
1321211201Stakawata	}
1322211201Stakawata
1323211201Stakawata	return rv;
1324211201Stakawata}
1325211201Stakawata
1326211201Stakawataint
1327211201Stakawata#ifdef __FreeBSD__
1328211201Stakawatatpmopen(struct cdev *dev, int flag, int mode, struct thread *td)
1329211201Stakawata#else
1330211201Stakawatatpmopen(dev_t dev, int flag, int mode, struct proc *p)
1331211201Stakawata#endif
1332211201Stakawata{
1333211201Stakawata	struct tpm_softc *sc = TPMSOFTC(dev);
1334211201Stakawata
1335211201Stakawata	if (!sc)
1336211201Stakawata		return ENXIO;
1337211201Stakawata
1338211201Stakawata	if (sc->sc_flags & TPM_OPEN)
1339211201Stakawata		return EBUSY;
1340211201Stakawata
1341211201Stakawata	sc->sc_flags |= TPM_OPEN;
1342211201Stakawata
1343211201Stakawata	return 0;
1344211201Stakawata}
1345211201Stakawata
1346211201Stakawataint
1347211201Stakawata#ifdef __FreeBSD__
1348211201Stakawatatpmclose(struct cdev *dev, int flag, int mode, struct thread *td)
1349211201Stakawata#else
1350211201Stakawatatpmclose(dev_t dev, int flag, int mode, struct proc *p)
1351211201Stakawata#endif
1352211201Stakawata{
1353211201Stakawata	struct tpm_softc *sc = TPMSOFTC(dev);
1354211201Stakawata
1355211201Stakawata	if (!sc)
1356211201Stakawata		return ENXIO;
1357211201Stakawata
1358211201Stakawata	if (!(sc->sc_flags & TPM_OPEN))
1359211201Stakawata		return EINVAL;
1360211201Stakawata
1361211201Stakawata	sc->sc_flags &= ~TPM_OPEN;
1362211201Stakawata
1363211201Stakawata	return 0;
1364211201Stakawata}
1365211201Stakawata
1366211201Stakawataint
1367211201Stakawata#ifdef __FreeBSD__
1368211201Stakawatatpmread(struct cdev *dev, struct uio *uio, int flags)
1369211201Stakawata#else
1370211201Stakawatatpmread(dev_t dev, struct uio *uio, int flags)
1371211201Stakawata#endif
1372211201Stakawata{
1373211201Stakawata	struct tpm_softc *sc = TPMSOFTC(dev);
1374211201Stakawata	u_int8_t buf[TPM_BUFSIZ], *p;
1375211201Stakawata	size_t cnt;
1376211201Stakawata	int n, len, rv, s;
1377211201Stakawata
1378211201Stakawata	if (!sc)
1379211201Stakawata		return ENXIO;
1380211201Stakawata
1381211201Stakawata	s = spltty();
1382211201Stakawata	if ((rv = (sc->sc_start)(sc, UIO_READ))) {
1383211201Stakawata		splx(s);
1384211201Stakawata		return rv;
1385211201Stakawata	}
1386211201Stakawata
1387211201Stakawata#ifdef TPM_DEBUG
1388211201Stakawata	printf("tpmread: getting header\n");
1389211201Stakawata#endif
1390211201Stakawata	if ((rv = (sc->sc_read)(sc, buf, TPM_HDRSIZE, &cnt, 0))) {
1391211201Stakawata		(sc->sc_end)(sc, UIO_READ, rv);
1392211201Stakawata		splx(s);
1393211201Stakawata		return rv;
1394211201Stakawata	}
1395211201Stakawata
1396211201Stakawata	len = (buf[2] << 24) | (buf[3] << 16) | (buf[4] << 8) | buf[5];
1397211201Stakawata#ifdef TPM_DEBUG
1398211201Stakawata	printf("tpmread: len %d, io count %d\n", len, uio->uio_resid);
1399211201Stakawata#endif
1400211201Stakawata	if (len > uio->uio_resid) {
1401211201Stakawata		rv = EIO;
1402211201Stakawata		(sc->sc_end)(sc, UIO_READ, rv);
1403211201Stakawata#ifdef TPM_DEBUG
1404211201Stakawata		printf("tpmread: bad residual io count 0x%x\n", uio->uio_resid);
1405211201Stakawata#endif
1406211201Stakawata		splx(s);
1407211201Stakawata		return rv;
1408211201Stakawata	}
1409211201Stakawata
1410211201Stakawata	/* Copy out header. */
1411211201Stakawata	if ((rv = uiomove((caddr_t)buf, cnt, uio))) {
1412211201Stakawata		(sc->sc_end)(sc, UIO_READ, rv);
1413211201Stakawata		splx(s);
1414211201Stakawata		return rv;
1415211201Stakawata	}
1416211201Stakawata
1417211201Stakawata	/* Get remaining part of the answer (if anything is left). */
1418211201Stakawata	for (len -= cnt, p = buf, n = sizeof(buf); len > 0; p = buf, len -= n,
1419211201Stakawata	    n = sizeof(buf)) {
1420211201Stakawata		n = MIN(n, len);
1421211201Stakawata#ifdef TPM_DEBUG
1422211201Stakawata		printf("tpmread: n %d len %d\n", n, len);
1423211201Stakawata#endif
1424211201Stakawata		if ((rv = (sc->sc_read)(sc, p, n, NULL, TPM_PARAM_SIZE))) {
1425211201Stakawata			(sc->sc_end)(sc, UIO_READ, rv);
1426211201Stakawata			splx(s);
1427211201Stakawata			return rv;
1428211201Stakawata		}
1429211201Stakawata		p += n;
1430211201Stakawata		if ((rv = uiomove((caddr_t)buf, p - buf, uio))) {
1431211201Stakawata			(sc->sc_end)(sc, UIO_READ, rv);
1432211201Stakawata			splx(s);
1433211201Stakawata			return rv;
1434211201Stakawata		}
1435211201Stakawata	}
1436211201Stakawata
1437211201Stakawata	rv = (sc->sc_end)(sc, UIO_READ, rv);
1438211201Stakawata	splx(s);
1439211201Stakawata	return rv;
1440211201Stakawata}
1441211201Stakawata
1442211201Stakawataint
1443211201Stakawata#ifdef __FreeBSD__
1444211201Stakawatatpmwrite(struct cdev *dev, struct uio *uio, int flags)
1445211201Stakawata#else
1446211201Stakawatatpmwrite(dev_t dev, struct uio *uio, int flags)
1447211201Stakawata#endif
1448211201Stakawata{
1449211201Stakawata	struct tpm_softc *sc = TPMSOFTC(dev);
1450211201Stakawata	u_int8_t buf[TPM_BUFSIZ];
1451211201Stakawata	int n, rv, s;
1452211201Stakawata
1453211201Stakawata	if (!sc)
1454211201Stakawata		return ENXIO;
1455211201Stakawata
1456211201Stakawata	s = spltty();
1457211201Stakawata
1458211201Stakawata#ifdef TPM_DEBUG
1459211201Stakawata	printf("tpmwrite: io count %d\n", uio->uio_resid);
1460211201Stakawata#endif
1461211201Stakawata
1462211201Stakawata	n = MIN(sizeof(buf), uio->uio_resid);
1463211201Stakawata	if ((rv = uiomove((caddr_t)buf, n, uio))) {
1464211201Stakawata		splx(s);
1465211201Stakawata		return rv;
1466211201Stakawata	}
1467211201Stakawata
1468211201Stakawata	if ((rv = (sc->sc_start)(sc, UIO_WRITE))) {
1469211201Stakawata		splx(s);
1470211201Stakawata		return rv;
1471211201Stakawata	}
1472211201Stakawata
1473211201Stakawata	if ((rv = (sc->sc_write(sc, buf, n)))) {
1474211201Stakawata		splx(s);
1475211201Stakawata		return rv;
1476211201Stakawata	}
1477211201Stakawata
1478211201Stakawata	rv = (sc->sc_end)(sc, UIO_WRITE, rv);
1479211201Stakawata	splx(s);
1480211201Stakawata	return rv;
1481211201Stakawata}
1482211201Stakawata
1483211201Stakawataint
1484211201Stakawata#ifdef __FreeBSD__
1485211201Stakawatatpmioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags,
1486211201Stakawata    struct thread *td)
1487211201Stakawata#else
1488211201Stakawatatpmioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
1489211201Stakawata#endif
1490211201Stakawata{
1491211201Stakawata	return ENOTTY;
1492211201Stakawata}
1493