Module Name:    src
Committed By:   maxv
Date:           Tue Oct  8 18:43:03 UTC 2019

Modified Files:
        src/share/man/man4: tpm.4
        src/sys/dev/acpi: tpm_acpi.c
        src/sys/dev/ic: tpm.c tpmreg.h tpmvar.h
        src/sys/dev/isa: tpm_isa.c

Log Message:
Improvements in tpm(4):
 - Remove interrupt support, do polling only, avoids unnecessary trouble.
 - Simplify a few things.
 - Fix the suspend function, the SaveState command is 0x98, not 0x9C.
 - Make the driver MP-safe.
 - Sync the man page with reality.


To generate a diff of this commit:
cvs rdiff -u -r1.4 -r1.5 src/share/man/man4/tpm.4
cvs rdiff -u -r1.8 -r1.9 src/sys/dev/acpi/tpm_acpi.c
cvs rdiff -u -r1.13 -r1.14 src/sys/dev/ic/tpm.c
cvs rdiff -u -r1.4 -r1.5 src/sys/dev/ic/tpmreg.h src/sys/dev/ic/tpmvar.h
cvs rdiff -u -r1.4 -r1.5 src/sys/dev/isa/tpm_isa.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/share/man/man4/tpm.4
diff -u src/share/man/man4/tpm.4:1.4 src/share/man/man4/tpm.4:1.5
--- src/share/man/man4/tpm.4:1.4	Thu Feb 22 01:40:49 2018
+++ src/share/man/man4/tpm.4	Tue Oct  8 18:43:03 2019
@@ -1,20 +1,33 @@
-.\"	$NetBSD: tpm.4,v 1.4 2018/02/22 01:40:49 pgoyette Exp $
+.\"	$NetBSD: tpm.4,v 1.5 2019/10/08 18:43:03 maxv Exp $
 .\"
-.\" Copyright (c) 2010 Hans-Jörg Höxer, <hans-joerg.hoe...@genua.de>
+.\" Copyright (c) 2019 The NetBSD Foundation, Inc.
+.\" All rights reserved.
 .\"
-.\" Permission to use, copy, modify, and distribute this software for any
-.\" purpose with or without fee is hereby granted, provided that the above
-.\" copyright notice and this permission notice appear in all copies.
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Maxime Villard.
 .\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
 .\"
-.Dd February 22, 2018
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd October 6, 2019
 .Dt TPM 4
 .Os
 .Sh NAME
@@ -22,40 +35,22 @@
 .Nd Trusted Platform Module
 .Sh SYNOPSIS
 .Cd "tpm* at isa? iomem 0xfed40000"
-.Cd "tpm* at isa? iomem 0xfed40000 irq 7"
 .Cd "tpm* at acpi?"
 .Sh DESCRIPTION
 The
 .Nm
-driver provides support for various trusted platform modules (TPM) that can
-store cryptographic keys.
+driver provides support for various Trusted Platform Module (TPM) chips.
 .Pp
 Supported modules:
 .Pp
 .Bl -bullet -compact -offset indent
 .It
-Atmel 97SC3203
-.It
-Broadcom BCM0102
-.It
-Infineon IFX SLD 9630 TT 1.1 and IFX SLB 9635 TT 1.2
-.It
-Intel INTC0102
-.It
-Sinosun SNS SSX35
-.It
-STM ST19WP18
+TPM 2.0 chips over ACPI
 .It
-Winbond WEC WPCT200
+TPM 1.2 chips over ISA
 .El
 .Pp
-The driver can be configured to use an IRQ by providing a free ISA
-interrupt vector using the keyword
-.Em irq
-in the kernel configuration file or using
-.Xr config 1 .
-If not IRQ is specified, the driver uses polling.
-This is the default configuration.
+Note that the supported interface version is TIS1.2 in each case.
 .Sh SEE ALSO
 .Xr config 1 ,
 .Xr intro 4
@@ -64,6 +59,7 @@ This is the default configuration.
 The
 .Nm
 driver was written by
+.An Maxime Villard ,
 .An Michael Shalayeff
 and
 .An Hans-Joerg Hoexer .

Index: src/sys/dev/acpi/tpm_acpi.c
diff -u src/sys/dev/acpi/tpm_acpi.c:1.8 src/sys/dev/acpi/tpm_acpi.c:1.9
--- src/sys/dev/acpi/tpm_acpi.c:1.8	Sat Jun 22 12:57:40 2019
+++ src/sys/dev/acpi/tpm_acpi.c	Tue Oct  8 18:43:02 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: tpm_acpi.c,v 1.8 2019/06/22 12:57:40 maxv Exp $ */
+/* $NetBSD: tpm_acpi.c,v 1.9 2019/10/08 18:43:02 maxv Exp $ */
 
 /*
  * Copyright (c) 2012, 2019 The NetBSD Foundation, Inc.
@@ -30,10 +30,9 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tpm_acpi.c,v 1.8 2019/06/22 12:57:40 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tpm_acpi.c,v 1.9 2019/10/08 18:43:02 maxv Exp $");
 
 #include <sys/param.h>
-#include <sys/device.h>
 #include <sys/systm.h>
 #include <sys/device.h>
 #include <sys/bus.h>
@@ -45,8 +44,6 @@ __KERNEL_RCSID(0, "$NetBSD: tpm_acpi.c,v
 #include <dev/acpi/acpireg.h>
 #include <dev/acpi/acpivar.h>
 
-#include <dev/isa/isavar.h>
-
 #include "ioconf.h"
 
 #define _COMPONENT	ACPI_RESOURCE_COMPONENT
@@ -100,41 +97,41 @@ tpm_acpi_attach(device_t parent, device_
 	struct acpi_attach_args *aa = aux;
 	struct acpi_resources res;
 	struct acpi_mem *mem;
-	struct acpi_irq *irq;
 	bus_addr_t base;
 	bus_addr_t size;
-	int rv, inum;
-
-	sc->sc_dev = self;
-	sc->sc_ver = TPM_2_0;
+	int rv;
 
 	rv = acpi_resource_parse(self, aa->aa_node->ad_handle, "_CRS", &res,
 	    &acpi_resource_parse_ops_default);
 	if (ACPI_FAILURE(rv)) {
-		aprint_error_dev(sc->sc_dev, "cannot parse resources %d\n", rv);
+		aprint_error_dev(self, "cannot parse resources %d\n", rv);
 		return;
 	}
 
 	mem = acpi_res_mem(&res, 0);
 	if (mem == NULL) {
-		aprint_error_dev(sc->sc_dev, "cannot find mem\n");
+		aprint_error_dev(self, "cannot find mem\n");
 		goto out;
 	}
 	if (mem->ar_length != TPM_SPACE_SIZE) {
-		aprint_error_dev(sc->sc_dev,
-		    "wrong size mem %"PRIu64" != %u\n",
+		aprint_error_dev(self, "wrong size mem %"PRIu64" != %u\n",
 		    (uint64_t)mem->ar_length, TPM_SPACE_SIZE);
 		goto out;
 	}
 
-	base = mem->ar_base;
-	size = mem->ar_length;
-	sc->sc_bt = aa->aa_memt;
+	sc->sc_dev = self;
+	sc->sc_ver = TPM_2_0;
+	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
+	sc->sc_busy = false;
 	sc->sc_init = tpm_tis12_init;
 	sc->sc_start = tpm_tis12_start;
 	sc->sc_read = tpm_tis12_read;
 	sc->sc_write = tpm_tis12_write;
 	sc->sc_end = tpm_tis12_end;
+	sc->sc_bt = aa->aa_memt;
+
+	base = mem->ar_base;
+	size = mem->ar_length;
 
 	if (bus_space_map(sc->sc_bt, base, size, 0, &sc->sc_bh)) {
 		aprint_error_dev(sc->sc_dev, "cannot map registers\n");
@@ -146,24 +143,11 @@ tpm_acpi_attach(device_t parent, device_
 		goto out1;
 	}
 
-	irq = acpi_res_irq(&res, 0);
-	if (irq == NULL)
-		inum = -1;
-	else
-		inum = irq->ar_irq;
-
-	if ((rv = (*sc->sc_init)(sc, inum)) != 0) {
+	if ((*sc->sc_init)(sc) != 0) {
 		aprint_error_dev(sc->sc_dev, "cannot init device %d\n", rv);
 		goto out1;
 	}
 
-	if (inum != -1 &&
-	    (sc->sc_ih = isa_intr_establish(aa->aa_ic, irq->ar_irq,
-	    IST_EDGE, IPL_TTY, tpm_intr, sc)) == NULL) {
-		aprint_error_dev(sc->sc_dev, "cannot establish interrupt\n");
-		goto out1;
-	}
-
 	acpi_resource_cleanup(&res);
 	return;
 

Index: src/sys/dev/ic/tpm.c
diff -u src/sys/dev/ic/tpm.c:1.13 src/sys/dev/ic/tpm.c:1.14
--- src/sys/dev/ic/tpm.c:1.13	Sat Jun 22 12:57:41 2019
+++ src/sys/dev/ic/tpm.c	Tue Oct  8 18:43:02 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: tpm.c,v 1.13 2019/06/22 12:57:41 maxv Exp $	*/
+/*	$NetBSD: tpm.c,v 1.14 2019/10/08 18:43:02 maxv Exp $	*/
 
 /*
  * Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -48,7 +48,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tpm.c,v 1.13 2019/06/22 12:57:41 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tpm.c,v 1.14 2019/10/08 18:43:02 maxv Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -67,6 +67,7 @@ __KERNEL_RCSID(0, "$NetBSD: tpm.c,v 1.13
 
 #define TPM_BUFSIZ	1024
 #define TPM_HDRSIZE	10
+
 #define TPM_PARAM_SIZE	0x0001	/* that's a flag */
 
 /* Timeouts. */
@@ -79,23 +80,6 @@ __KERNEL_RCSID(0, "$NetBSD: tpm.c,v 1.13
 	(TPM_INTF_DATA_AVAIL_INT|TPM_INTF_LOCALITY_CHANGE_INT| \
 	 TPM_INTF_INT_LEVEL_LOW)
 
-static const struct {
-	uint32_t devid;
-	const char *name;
-	int flags;
-#define TPM_DEV_NOINTS	0x0001
-} tpm_devs[] = {
-	{ 0x000615d1, "IFX SLD 9630 TT 1.1", 0 },
-	{ 0x000b15d1, "IFX SLB 9635 TT 1.2", 0 },
-	{ 0x100214e4, "Broadcom BCM0102", TPM_DEV_NOINTS },
-	{ 0x00fe1050, "WEC WPCT200", 0 },
-	{ 0x687119fa, "SNS SSX35", 0 },
-	{ 0x2e4d5453, "STM ST19WP18", 0 },
-	{ 0x32021114, "ATML 97SC3203", TPM_DEV_NOINTS },
-	{ 0x10408086, "INTEL INTC0102", 0 },
-	{ 0, "", TPM_DEV_NOINTS },
-};
-
 static inline int
 tpm_tmotohz(int tmo)
 {
@@ -129,7 +113,7 @@ tpm_request_locality(struct tpm_softc *s
 	while ((r = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS) &
 	    (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) !=
 	    (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY) && to--) {
-		rv = tsleep(sc->sc_init, PRIBIO | PCATCH, "tpm_locality", 1);
+		rv = tsleep(sc->sc_init, PCATCH, "tpm_locality", 1);
 		if (rv && rv != EWOULDBLOCK) {
 			return rv;
 		}
@@ -161,7 +145,7 @@ tpm_getburst(struct tpm_softc *sc)
 		if (burst)
 			return burst;
 
-		rv = tsleep(sc, PRIBIO | PCATCH, "tpm_getburst", 1);
+		rv = tsleep(sc, PCATCH, "tpm_getburst", 1);
 		if (rv && rv != EWOULDBLOCK) {
 			return 0;
 		}
@@ -189,9 +173,9 @@ tpm12_suspend(device_t dev, const pmf_qu
 {
 	struct tpm_softc *sc = device_private(dev);
 	static const uint8_t command[] = {
-		0, 193,		/* TPM_TAG_RQU_COMMAND */
+		0, 0xC1,	/* TPM_TAG_RQU_COMMAND */
 		0, 0, 0, 10,	/* Length in bytes */
-		0, 0, 0, 156	/* TPM_ORD_SaveStates */
+		0, 0, 0, 0x98	/* TPM_ORD_SaveState */
 	};
 	uint8_t scratch[sizeof(command)];
 
@@ -209,16 +193,13 @@ tpm12_resume(device_t dev, const pmf_qua
 
 /* -------------------------------------------------------------------------- */
 
-/*
- * Wait for given status bits using polling.
- */
 static int
-tpm_waitfor_poll(struct tpm_softc *sc, uint8_t mask, int to, wchan_t chan)
+tpm_poll(struct tpm_softc *sc, uint8_t mask, int to, wchan_t chan)
 {
 	int rv;
 
 	while (((sc->sc_status = tpm_status(sc)) & mask) != mask && to--) {
-		rv = tsleep(chan, PRIBIO | PCATCH, "tpm_poll", 1);
+		rv = tsleep(chan, PCATCH, "tpm_poll", 1);
 		if (rv && rv != EWOULDBLOCK) {
 			return rv;
 		}
@@ -227,138 +208,32 @@ tpm_waitfor_poll(struct tpm_softc *sc, u
 	return 0;
 }
 
-/*
- * Wait for given status bits using interrupts.
- */
-static int
-tpm_waitfor_int(struct tpm_softc *sc, uint8_t mask, int tmo, wchan_t chan,
-    int inttype)
-{
-	int rv, to;
-
-	sc->sc_status = tpm_status(sc);
-	if ((sc->sc_status & mask) == mask)
-		return 0;
-
-	/*
-	 * Enable interrupt on tpm chip.  Note that interrupts on our
-	 * level (SPL_TTY) are disabled (see tpm{read,write} et al) and
-	 * will not be delivered to the cpu until we call tsleep(9) below.
-	 */
-	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INT_ENABLE,
-	    bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INT_ENABLE) |
-	    inttype);
-	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INT_ENABLE,
-	    bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INT_ENABLE) |
-	    TPM_GLOBAL_INT_ENABLE);
-
-	sc->sc_status = tpm_status(sc);
-	if ((sc->sc_status & mask) == mask) {
-		rv = 0;
-		goto out;
-	}
-
-	to = tpm_tmotohz(tmo);
-
-	/*
-	 * tsleep(9) enables interrupts on the cpu and returns after
-	 * wake up with interrupts disabled again.  Note that interrupts
-	 * generated by the tpm chip while being at SPL_TTY are not lost
-	 * but held and delivered as soon as the cpu goes below SPL_TTY.
-	 */
-	rv = tsleep(chan, PRIBIO | PCATCH, "tpm_wait", to);
-
-	sc->sc_status = tpm_status(sc);
-	if ((sc->sc_status & mask) == mask)
-		rv = 0;
-
-out:
-	/* Disable interrupts on tpm chip again. */
-	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INT_ENABLE,
-	    bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INT_ENABLE) &
-	    ~TPM_GLOBAL_INT_ENABLE);
-	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INT_ENABLE,
-	    bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INT_ENABLE) &
-	    ~inttype);
-
-	return rv;
-}
-
-/*
- * Wait on given status bits, use interrupts where possible, otherwise poll.
- */
 static int
 tpm_waitfor(struct tpm_softc *sc, uint8_t bits, int tmo, wchan_t chan)
 {
 	int retry, to, rv;
 	uint8_t todo;
 
-	/*
-	 * We use interrupts for TPM_STS_DATA_AVAIL and TPM_STS_VALID (if the
-	 * TPM chip supports them) as waiting for those can take really long.
-	 * The other TPM_STS* are not needed very often so we do not support
-	 * them.
-	 */
-	if (sc->sc_vector != -1) {
-		todo = bits;
-
-		/*
-		 * Wait for data ready. This interrupt only occurs when both
-		 * TPM_STS_VALID and TPM_STS_DATA_AVAIL are asserted. Thus we
-		 * don't have to bother with TPM_STS_VALID separately and can
-		 * just return.
-		 *
-		 * This only holds for interrupts! When using polling both
-		 * flags have to be waited for, see below.
-		 */
-		if ((bits & TPM_STS_DATA_AVAIL) &&
-		    (sc->sc_capabilities & TPM_INTF_DATA_AVAIL_INT))
-			return tpm_waitfor_int(sc, bits, tmo, chan,
-			    TPM_DATA_AVAIL_INT);
-
-		/* Wait for status valid bit. */
-		if ((bits & TPM_STS_VALID) &&
-		    (sc->sc_capabilities & TPM_INTF_STS_VALID_INT)) {
-			rv = tpm_waitfor_int(sc, bits, tmo, chan,
-			    TPM_STS_VALID_INT);
-			if (rv)
-				return rv;
-			todo = bits & ~TPM_STS_VALID;
-		}
-
-		/*
-		 * When all flags have been taken care of, return. Otherwise
-		 * use polling for eg TPM_STS_CMD_READY.
-		 */
-		if (todo == 0)
-			return 0;
-	}
-
+	to = tpm_tmotohz(tmo);
 	retry = 3;
 
 restart:
-	/*
-	 * If requested, wait for TPM_STS_VALID before dealing with any other
-	 * flag. Eg when both TPM_STS_DATA_AVAIL and TPM_STS_VALID are
-	 * requested, wait for the latter first.
-	 */
 	todo = bits;
-	if (bits & TPM_STS_VALID)
-		todo = TPM_STS_VALID;
-	to = tpm_tmotohz(tmo);
-again:
-	if ((rv = tpm_waitfor_poll(sc, todo, to, chan)) != 0)
-		return rv;
 
-	if ((todo & sc->sc_status) == TPM_STS_VALID) {
-		/* Now wait for other flags. */
-		todo = bits & ~TPM_STS_VALID;
-		to++;
-		goto again;
+	/*
+	 * TPM_STS_VALID has priority over the others.
+	 */
+	if (todo & TPM_STS_VALID) {
+		if ((rv = tpm_poll(sc, TPM_STS_VALID, to+1, chan)) != 0)
+			return rv;
+		todo &= ~TPM_STS_VALID;
 	}
 
+	if ((rv = tpm_poll(sc, todo, to, chan)) != 0)
+		return rv;
+
 	if ((todo & sc->sc_status) != todo) {
-		if (retry-- && (bits & TPM_STS_VALID)) {
+		if ((retry-- > 0) && (bits & TPM_STS_VALID)) {
 			bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_STS,
 			    TPM_STS_RESP_RETRY);
 			goto restart;
@@ -369,31 +244,6 @@ again:
 	return 0;
 }
 
-int
-tpm_intr(void *v)
-{
-	struct tpm_softc *sc = v;
-	uint32_t reg;
-
-	reg = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INT_STATUS);
-	if (!(reg & (TPM_CMD_READY_INT | TPM_LOCALITY_CHANGE_INT |
-	    TPM_STS_VALID_INT | TPM_DATA_AVAIL_INT)))
-		return 0;
-
-	if (reg & TPM_STS_VALID_INT)
-		wakeup(sc);
-	if (reg & TPM_CMD_READY_INT)
-		wakeup(sc->sc_write);
-	if (reg & TPM_DATA_AVAIL_INT)
-		wakeup(sc->sc_read);
-	if (reg & TPM_LOCALITY_CHANGE_INT)
-		wakeup(sc->sc_init);
-
-	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INT_STATUS, reg);
-
-	return 1;
-}
-
 /* -------------------------------------------------------------------------- */
 
 /*
@@ -412,8 +262,6 @@ tpm_tis12_probe(bus_space_tag_t bt, bus_
 		return 0;
 	if ((cap & TPM_CAPS_REQUIRED) != TPM_CAPS_REQUIRED)
 		return 0;
-	if (!(cap & (TPM_INTF_INT_EDGE_RISING | TPM_INTF_INT_LEVEL_LOW)))
-		return 0;
 
 	/* Request locality 0. */
 	bus_space_write_1(bt, bh, TPM_ACCESS, TPM_ACCESS_REQUEST_USE);
@@ -436,70 +284,16 @@ tpm_tis12_probe(bus_space_tag_t bt, bus_
 	return 1;
 }
 
-static int
-tpm_tis12_irqinit(struct tpm_softc *sc, int irq, int idx)
-{
-	uint32_t reg;
-
-	if ((irq == -1) || (tpm_devs[idx].flags & TPM_DEV_NOINTS)) {
-		sc->sc_vector = -1;
-		return 0;
-	}
-
-	/* Ack and disable all interrupts. */
-	reg = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INT_ENABLE);
-	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INT_ENABLE,
-	    reg & ~TPM_GLOBAL_INT_ENABLE);
-	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INT_STATUS,
-	    bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INT_STATUS));
-
-	/* Program interrupt vector. */
-	bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_INT_VECTOR, irq);
-	sc->sc_vector = irq;
-
-	/* Program interrupt type. */
-	reg &= ~(TPM_INT_EDGE_RISING|TPM_INT_EDGE_FALLING|TPM_INT_LEVEL_HIGH|
-	    TPM_INT_LEVEL_LOW);
-	reg |= TPM_GLOBAL_INT_ENABLE|TPM_CMD_READY_INT|TPM_LOCALITY_CHANGE_INT|
-	    TPM_STS_VALID_INT|TPM_DATA_AVAIL_INT;
-	if (sc->sc_capabilities & TPM_INTF_INT_EDGE_RISING)
-		reg |= TPM_INT_EDGE_RISING;
-	else if (sc->sc_capabilities & TPM_INTF_INT_EDGE_FALLING)
-		reg |= TPM_INT_EDGE_FALLING;
-	else if (sc->sc_capabilities & TPM_INTF_INT_LEVEL_HIGH)
-		reg |= TPM_INT_LEVEL_HIGH;
-	else
-		reg |= TPM_INT_LEVEL_LOW;
-
-	bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INT_ENABLE, reg);
-
-	return 0;
-}
-
 int
-tpm_tis12_init(struct tpm_softc *sc, int irq)
+tpm_tis12_init(struct tpm_softc *sc)
 {
-	int i;
-
-	sc->sc_capabilities = bus_space_read_4(sc->sc_bt, sc->sc_bh,
+	sc->sc_caps = bus_space_read_4(sc->sc_bt, sc->sc_bh,
 	    TPM_INTF_CAPABILITY);
 	sc->sc_devid = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_ID);
 	sc->sc_rev = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_REV);
 
-	for (i = 0; tpm_devs[i].devid; i++) {
-		if (tpm_devs[i].devid == sc->sc_devid)
-			break;
-	}
-
-	if (tpm_devs[i].devid)
-		aprint_normal_dev(sc->sc_dev, "%s rev 0x%x\n",
-		    tpm_devs[i].name, sc->sc_rev);
-	else
-		aprint_normal_dev(sc->sc_dev, "device 0x%08x rev 0x%x\n",
-		    sc->sc_devid, sc->sc_rev);
-
-	if (tpm_tis12_irqinit(sc, irq, i))
-		return 1;
+	aprint_normal_dev(sc->sc_dev, "device 0x%08x rev 0x%x\n",
+	    sc->sc_devid, sc->sc_rev);
 
 	if (tpm_request_locality(sc, 0))
 		return 1;
@@ -511,11 +305,11 @@ tpm_tis12_init(struct tpm_softc *sc, int
 }
 
 int
-tpm_tis12_start(struct tpm_softc *sc, int flag)
+tpm_tis12_start(struct tpm_softc *sc, int rw)
 {
 	int rv;
 
-	if (flag == UIO_READ) {
+	if (rw == UIO_READ) {
 		rv = tpm_waitfor(sc, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
 		    TPM_READ_TMO, sc->sc_read);
 		return rv;
@@ -544,7 +338,7 @@ tpm_tis12_read(struct tpm_softc *sc, voi
 {
 	uint8_t *p = buf;
 	size_t cnt;
-	int rv, n, bcnt;
+	int rv, n;
 
 	cnt = 0;
 	while (len > 0) {
@@ -553,12 +347,12 @@ tpm_tis12_read(struct tpm_softc *sc, voi
 		if (rv)
 			return rv;
 
-		bcnt = tpm_getburst(sc);
-		n = MIN(len, bcnt);
-
-		for (; n--; len--) {
+		n = MIN(len, tpm_getburst(sc));
+		while (n > 0) {
 			*p++ = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_DATA);
 			cnt++;
+			len--;
+			n--;
 		}
 
 		if ((flags & TPM_PARAM_SIZE) == 0 && cnt >= 6)
@@ -612,19 +406,18 @@ tpm_tis12_write(struct tpm_softc *sc, co
 }
 
 int
-tpm_tis12_end(struct tpm_softc *sc, int flag, int err)
+tpm_tis12_end(struct tpm_softc *sc, int rw, int err)
 {
 	int rv = 0;
 
-	if (flag == UIO_READ) {
+	if (rw == UIO_READ) {
 		rv = tpm_waitfor(sc, TPM_STS_VALID, TPM_READ_TMO, sc->sc_read);
 		if (rv)
 			return rv;
 
 		/* Still more data? */
 		sc->sc_status = tpm_status(sc);
-		if (!err && ((sc->sc_status & TPM_STS_DATA_AVAIL) ==
-		    TPM_STS_DATA_AVAIL)) {
+		if (!err && (sc->sc_status & TPM_STS_DATA_AVAIL)) {
 			rv = EIO;
 		}
 
@@ -668,107 +461,111 @@ const struct cdevsw tpm_cdevsw = {
 	.d_mmap = nommap,
 	.d_kqfilter = nokqfilter,
 	.d_discard = nodiscard,
-	.d_flag = D_OTHER,
+	.d_flag = D_OTHER | D_MPSAFE,
 };
 
-#define TPMUNIT(a)	minor(a)
-
 static int
 tpmopen(dev_t dev, int flag, int mode, struct lwp *l)
 {
-	struct tpm_softc *sc = device_lookup_private(&tpm_cd, TPMUNIT(dev));
+	struct tpm_softc *sc = device_lookup_private(&tpm_cd, minor(dev));
+	int ret = 0;
 
 	if (sc == NULL)
 		return ENXIO;
-	if (sc->sc_flags & TPM_OPEN)
-		return EBUSY;
 
-	sc->sc_flags |= TPM_OPEN;
+	mutex_enter(&sc->sc_lock);
+	if (sc->sc_busy) {
+		ret = EBUSY;
+	} else {
+		sc->sc_busy = true;
+	}
+	mutex_exit(&sc->sc_lock);
 
-	return 0;
+	return ret;
 }
 
 static int
 tpmclose(dev_t dev, int flag, int mode, struct lwp *l)
 {
-	struct tpm_softc *sc = device_lookup_private(&tpm_cd, TPMUNIT(dev));
+	struct tpm_softc *sc = device_lookup_private(&tpm_cd, minor(dev));
+	int ret = 0;
 
 	if (sc == NULL)
 		return ENXIO;
-	if (!(sc->sc_flags & TPM_OPEN))
-		return EINVAL;
 
-	sc->sc_flags &= ~TPM_OPEN;
+	mutex_enter(&sc->sc_lock);
+	if (!sc->sc_busy) {
+		ret = EINVAL;
+	} else {
+		sc->sc_busy = false;
+	}
+	mutex_exit(&sc->sc_lock);
 
-	return 0;
+	return ret;
 }
 
 static int
 tpmread(dev_t dev, struct uio *uio, int flags)
 {
-	struct tpm_softc *sc = device_lookup_private(&tpm_cd, TPMUNIT(dev));
-	uint8_t buf[TPM_BUFSIZ], *p;
+	struct tpm_softc *sc = device_lookup_private(&tpm_cd, minor(dev));
+	uint8_t buf[TPM_BUFSIZ];
 	size_t cnt, len, n;
-	int  rv, s;
+	int rv;
 
 	if (sc == NULL)
 		return ENXIO;
 
-	s = spltty();
 	if ((rv = (*sc->sc_start)(sc, UIO_READ)))
 		goto out;
 
+	/* Get the header. */
 	if ((rv = (*sc->sc_read)(sc, buf, TPM_HDRSIZE, &cnt, 0))) {
 		(*sc->sc_end)(sc, UIO_READ, rv);
 		goto out;
 	}
-
 	len = (buf[2] << 24) | (buf[3] << 16) | (buf[4] << 8) | buf[5];
-	if (len > uio->uio_resid) {
+	if (len > uio->uio_resid || len < cnt) {
 		rv = EIO;
 		(*sc->sc_end)(sc, UIO_READ, rv);
 		goto out;
 	}
 
-	/* Copy out header. */
+	/* Copy out the header. */
 	if ((rv = uiomove(buf, cnt, uio))) {
 		(*sc->sc_end)(sc, UIO_READ, rv);
 		goto out;
 	}
 
-	/* Get remaining part of the answer (if anything is left). */
-	for (len -= cnt, p = buf, n = sizeof(buf); len > 0; p = buf, len -= n,
-	    n = sizeof(buf)) {
-		n = MIN(n, len);
-		if ((rv = (*sc->sc_read)(sc, p, n, NULL, TPM_PARAM_SIZE))) {
+	/* Process the rest. */
+	len -= cnt;
+	while (len > 0) {
+		n = MIN(sizeof(buf), len);
+		if ((rv = (*sc->sc_read)(sc, buf, n, NULL, TPM_PARAM_SIZE))) {
 			(*sc->sc_end)(sc, UIO_READ, rv);
 			goto out;
 		}
-		p += n;
-		if ((rv = uiomove(buf, p - buf, uio))) {
+		if ((rv = uiomove(buf, n, uio))) {
 			(*sc->sc_end)(sc, UIO_READ, rv);
 			goto out;
 		}
+		len -= n;
 	}
 
 	rv = (*sc->sc_end)(sc, UIO_READ, rv);
 out:
-	splx(s);
 	return rv;
 }
 
 static int
 tpmwrite(dev_t dev, struct uio *uio, int flags)
 {
-	struct tpm_softc *sc = device_lookup_private(&tpm_cd, TPMUNIT(dev));
+	struct tpm_softc *sc = device_lookup_private(&tpm_cd, minor(dev));
 	uint8_t buf[TPM_BUFSIZ];
-	int n, rv, s;
+	int n, rv;
 
 	if (sc == NULL)
 		return ENXIO;
 
-	s = spltty();
-
 	n = MIN(sizeof(buf), uio->uio_resid);
 	if ((rv = uiomove(buf, n, uio))) {
 		goto out;
@@ -782,14 +579,13 @@ tpmwrite(dev_t dev, struct uio *uio, int
 
 	rv = (*sc->sc_end)(sc, UIO_WRITE, rv);
 out:
-	splx(s);
 	return rv;
 }
 
 static int
 tpmioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l)
 {
-	struct tpm_softc *sc = device_lookup_private(&tpm_cd, TPMUNIT(dev));
+	struct tpm_softc *sc = device_lookup_private(&tpm_cd, minor(dev));
 	struct tpm_ioc_getinfo *info;
 
 	if (sc == NULL)
@@ -802,7 +598,7 @@ tpmioctl(dev_t dev, u_long cmd, void *ad
 		info->tpm_version = sc->sc_ver;
 		info->device_id = sc->sc_devid;
 		info->device_rev = sc->sc_rev;
-		info->device_caps = sc->sc_capabilities;
+		info->device_caps = sc->sc_caps;
 		return 0;
 	default:
 		break;

Index: src/sys/dev/ic/tpmreg.h
diff -u src/sys/dev/ic/tpmreg.h:1.4 src/sys/dev/ic/tpmreg.h:1.5
--- src/sys/dev/ic/tpmreg.h:1.4	Sat Jun 22 12:57:41 2019
+++ src/sys/dev/ic/tpmreg.h	Tue Oct  8 18:43:02 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: tpmreg.h,v 1.4 2019/06/22 12:57:41 maxv Exp $	*/
+/*	$NetBSD: tpmreg.h,v 1.5 2019/10/08 18:43:02 maxv Exp $	*/
 
 /*
  * Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -78,6 +78,7 @@
 #define		TPM_STS_GO			__BIT(5)
 #define		TPM_STS_DATA_AVAIL		__BIT(4)
 #define		TPM_STS_DATA_EXPECT		__BIT(3)
+#define		TPM_STS_SELFTEST_DONE		__BIT(2)
 #define		TPM_STS_RESP_RETRY		__BIT(1)
 
 #define	TPM_DATA			0x0024	/* 32bit register */
Index: src/sys/dev/ic/tpmvar.h
diff -u src/sys/dev/ic/tpmvar.h:1.4 src/sys/dev/ic/tpmvar.h:1.5
--- src/sys/dev/ic/tpmvar.h:1.4	Sat Jun 22 12:57:41 2019
+++ src/sys/dev/ic/tpmvar.h	Tue Oct  8 18:43:02 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: tpmvar.h,v 1.4 2019/06/22 12:57:41 maxv Exp $	*/
+/*	$NetBSD: tpmvar.h,v 1.5 2019/10/08 18:43:02 maxv Exp $	*/
 
 /*
  * Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -29,24 +29,6 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-/*
- * Copyright (c) 2008, 2009 Michael Shalayeff
- * Copyright (c) 2009, 2010 Hans-Joerg Hoexer
- * All rights reserved.
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
- * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
 #define TPM_API_VERSION		1
 
 enum tpm_version {
@@ -56,7 +38,6 @@ enum tpm_version {
 
 struct tpm_ioc_getinfo {
 	uint32_t api_version;
-
 	uint32_t tpm_version;
 	uint32_t device_id;
 	uint32_t device_rev;
@@ -70,35 +51,29 @@ struct tpm_ioc_getinfo {
 struct tpm_softc {
 	device_t sc_dev;
 	enum tpm_version sc_ver;
-	void *sc_ih;
+	kmutex_t sc_lock;
+	bool sc_busy;
 
-	int (*sc_init)(struct tpm_softc *, int);
+	int (*sc_init)(struct tpm_softc *);
 	int (*sc_start)(struct tpm_softc *, int);
 	int (*sc_read)(struct tpm_softc *, void *, size_t, size_t *, int);
 	int (*sc_write)(struct tpm_softc *, const void *, size_t);
 	int (*sc_end)(struct tpm_softc *, int, int);
 
-	bus_space_tag_t sc_bt, sc_batm;
-	bus_space_handle_t sc_bh, sc_bahm;
+	bus_space_tag_t sc_bt;
+	bus_space_handle_t sc_bh;
 
 	uint32_t sc_devid;
 	uint32_t sc_rev;
 	uint32_t sc_status;
-	uint32_t sc_capabilities;
-
-	int sc_flags;
-#define	TPM_OPEN	0x0001
-
-	int sc_vector;
+	uint32_t sc_caps;
 };
 
-int tpm_intr(void *);
-
 bool tpm12_suspend(device_t, const pmf_qual_t *);
 bool tpm12_resume(device_t, const pmf_qual_t *);
 
 int tpm_tis12_probe(bus_space_tag_t, bus_space_handle_t);
-int tpm_tis12_init(struct tpm_softc *, int);
+int tpm_tis12_init(struct tpm_softc *);
 int tpm_tis12_start(struct tpm_softc *, int);
 int tpm_tis12_read(struct tpm_softc *, void *, size_t, size_t *, int);
 int tpm_tis12_write(struct tpm_softc *, const void *, size_t);

Index: src/sys/dev/isa/tpm_isa.c
diff -u src/sys/dev/isa/tpm_isa.c:1.4 src/sys/dev/isa/tpm_isa.c:1.5
--- src/sys/dev/isa/tpm_isa.c:1.4	Sat Jun 22 12:57:41 2019
+++ src/sys/dev/isa/tpm_isa.c	Tue Oct  8 18:43:03 2019
@@ -1,8 +1,37 @@
-/*	$NetBSD: tpm_isa.c,v 1.4 2019/06/22 12:57:41 maxv Exp $	*/
+/*	$NetBSD: tpm_isa.c,v 1.5 2019/10/08 18:43:03 maxv Exp $	*/
+
+/*
+ * Copyright (c) 2019 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Maxime Villard.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
 
 /*
  * Copyright (c) 2008, 2009 Michael Shalayeff
- * Copyright (c) 2009, 2010 Hans-Jrg Hxer
+ * Copyright (c) 2009, 2010 Hans-Joerg Hoexer
  * All rights reserved.
  *
  * Permission to use, copy, modify, and distribute this software for any
@@ -19,13 +48,10 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tpm_isa.c,v 1.4 2019/06/22 12:57:41 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tpm_isa.c,v 1.5 2019/10/08 18:43:03 maxv Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
-#include <sys/kernel.h>
-#include <sys/malloc.h>
-#include <sys/proc.h>
 #include <sys/device.h>
 #include <sys/bus.h>
 #include <sys/pmf.h>
@@ -36,13 +62,13 @@ __KERNEL_RCSID(0, "$NetBSD: tpm_isa.c,v 
 #include <dev/isa/isareg.h>
 #include <dev/isa/isavar.h>
 
+#include "ioconf.h"
+
 static int	tpm_isa_match(device_t, cfdata_t, void *);
 static void	tpm_isa_attach(device_t, device_t, void *);
 
-CFATTACH_DECL_NEW(tpm_isa, sizeof(struct tpm_softc),
-    tpm_isa_match, tpm_isa_attach, NULL, NULL);
-
-extern struct cfdriver tpm_cd;
+CFATTACH_DECL_NEW(tpm_isa, sizeof(struct tpm_softc), tpm_isa_match,
+    tpm_isa_attach, NULL, NULL);
 
 static int
 tpm_isa_match(device_t parent, cfdata_t match, void *aux)
@@ -60,8 +86,8 @@ tpm_isa_match(device_t parent, cfdata_t 
 		return 0;
 
 	/* XXX: integer locator sign extension */
-	if (bus_space_map(bt, (unsigned int)ia->ia_iomem[0].ir_addr, TPM_SPACE_SIZE,
-	    0, &bh))
+	if (bus_space_map(bt, (unsigned int)ia->ia_iomem[0].ir_addr,
+	    TPM_SPACE_SIZE, 0, &bh))
 		return 0;
 
 	if ((rv = tpm_tis12_probe(bt, bh))) {
@@ -80,46 +106,33 @@ tpm_isa_attach(device_t parent, device_t
 {
 	struct tpm_softc *sc = device_private(self);
 	struct isa_attach_args *ia = aux;
-	bus_addr_t iobase;
+	bus_addr_t base;
 	bus_size_t size;
-	int rv;
 
 	sc->sc_dev = self;
 	sc->sc_ver = TPM_1_2;
-
-	sc->sc_bt = ia->ia_memt;
-	iobase = (unsigned int)ia->ia_iomem[0].ir_addr;
-	size = TPM_SPACE_SIZE;
+	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
+	sc->sc_busy = false;
 	sc->sc_init = tpm_tis12_init;
 	sc->sc_start = tpm_tis12_start;
 	sc->sc_read = tpm_tis12_read;
 	sc->sc_write = tpm_tis12_write;
 	sc->sc_end = tpm_tis12_end;
+	sc->sc_bt = ia->ia_memt;
 
-	if (bus_space_map(sc->sc_bt, iobase, size, 0, &sc->sc_bh)) {
+	base = (unsigned int)ia->ia_iomem[0].ir_addr;
+	size = TPM_SPACE_SIZE;
+
+	if (bus_space_map(sc->sc_bt, base, size, 0, &sc->sc_bh)) {
 		aprint_error_dev(sc->sc_dev, "cannot map registers\n");
 		return;
 	}
 
-	if ((rv = (*sc->sc_init)(sc, ia->ia_irq[0].ir_irq)) != 0) {
+	if ((*sc->sc_init)(sc) != 0) {
 		bus_space_unmap(sc->sc_bt, sc->sc_bh, size);
 		return;
 	}
 
-	/*
-	 * Only setup interrupt handler when we have a vector and the
-	 * chip is TIS 1.2 compliant.
-	 */
-	if (sc->sc_init == tpm_tis12_init &&
-	    ia->ia_irq[0].ir_irq != ISA_UNKNOWN_IRQ &&
-	    (sc->sc_ih = isa_intr_establish_xname(ia->ia_ic,
-	     ia->ia_irq[0].ir_irq, IST_EDGE, IPL_TTY, tpm_intr, sc,
-	     device_xname(sc->sc_dev))) == NULL) {
-		bus_space_unmap(sc->sc_bt, sc->sc_bh, TPM_SPACE_SIZE);
-		aprint_error_dev(sc->sc_dev, "cannot establish interrupt\n");
-		return;
-	}
-
 	if (!pmf_device_register(sc->sc_dev, tpm12_suspend, tpm12_resume))
-		aprint_error_dev(sc->sc_dev, "cannot set power mgmt handler\n");
+		aprint_error_dev(sc->sc_dev, "couldn't establish power handler\n");
 }

Reply via email to