brintos

brintos / linux-shallow public Read only

0
0
Text · 9.2 KiB · 61b42c8 Raw
363 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2015 Infineon Technologies AG4 * Copyright (C) 2016 STMicroelectronics SAS5 *6 * Authors:7 * Peter Huewe <peter.huewe@infineon.com>8 * Christophe Ricard <christophe-h.ricard@st.com>9 *10 * Maintained by: <tpmdd-devel@lists.sourceforge.net>11 *12 * Device driver for TCG/TCPA TPM (trusted platform module).13 * Specifications at www.trustedcomputinggroup.org14 *15 * This device driver implements the TPM interface as defined in16 * the TCG TPM Interface Spec version 1.3, revision 27 via _raw/native17 * SPI access_.18 *19 * It is based on the original tpm_tis device driver from Leendert van20 * Dorn and Kyleen Hall and Jarko Sakkinnen.21 */22 23#include <linux/acpi.h>24#include <linux/completion.h>25#include <linux/init.h>26#include <linux/interrupt.h>27#include <linux/kernel.h>28#include <linux/module.h>29#include <linux/slab.h>30 31#include <linux/of.h>32#include <linux/spi/spi.h>33#include <linux/tpm.h>34 35#include "tpm.h"36#include "tpm_tis_core.h"37#include "tpm_tis_spi.h"38 39#define MAX_SPI_FRAMESIZE 6440#define SPI_HDRSIZE 441 42/*43 * TCG SPI flow control is documented in section 6.4 of the spec[1]. In short,44 * keep trying to read from the device until MISO goes high indicating the45 * wait state has ended.46 *47 * [1] https://trustedcomputinggroup.org/resource/pc-client-platform-tpm-profile-ptp-specification/48 */49static int tpm_tis_spi_flow_control(struct tpm_tis_spi_phy *phy,50				    struct spi_transfer *spi_xfer)51{52	struct spi_message m;53	int ret, i;54 55	if ((phy->iobuf[3] & 0x01) == 0) {56		// handle SPI wait states57		for (i = 0; i < TPM_RETRY; i++) {58			spi_xfer->len = 1;59			spi_message_init(&m);60			spi_message_add_tail(spi_xfer, &m);61			ret = spi_sync_locked(phy->spi_device, &m);62			if (ret < 0)63				return ret;64			if (phy->iobuf[0] & 0x01)65				break;66		}67 68		if (i == TPM_RETRY)69			return -ETIMEDOUT;70	}71 72	return 0;73}74 75/*76 * Half duplex controller with support for TPM wait state detection like77 * Tegra QSPI need CMD, ADDR & DATA sent in single message to manage HW flow78 * control. Each phase sent in different transfer for controller to idenity79 * phase.80 */81static int tpm_tis_spi_transfer_half(struct tpm_tis_data *data,	u32 addr,82				     u16 len, u8 *in, const u8 *out)83{84	struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);85	struct spi_transfer spi_xfer[3];86	struct spi_message m;87	u8 transfer_len;88	int ret;89 90	while (len) {91		transfer_len = min_t(u16, len, MAX_SPI_FRAMESIZE);92 93		spi_message_init(&m);94		phy->iobuf[0] = (in ? 0x80 : 0) | (transfer_len - 1);95		phy->iobuf[1] = 0xd4;96		phy->iobuf[2] = addr >> 8;97		phy->iobuf[3] = addr;98 99		memset(&spi_xfer, 0, sizeof(spi_xfer));100 101		spi_xfer[0].tx_buf = phy->iobuf;102		spi_xfer[0].len = 1;103		spi_message_add_tail(&spi_xfer[0], &m);104 105		spi_xfer[1].tx_buf = phy->iobuf + 1;106		spi_xfer[1].len = 3;107		spi_message_add_tail(&spi_xfer[1], &m);108 109		if (out) {110			spi_xfer[2].tx_buf = &phy->iobuf[4];111			spi_xfer[2].rx_buf = NULL;112			memcpy(&phy->iobuf[4], out, transfer_len);113			out += transfer_len;114		}115 116		if (in) {117			spi_xfer[2].tx_buf = NULL;118			spi_xfer[2].rx_buf = &phy->iobuf[4];119		}120 121		spi_xfer[2].len = transfer_len;122		spi_message_add_tail(&spi_xfer[2], &m);123 124		reinit_completion(&phy->ready);125 126		ret = spi_sync(phy->spi_device, &m);127		if (ret < 0)128			return ret;129 130		if (in) {131			memcpy(in, &phy->iobuf[4], transfer_len);132			in += transfer_len;133		}134 135		len -= transfer_len;136	}137 138	return ret;139}140 141static int tpm_tis_spi_transfer_full(struct tpm_tis_data *data, u32 addr,142				     u16 len, u8 *in, const u8 *out)143{144	struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);145	int ret = 0;146	struct spi_message m;147	struct spi_transfer spi_xfer;148	u8 transfer_len;149 150	spi_bus_lock(phy->spi_device->controller);151 152	while (len) {153		transfer_len = min_t(u16, len, MAX_SPI_FRAMESIZE);154 155		phy->iobuf[0] = (in ? 0x80 : 0) | (transfer_len - 1);156		phy->iobuf[1] = 0xd4;157		phy->iobuf[2] = addr >> 8;158		phy->iobuf[3] = addr;159 160		memset(&spi_xfer, 0, sizeof(spi_xfer));161		spi_xfer.tx_buf = phy->iobuf;162		spi_xfer.rx_buf = phy->iobuf;163		spi_xfer.len = 4;164		spi_xfer.cs_change = 1;165 166		spi_message_init(&m);167		spi_message_add_tail(&spi_xfer, &m);168		ret = spi_sync_locked(phy->spi_device, &m);169		if (ret < 0)170			goto exit;171 172		/* Flow control transfers are receive only */173		spi_xfer.tx_buf = NULL;174		ret = phy->flow_control(phy, &spi_xfer);175		if (ret < 0)176			goto exit;177 178		spi_xfer.cs_change = 0;179		spi_xfer.len = transfer_len;180		spi_xfer.delay.value = 5;181		spi_xfer.delay.unit = SPI_DELAY_UNIT_USECS;182 183		if (out) {184			spi_xfer.tx_buf = phy->iobuf;185			spi_xfer.rx_buf = NULL;186			memcpy(phy->iobuf, out, transfer_len);187			out += transfer_len;188		}189 190		spi_message_init(&m);191		spi_message_add_tail(&spi_xfer, &m);192		reinit_completion(&phy->ready);193		ret = spi_sync_locked(phy->spi_device, &m);194		if (ret < 0)195			goto exit;196 197		if (in) {198			memcpy(in, phy->iobuf, transfer_len);199			in += transfer_len;200		}201 202		len -= transfer_len;203	}204 205exit:206	if (ret < 0) {207		/* Deactivate chip select */208		memset(&spi_xfer, 0, sizeof(spi_xfer));209		spi_message_init(&m);210		spi_message_add_tail(&spi_xfer, &m);211		spi_sync_locked(phy->spi_device, &m);212	}213 214	spi_bus_unlock(phy->spi_device->controller);215	return ret;216}217 218int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len,219			 u8 *in, const u8 *out)220{221	struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);222	struct spi_controller *ctlr = phy->spi_device->controller;223 224	/*225	 * TPM flow control over SPI requires full duplex support.226	 * Send entire message to a half duplex controller to handle227	 * wait polling in controller.228	 * Set TPM HW flow control flag..229	 */230	if (ctlr->flags & SPI_CONTROLLER_HALF_DUPLEX)231		return tpm_tis_spi_transfer_half(data, addr, len, in, out);232	else233		return tpm_tis_spi_transfer_full(data, addr, len, in, out);234}235 236static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr,237				  u16 len, u8 *result, enum tpm_tis_io_mode io_mode)238{239	return tpm_tis_spi_transfer(data, addr, len, result, NULL);240}241 242static int tpm_tis_spi_write_bytes(struct tpm_tis_data *data, u32 addr,243				   u16 len, const u8 *value, enum tpm_tis_io_mode io_mode)244{245	return tpm_tis_spi_transfer(data, addr, len, NULL, value);246}247 248int tpm_tis_spi_init(struct spi_device *spi, struct tpm_tis_spi_phy *phy,249		     int irq, const struct tpm_tis_phy_ops *phy_ops)250{251	phy->iobuf = devm_kmalloc(&spi->dev, SPI_HDRSIZE + MAX_SPI_FRAMESIZE, GFP_KERNEL);252	if (!phy->iobuf)253		return -ENOMEM;254 255	phy->spi_device = spi;256 257	return tpm_tis_core_init(&spi->dev, &phy->priv, irq, phy_ops, NULL);258}259 260static const struct tpm_tis_phy_ops tpm_spi_phy_ops = {261	.read_bytes = tpm_tis_spi_read_bytes,262	.write_bytes = tpm_tis_spi_write_bytes,263};264 265static int tpm_tis_spi_probe(struct spi_device *dev)266{267	struct tpm_tis_spi_phy *phy;268	int irq;269 270	phy = devm_kzalloc(&dev->dev, sizeof(struct tpm_tis_spi_phy),271			   GFP_KERNEL);272	if (!phy)273		return -ENOMEM;274 275	phy->flow_control = tpm_tis_spi_flow_control;276 277	if (dev->controller->flags & SPI_CONTROLLER_HALF_DUPLEX)278		dev->mode |= SPI_TPM_HW_FLOW;279 280	/* If the SPI device has an IRQ then use that */281	if (dev->irq > 0)282		irq = dev->irq;283	else284		irq = -1;285 286	init_completion(&phy->ready);287	return tpm_tis_spi_init(dev, phy, irq, &tpm_spi_phy_ops);288}289 290typedef int (*tpm_tis_spi_probe_func)(struct spi_device *);291 292static int tpm_tis_spi_driver_probe(struct spi_device *spi)293{294	const struct spi_device_id *spi_dev_id = spi_get_device_id(spi);295	tpm_tis_spi_probe_func probe_func;296 297	probe_func = of_device_get_match_data(&spi->dev);298	if (!probe_func) {299		if (spi_dev_id) {300			probe_func = (tpm_tis_spi_probe_func)spi_dev_id->driver_data;301			if (!probe_func)302				return -ENODEV;303		} else304			probe_func = tpm_tis_spi_probe;305	}306 307	return probe_func(spi);308}309 310static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_spi_resume);311 312static void tpm_tis_spi_remove(struct spi_device *dev)313{314	struct tpm_chip *chip = spi_get_drvdata(dev);315 316	tpm_chip_unregister(chip);317	tpm_tis_remove(chip);318}319 320static const struct spi_device_id tpm_tis_spi_id[] = {321	{ "attpm20p", (unsigned long)tpm_tis_spi_probe },322	{ "st33htpm-spi", (unsigned long)tpm_tis_spi_probe },323	{ "slb9670", (unsigned long)tpm_tis_spi_probe },324	{ "tpm_tis_spi", (unsigned long)tpm_tis_spi_probe },325	{ "tpm_tis-spi", (unsigned long)tpm_tis_spi_probe },326	{ "cr50", (unsigned long)cr50_spi_probe },327	{}328};329MODULE_DEVICE_TABLE(spi, tpm_tis_spi_id);330 331static const struct of_device_id of_tis_spi_match[] __maybe_unused = {332	{ .compatible = "atmel,attpm20p", .data = tpm_tis_spi_probe },333	{ .compatible = "st,st33htpm-spi", .data = tpm_tis_spi_probe },334	{ .compatible = "infineon,slb9670", .data = tpm_tis_spi_probe },335	{ .compatible = "tcg,tpm_tis-spi", .data = tpm_tis_spi_probe },336	{ .compatible = "google,cr50", .data = cr50_spi_probe },337	{}338};339MODULE_DEVICE_TABLE(of, of_tis_spi_match);340 341static const struct acpi_device_id acpi_tis_spi_match[] __maybe_unused = {342	{"SMO0768", 0},343	{}344};345MODULE_DEVICE_TABLE(acpi, acpi_tis_spi_match);346 347static struct spi_driver tpm_tis_spi_driver = {348	.driver = {349		.name = "tpm_tis_spi",350		.pm = &tpm_tis_pm,351		.of_match_table = of_match_ptr(of_tis_spi_match),352		.acpi_match_table = ACPI_PTR(acpi_tis_spi_match),353		.probe_type = PROBE_PREFER_ASYNCHRONOUS,354	},355	.probe = tpm_tis_spi_driver_probe,356	.remove = tpm_tis_spi_remove,357	.id_table = tpm_tis_spi_id,358};359module_spi_driver(tpm_tis_spi_driver);360 361MODULE_DESCRIPTION("TPM Driver for native SPI access");362MODULE_LICENSE("GPL");363