drivers/char/tpm/tpm_tis_spi_cr50.c
Source file repositories/reference/linux-study-clean/drivers/char/tpm/tpm_tis_spi_cr50.c
File Facts
- System
- Linux kernel
- Corpus path
drivers/char/tpm/tpm_tis_spi_cr50.c- Extension
.c- Size
- 9850 bytes
- Lines
- 343
- Domain
- Driver Families
- Bucket
- drivers/char
- Inferred role
- Driver Families: implementation source
- Status
- source implementation candidate
Why This File Exists
Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.
- Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.
- Uses kernel synchronization; read lock ordering, sleepability, and interrupt context assumptions before translating.
- Touches IRQ or DMA behavior; this matters for the representative real-device path.
- Allocates kernel memory; connect allocation flags and lifetime to context constraints.
- Defines or uses C structs; map object ownership, embedded links, reference counts, and lock ownership.
Dependency Surface
linux/completion.hlinux/interrupt.hlinux/module.hlinux/of.hlinux/pm.hlinux/spi/spi.hlinux/wait.htpm_tis_core.htpm_tis_spi.h
Detected Declarations
struct cr50_spi_phyfunction cr50_spi_irq_handlerfunction cr50_ensure_access_delayfunction cr50_needs_wakingfunction cr50_wake_if_neededfunction cr50_spi_flow_controlfunction tpm_cr50_spi_is_firmware_power_managedfunction tpm_tis_spi_cr50_transferfunction tpm_tis_spi_cr50_read_bytesfunction tpm_tis_spi_cr50_write_bytesfunction cr50_print_fw_versionfunction cr50_spi_probefunction tpm_tis_spi_resume
Annotated Snippet
struct cr50_spi_phy {
struct tpm_tis_spi_phy spi_phy;
struct mutex time_track_mutex;
unsigned long last_access;
unsigned long access_delay;
unsigned int irq_confirmation_attempt;
bool irq_needs_confirmation;
bool irq_confirmed;
};
static inline struct cr50_spi_phy *to_cr50_spi_phy(struct tpm_tis_spi_phy *phy)
{
return container_of(phy, struct cr50_spi_phy, spi_phy);
}
/*
* The cr50 interrupt handler just signals waiting threads that the
* interrupt was asserted. It does not do any processing triggered
* by interrupts but is instead used to avoid fixed delays.
*/
static irqreturn_t cr50_spi_irq_handler(int dummy, void *dev_id)
{
struct cr50_spi_phy *cr50_phy = dev_id;
cr50_phy->irq_confirmed = true;
complete(&cr50_phy->spi_phy.ready);
return IRQ_HANDLED;
}
/*
* Cr50 needs to have at least some delay between consecutive
* transactions. Make sure we wait.
*/
static void cr50_ensure_access_delay(struct cr50_spi_phy *phy)
{
unsigned long allowed_access = phy->last_access + phy->access_delay;
unsigned long time_now = jiffies;
struct device *dev = &phy->spi_phy.spi_device->dev;
/*
* Note: There is a small chance, if Cr50 is not accessed in a few days,
* that time_in_range will not provide the correct result after the wrap
* around for jiffies. In this case, we'll have an unneeded short delay,
* which is fine.
*/
if (time_in_range_open(time_now, phy->last_access, allowed_access)) {
unsigned long remaining, timeout = allowed_access - time_now;
remaining = wait_for_completion_timeout(&phy->spi_phy.ready,
timeout);
if (!remaining && phy->irq_confirmed)
dev_warn(dev, "Timeout waiting for TPM ready IRQ\n");
}
if (phy->irq_needs_confirmation) {
unsigned int attempt = ++phy->irq_confirmation_attempt;
if (phy->irq_confirmed) {
phy->irq_needs_confirmation = false;
phy->access_delay = CR50_READY_IRQ_TIMEOUT;
dev_info(dev, "TPM ready IRQ confirmed on attempt %u\n",
attempt);
} else if (attempt > MAX_IRQ_CONFIRMATION_ATTEMPTS) {
phy->irq_needs_confirmation = false;
dev_warn(dev, "IRQ not confirmed - will use delays\n");
}
}
}
/*
* Cr50 might go to sleep if there is no SPI activity for some time and
* miss the first few bits/bytes on the bus. In such case, wake it up
* by asserting CS and give it time to start up.
*/
static bool cr50_needs_waking(struct cr50_spi_phy *phy)
{
/*
* Note: There is a small chance, if Cr50 is not accessed in a few days,
* that time_in_range will not provide the correct result after the wrap
* around for jiffies. In this case, we'll probably timeout or read
* incorrect value from TPM_STS and just retry the operation.
*/
return !time_in_range_open(jiffies, phy->last_access,
phy->spi_phy.wake_after);
}
Annotation
- Immediate include surface: `linux/completion.h`, `linux/interrupt.h`, `linux/module.h`, `linux/of.h`, `linux/pm.h`, `linux/spi/spi.h`, `linux/wait.h`, `tpm_tis_core.h`.
- Detected declarations: `struct cr50_spi_phy`, `function cr50_spi_irq_handler`, `function cr50_ensure_access_delay`, `function cr50_needs_waking`, `function cr50_wake_if_needed`, `function cr50_spi_flow_control`, `function tpm_cr50_spi_is_firmware_power_managed`, `function tpm_tis_spi_cr50_transfer`, `function tpm_tis_spi_cr50_read_bytes`, `function tpm_tis_spi_cr50_write_bytes`.
- Atlas domain: Driver Families / drivers/char.
- Implementation status: source implementation candidate.
- Synchronization appears in or near this file; preserve lock ordering, sleepability, and interrupt-context constraints.
- IRQ or DMA behavior appears here, which is relevant to the selected PCIe/NVMe device path.
Implementation Notes
- This generated page is the file-by-file coverage layer; curated subsystem chapters should link here when they synthesize a multi-file control flow.
- Core OS pages should be promoted from atlas-only to deep-reviewed when they explain data structures, invariants, locking, lifecycle, and C implementation snippets.
- Driver-family pages are intentionally pattern-oriented unless they are part of the selected PCIe/NVMe representative device path.