Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/tpm/tpm_tis.c
Show First 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | |||||
static int tpmtis_transmit(struct tpm_sc *sc, size_t length); | static int tpmtis_transmit(struct tpm_sc *sc, size_t length); | ||||
static int tpmtis_acpi_probe(device_t dev); | static int tpmtis_acpi_probe(device_t dev); | ||||
static int tpmtis_attach(device_t dev); | static int tpmtis_attach(device_t dev); | ||||
static int tpmtis_detach(device_t dev); | static int tpmtis_detach(device_t dev); | ||||
static void tpmtis_intr_handler(void *arg); | static void tpmtis_intr_handler(void *arg); | ||||
static ACPI_STATUS tpmtis_get_SIRQ_channel(ACPI_RESOURCE *res, void *arg); | static void tpmtis_setup_intr(struct tpm_sc *sc); | ||||
static bool tpmtis_setup_intr(struct tpm_sc *sc); | |||||
static bool tpmtis_read_bytes(struct tpm_sc *sc, size_t count, uint8_t *buf); | static bool tpmtis_read_bytes(struct tpm_sc *sc, size_t count, uint8_t *buf); | ||||
static bool tpmtis_write_bytes(struct tpm_sc *sc, size_t count, uint8_t *buf); | static bool tpmtis_write_bytes(struct tpm_sc *sc, size_t count, uint8_t *buf); | ||||
static bool tpmtis_request_locality(struct tpm_sc *sc, int locality); | static bool tpmtis_request_locality(struct tpm_sc *sc, int locality); | ||||
static void tpmtis_relinquish_locality(struct tpm_sc *sc); | static void tpmtis_relinquish_locality(struct tpm_sc *sc); | ||||
static bool tpmtis_go_ready(struct tpm_sc *sc); | static bool tpmtis_go_ready(struct tpm_sc *sc); | ||||
static bool tpm_wait_for_u32(struct tpm_sc *sc, bus_size_t off, | static bool tpm_wait_for_u32(struct tpm_sc *sc, bus_size_t off, | ||||
Show All 26 Lines | |||||
static int | static int | ||||
tpmtis_attach(device_t dev) | tpmtis_attach(device_t dev) | ||||
{ | { | ||||
struct tpm_sc *sc; | struct tpm_sc *sc; | ||||
int result; | int result; | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
sc->dev = dev; | sc->dev = dev; | ||||
sc->transmit = tpmtis_transmit; | |||||
sc->intr_type = -1; | |||||
sx_init(&sc->dev_lock, "TPM driver lock"); | |||||
sc->buf = malloc(TPM_BUFSIZE, M_TPM20, M_WAITOK); | |||||
sc->mem_rid = 0; | sc->mem_rid = 0; | ||||
sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, | sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, | ||||
RF_ACTIVE); | RF_ACTIVE); | ||||
if (sc->mem_res == NULL) | if (sc->mem_res == NULL) { | ||||
tpmtis_detach(dev); | |||||
return (ENXIO); | return (ENXIO); | ||||
} | |||||
sc->irq_rid = 0; | sc->irq_rid = 0; | ||||
sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, | sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, | ||||
RF_ACTIVE | RF_SHAREABLE); | RF_ACTIVE | RF_SHAREABLE); | ||||
if (sc->irq_res != NULL) { | if (sc->irq_res == NULL) | ||||
if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, | goto skip_irq; | ||||
NULL, tpmtis_intr_handler, sc, &sc->intr_cookie)) | |||||
sc->interrupts = false; | result = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, | ||||
else | NULL, tpmtis_intr_handler, sc, &sc->intr_cookie); | ||||
sc->interrupts = tpmtis_setup_intr(sc); | if (result != 0) { | ||||
} else { | bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq_res); | ||||
sc->interrupts = false; | goto skip_irq; | ||||
} | } | ||||
tpmtis_setup_intr(sc); | |||||
sc->intr_type = -1; | skip_irq: | ||||
sc->transmit = tpmtis_transmit; | |||||
result = tpm20_init(sc); | result = tpm20_init(sc); | ||||
if (result != 0) | if (result != 0) | ||||
tpmtis_detach(dev); | tpmtis_detach(dev); | ||||
return (result); | return (result); | ||||
} | } | ||||
static int | static int | ||||
Show All 13 Lines | tpmtis_detach(device_t dev) | ||||
if (sc->mem_res != NULL) | if (sc->mem_res != NULL) | ||||
bus_release_resource(dev, SYS_RES_MEMORY, | bus_release_resource(dev, SYS_RES_MEMORY, | ||||
sc->mem_rid, sc->mem_res); | sc->mem_rid, sc->mem_res); | ||||
return (0); | return (0); | ||||
} | } | ||||
static ACPI_STATUS | /* | ||||
tpmtis_get_SIRQ_channel(ACPI_RESOURCE *res, void *arg) | * Test if the advertisted interrupt actually works. | ||||
* This sends a simple command. (GetRandom) | |||||
* Interrupts are then enabled in the handler. | |||||
*/ | |||||
static void | |||||
tpmtis_test_intr(struct tpm_sc *sc) | |||||
{ | { | ||||
struct tpm_sc *sc; | uint8_t cmd[] = { | ||||
uint8_t channel; | 0x80, 0x01, /* TPM_ST_NO_SESSIONS tag*/ | ||||
0x00, 0x00, 0x00, 0x0c, /* cmd length */ | |||||
0x00, 0x00, 0x01, 0x7b, /* cmd TPM_CC_GetRandom */ | |||||
0x00, 0x01 /* number of bytes requested */ | |||||
}; | |||||
sc = (struct tpm_sc *)arg; | sx_xlock(&sc->dev_lock); | ||||
memcpy(sc->buf, cmd, sizeof(cmd)); | |||||
switch (res->Type) { | tpmtis_transmit(sc, sizeof(cmd)); | ||||
case ACPI_RESOURCE_TYPE_IRQ: | sc->pending_data_length = 0; | ||||
channel = res->Data.Irq.Interrupts[0]; | sx_xunlock(&sc->dev_lock); | ||||
break; | |||||
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: | |||||
channel = res->Data.ExtendedIrq.Interrupts[0]; | |||||
break; | |||||
default: | |||||
return (AE_OK); | |||||
} | } | ||||
WR1(sc, TPM_INT_VECTOR, channel); | static void | ||||
return (AE_OK); | |||||
} | |||||
static bool | |||||
tpmtis_setup_intr(struct tpm_sc *sc) | tpmtis_setup_intr(struct tpm_sc *sc) | ||||
{ | { | ||||
ACPI_STATUS status; | uint32_t reg; | ||||
ACPI_HANDLE handle; | uint8_t irq; | ||||
uint32_t irq_mask; | |||||
handle = acpi_get_handle(sc->dev); | irq = bus_get_resource_start(sc->dev, SYS_RES_IRQ, sc->irq_rid); | ||||
/* | |||||
* SIRQ has to be between 1 - 15. | |||||
* I found a system with ACPI table that reported a value of 0x2d. | |||||
* An attempt to use such value resulted in an interrupt storm. | |||||
*/ | |||||
if (irq == 0 || irq > 0xF) | |||||
return; | |||||
if(!tpmtis_request_locality(sc, 0)) | if(!tpmtis_request_locality(sc, 0)) | ||||
return (false); | sc->interrupts = false; | ||||
irq_mask = RD4(sc, TPM_INT_ENABLE); | WR1(sc, TPM_INT_VECTOR, irq); | ||||
irq_mask |= TPM_INT_ENABLE_GLOBAL_ENABLE | | |||||
/* Clear all pending interrupts. */ | |||||
reg = RD4(sc, TPM_INT_STS); | |||||
WR4(sc, TPM_INT_STS, reg); | |||||
reg = RD4(sc, TPM_INT_ENABLE); | |||||
reg |= TPM_INT_ENABLE_GLOBAL_ENABLE | | |||||
TPM_INT_ENABLE_DATA_AVAIL | | TPM_INT_ENABLE_DATA_AVAIL | | ||||
TPM_INT_ENABLE_LOC_CHANGE | | TPM_INT_ENABLE_LOC_CHANGE | | ||||
TPM_INT_ENABLE_CMD_RDY | | TPM_INT_ENABLE_CMD_RDY | | ||||
TPM_INT_ENABLE_STS_VALID; | TPM_INT_ENABLE_STS_VALID; | ||||
WR4(sc, TPM_INT_ENABLE, irq_mask); | WR4(sc, TPM_INT_ENABLE, reg); | ||||
status = AcpiWalkResources(handle, "_CRS", | |||||
tpmtis_get_SIRQ_channel, (void *)sc); | |||||
tpmtis_relinquish_locality(sc); | tpmtis_relinquish_locality(sc); | ||||
tpmtis_test_intr(sc); | |||||
return (ACPI_SUCCESS(status)); | |||||
} | } | ||||
static void | static void | ||||
tpmtis_intr_handler(void *arg) | tpmtis_intr_handler(void *arg) | ||||
{ | { | ||||
struct tpm_sc *sc; | struct tpm_sc *sc; | ||||
uint32_t status; | uint32_t status; | ||||
sc = (struct tpm_sc *)arg; | sc = (struct tpm_sc *)arg; | ||||
status = RD4(sc, TPM_INT_STS); | status = RD4(sc, TPM_INT_STS); | ||||
WR4(sc, TPM_INT_STS, status); | WR4(sc, TPM_INT_STS, status); | ||||
if (sc->intr_type != -1 && sc->intr_type & status) | |||||
/* Check for stray interrupts. */ | |||||
if (sc->intr_type == -1 || (sc->intr_type & status) == 0) | |||||
return; | |||||
sc->interrupts = true; | |||||
wakeup(sc); | wakeup(sc); | ||||
} | } | ||||
static bool | static bool | ||||
tpm_wait_for_u32(struct tpm_sc *sc, bus_size_t off, uint32_t mask, uint32_t val, | tpm_wait_for_u32(struct tpm_sc *sc, bus_size_t off, uint32_t mask, uint32_t val, | ||||
int32_t timeout) | int32_t timeout) | ||||
{ | { | ||||
/* Check for condition */ | /* Check for condition */ | ||||
▲ Show 20 Lines • Show All 253 Lines • Show Last 20 Lines |