Skip to content

Commit a86a42a

Browse files
kkyarlagaddajarkkojs
authored andcommitted
tpm_tis_spi: Add hardware wait polling
TPM devices may insert wait state on last clock cycle of ADDR phase. For SPI controllers that support full-duplex transfers, this can be detected using software by reading the MISO line. For SPI controllers that only support half-duplex transfers, such as the Tegra QSPI, it is not possible to detect the wait signal from software. The QSPI controller in Tegra234 and Tegra241 implement hardware detection of the wait signal which can be enabled in the controller for TPM devices. The current TPM TIS driver only supports software detection of the wait signal. To support SPI controllers that use hardware to detect the wait signal, add the function tpm_tis_spi_transfer_half() and move the existing code for software based detection into a function called tpm_tis_spi_transfer_full(). SPI controllers that only support half-duplex transfers will always call tpm_tis_spi_transfer_half() because they cannot support software based detection. The bit SPI_TPM_HW_FLOW is set to indicate to the SPI controller that hardware detection is required and it is the responsibility of the SPI controller driver to determine if this is supported or not. For hardware flow control, CMD-ADDR-DATA messages are combined into a single message where as for software flow control exiting method of CMD-ADDR in a message and DATA in another is followed. [jarkko: Fixed the function names to match the code change, and the tag in the short summary.] Signed-off-by: Krishna Yarlagadda <kyarlagadda@nvidia.com> Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org> Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
1 parent 604b8e7 commit a86a42a

1 file changed

Lines changed: 89 additions & 2 deletions

File tree

drivers/char/tpm/tpm_tis_spi_main.c

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,74 @@ static int tpm_tis_spi_flow_control(struct tpm_tis_spi_phy *phy,
7171
return 0;
7272
}
7373

74-
int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len,
75-
u8 *in, const u8 *out)
74+
/*
75+
* Half duplex controller with support for TPM wait state detection like
76+
* Tegra QSPI need CMD, ADDR & DATA sent in single message to manage HW flow
77+
* control. Each phase sent in different transfer for controller to idenity
78+
* phase.
79+
*/
80+
static int tpm_tis_spi_transfer_half(struct tpm_tis_data *data, u32 addr,
81+
u16 len, u8 *in, const u8 *out)
82+
{
83+
struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
84+
struct spi_transfer spi_xfer[3];
85+
struct spi_message m;
86+
u8 transfer_len;
87+
int ret;
88+
89+
while (len) {
90+
transfer_len = min_t(u16, len, MAX_SPI_FRAMESIZE);
91+
92+
spi_message_init(&m);
93+
phy->iobuf[0] = (in ? 0x80 : 0) | (transfer_len - 1);
94+
phy->iobuf[1] = 0xd4;
95+
phy->iobuf[2] = addr >> 8;
96+
phy->iobuf[3] = addr;
97+
98+
memset(&spi_xfer, 0, sizeof(spi_xfer));
99+
100+
spi_xfer[0].tx_buf = phy->iobuf;
101+
spi_xfer[0].len = 1;
102+
spi_message_add_tail(&spi_xfer[0], &m);
103+
104+
spi_xfer[1].tx_buf = phy->iobuf + 1;
105+
spi_xfer[1].len = 3;
106+
spi_message_add_tail(&spi_xfer[1], &m);
107+
108+
if (out) {
109+
spi_xfer[2].tx_buf = &phy->iobuf[4];
110+
spi_xfer[2].rx_buf = NULL;
111+
memcpy(&phy->iobuf[4], out, transfer_len);
112+
out += transfer_len;
113+
}
114+
115+
if (in) {
116+
spi_xfer[2].tx_buf = NULL;
117+
spi_xfer[2].rx_buf = &phy->iobuf[4];
118+
}
119+
120+
spi_xfer[2].len = transfer_len;
121+
spi_message_add_tail(&spi_xfer[2], &m);
122+
123+
reinit_completion(&phy->ready);
124+
125+
ret = spi_sync(phy->spi_device, &m);
126+
if (ret < 0)
127+
return ret;
128+
129+
if (in) {
130+
memcpy(in, &phy->iobuf[4], transfer_len);
131+
in += transfer_len;
132+
}
133+
134+
len -= transfer_len;
135+
}
136+
137+
return ret;
138+
}
139+
140+
static int tpm_tis_spi_transfer_full(struct tpm_tis_data *data, u32 addr,
141+
u16 len, u8 *in, const u8 *out)
76142
{
77143
struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
78144
int ret = 0;
@@ -148,6 +214,24 @@ int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len,
148214
return ret;
149215
}
150216

217+
int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len,
218+
u8 *in, const u8 *out)
219+
{
220+
struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
221+
struct spi_controller *ctlr = phy->spi_device->controller;
222+
223+
/*
224+
* TPM flow control over SPI requires full duplex support.
225+
* Send entire message to a half duplex controller to handle
226+
* wait polling in controller.
227+
* Set TPM HW flow control flag..
228+
*/
229+
if (ctlr->flags & SPI_CONTROLLER_HALF_DUPLEX)
230+
return tpm_tis_spi_transfer_half(data, addr, len, in, out);
231+
else
232+
return tpm_tis_spi_transfer_full(data, addr, len, in, out);
233+
}
234+
151235
static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr,
152236
u16 len, u8 *result, enum tpm_tis_io_mode io_mode)
153237
{
@@ -189,6 +273,9 @@ static int tpm_tis_spi_probe(struct spi_device *dev)
189273

190274
phy->flow_control = tpm_tis_spi_flow_control;
191275

276+
if (dev->controller->flags & SPI_CONTROLLER_HALF_DUPLEX)
277+
dev->mode |= SPI_TPM_HW_FLOW;
278+
192279
/* If the SPI device has an IRQ then use that */
193280
if (dev->irq > 0)
194281
irq = dev->irq;

0 commit comments

Comments
 (0)