tpm_tis.c 9.52 KB
Newer Older
1 2
/*
 * Copyright (C) 2005, 2006 IBM Corporation
3
 * Copyright (C) 2014, 2015 Intel Corporation
4 5 6 7 8
 *
 * Authors:
 * Leendert van Doorn <leendert@watson.ibm.com>
 * Kylene Hall <kjhall@us.ibm.com>
 *
Kent Yoder's avatar
Kent Yoder committed
9 10
 * Maintained by: <tpmdd-devel@lists.sourceforge.net>
 *
11 12 13 14 15 16 17 18 19 20 21
 * Device driver for TCG/TCPA TPM (trusted platform module).
 * Specifications at www.trustedcomputinggroup.org
 *
 * This device driver implements the TPM interface as defined in
 * the TCG TPM Interface Spec version 1.2, revision 1.0.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, version 2 of the
 * License.
 */
22 23 24
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
25
#include <linux/pnp.h>
26
#include <linux/slab.h>
27 28
#include <linux/interrupt.h>
#include <linux/wait.h>
Matthew Garrett's avatar
Matthew Garrett committed
29
#include <linux/acpi.h>
30
#include <linux/freezer.h>
31 32
#include <linux/of.h>
#include <linux/of_device.h>
33
#include <linux/kernel.h>
34
#include "tpm.h"
35
#include "tpm_tis_core.h"
36

37
struct tpm_info {
38
	struct resource res;
39 40 41 42 43
	/* irq > 0 means: use irq $irq;
	 * irq = 0 means: autoprobe for an irq;
	 * irq = -1 means: no irq support
	 */
	int irq;
44 45
};

46 47
struct tpm_tis_tcg_phy {
	struct tpm_tis_data priv;
48
	void __iomem *iobase;
49 50
};

51 52 53 54 55
static inline struct tpm_tis_tcg_phy *to_tpm_tis_tcg_phy(struct tpm_tis_data *data)
{
	return container_of(data, struct tpm_tis_tcg_phy, priv);
}

56 57 58 59 60 61 62 63 64 65 66 67 68 69
static bool interrupts = true;
module_param(interrupts, bool, 0444);
MODULE_PARM_DESC(interrupts, "Enable interrupts");

static bool itpm;
module_param(itpm, bool, 0444);
MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)");

static bool force;
#ifdef CONFIG_X86
module_param(force, bool, 0444);
MODULE_PARM_DESC(force, "Force device probe rather than using ACPI entry");
#endif

70
#if defined(CONFIG_PNP) && defined(CONFIG_ACPI)
71
static int has_hid(struct acpi_device *dev, const char *hid)
Matthew Garrett's avatar
Matthew Garrett committed
72 73 74
{
	struct acpi_hardware_id *id;

75 76
	list_for_each_entry(id, &dev->pnp.ids, list)
		if (!strcmp(hid, id->id))
Matthew Garrett's avatar
Matthew Garrett committed
77 78 79 80
			return 1;

	return 0;
}
81 82 83

static inline int is_itpm(struct acpi_device *dev)
{
84 85
	if (!dev)
		return 0;
86 87
	return has_hid(dev, "INTC0102");
}
88
#else
89
static inline int is_itpm(struct acpi_device *dev)
90 91 92
{
	return 0;
}
Matthew Garrett's avatar
Matthew Garrett committed
93 94
#endif

95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
#if defined(CONFIG_ACPI)
#define DEVICE_IS_TPM2 1

static const struct acpi_device_id tpm_acpi_tbl[] = {
	{"MSFT0101", DEVICE_IS_TPM2},
	{},
};
MODULE_DEVICE_TABLE(acpi, tpm_acpi_tbl);

static int check_acpi_tpm2(struct device *dev)
{
	const struct acpi_device_id *aid = acpi_match_device(tpm_acpi_tbl, dev);
	struct acpi_table_tpm2 *tbl;
	acpi_status st;

	if (!aid || aid->driver_data != DEVICE_IS_TPM2)
		return 0;

	/* If the ACPI TPM2 signature is matched then a global ACPI_SIG_TPM2
	 * table is mandatory
	 */
	st =
	    acpi_get_table(ACPI_SIG_TPM2, 1, (struct acpi_table_header **)&tbl);
	if (ACPI_FAILURE(st) || tbl->header.length < sizeof(*tbl)) {
		dev_err(dev, FW_BUG "failed to get TPM2 ACPI table\n");
		return -EINVAL;
	}

	/* The tpm2_crb driver handles this device */
	if (tbl->start_method != ACPI_TPM2_MEMORY_MAPPED)
		return -ENODEV;

	return 0;
}
#else
static int check_acpi_tpm2(struct device *dev)
{
	return 0;
}
#endif

136 137 138 139 140
static int tpm_tcg_read_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
			      u8 *result)
{
	struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);

141 142
	if (is_bsw() && !(data->flags & TPM_TIS_CLK_ENABLE))
		WARN(1, "CLKRUN not enabled!\n");
143

144 145
	while (len--)
		*result++ = ioread8(phy->iobase + addr);
146

147 148 149 150
	return 0;
}

static int tpm_tcg_write_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
151
			       const u8 *value)
152 153 154
{
	struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);

155 156
	if (is_bsw() && !(data->flags & TPM_TIS_CLK_ENABLE))
		WARN(1, "CLKRUN not enabled!\n");
157

158 159
	while (len--)
		iowrite8(*value++, phy->iobase + addr);
160

161 162 163 164 165 166 167
	return 0;
}

static int tpm_tcg_read16(struct tpm_tis_data *data, u32 addr, u16 *result)
{
	struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);

168 169
	if (is_bsw() && !(data->flags & TPM_TIS_CLK_ENABLE))
		WARN(1, "CLKRUN not enabled!\n");
170

171
	*result = ioread16(phy->iobase + addr);
172

173 174 175 176 177 178 179
	return 0;
}

static int tpm_tcg_read32(struct tpm_tis_data *data, u32 addr, u32 *result)
{
	struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);

180 181
	if (is_bsw() && !(data->flags & TPM_TIS_CLK_ENABLE))
		WARN(1, "CLKRUN not enabled!\n");
182

183
	*result = ioread32(phy->iobase + addr);
184

185 186 187 188 189 190 191
	return 0;
}

static int tpm_tcg_write32(struct tpm_tis_data *data, u32 addr, u32 value)
{
	struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);

192 193
	if (is_bsw() && !(data->flags & TPM_TIS_CLK_ENABLE))
		WARN(1, "CLKRUN not enabled!\n");
194

195
	iowrite32(value, phy->iobase + addr);
196

197 198 199 200 201 202 203 204 205 206 207
	return 0;
}

static const struct tpm_tis_phy_ops tpm_tcg = {
	.read_bytes = tpm_tcg_read_bytes,
	.write_bytes = tpm_tcg_write_bytes,
	.read16 = tpm_tcg_read16,
	.read32 = tpm_tcg_read32,
	.write32 = tpm_tcg_write32,
};

208
static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info)
209
{
210
	struct tpm_tis_tcg_phy *phy;
211
	int irq = -1;
212 213 214 215 216
	int rc;

	rc = check_acpi_tpm2(dev);
	if (rc)
		return rc;
217

218 219
	phy = devm_kzalloc(dev, sizeof(struct tpm_tis_tcg_phy), GFP_KERNEL);
	if (phy == NULL)
220
		return -ENOMEM;
221

222 223 224
	phy->iobase = devm_ioremap_resource(dev, &tpm_info->res);
	if (IS_ERR(phy->iobase))
		return PTR_ERR(phy->iobase);
225

226 227
	if (interrupts)
		irq = tpm_info->irq;
228

229
	if (itpm || is_itpm(ACPI_COMPANION(dev)))
230
		phy->priv.flags |= TPM_TIS_ITPM_WORKAROUND;
231

232
	return tpm_tis_core_init(dev, &phy->priv, irq, &tpm_tcg,
233
				 ACPI_HANDLE(dev));
234
}
235

236 237
static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume);

Bill Pemberton's avatar
Bill Pemberton committed
238
static int tpm_tis_pnp_init(struct pnp_dev *pnp_dev,
239
			    const struct pnp_device_id *pnp_id)
240
{
241
	struct tpm_info tpm_info = {};
242
	struct resource *res;
243

244 245 246 247
	res = pnp_get_resource(pnp_dev, IORESOURCE_MEM, 0);
	if (!res)
		return -ENODEV;
	tpm_info.res = *res;
248

249
	if (pnp_irq_valid(pnp_dev, 0))
250
		tpm_info.irq = pnp_irq(pnp_dev, 0);
251
	else
252
		tpm_info.irq = -1;
253

254
	return tpm_tis_init(&pnp_dev->dev, &tpm_info);
255 256
}

257
static struct pnp_device_id tpm_pnp_tbl[] = {
258
	{"PNP0C31", 0},		/* TPM */
259 260 261
	{"ATM1200", 0},		/* Atmel */
	{"IFX0102", 0},		/* Infineon */
	{"BCM0101", 0},		/* Broadcom */
262
	{"BCM0102", 0},		/* Broadcom */
263
	{"NSC1200", 0},		/* National */
264
	{"ICO0102", 0},		/* Intel */
265 266 267
	/* Add new here */
	{"", 0},		/* User Specified */
	{"", 0}			/* Terminator */
268
};
269
MODULE_DEVICE_TABLE(pnp, tpm_pnp_tbl);
270

Bill Pemberton's avatar
Bill Pemberton committed
271
static void tpm_tis_pnp_remove(struct pnp_dev *dev)
272 273
{
	struct tpm_chip *chip = pnp_get_drvdata(dev);
274
	struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
275

276 277
	tpm_chip_unregister(chip);
	tpm_tis_remove(chip);
278 279
}

280 281 282 283
static struct pnp_driver tis_pnp_driver = {
	.name = "tpm_tis",
	.id_table = tpm_pnp_tbl,
	.probe = tpm_tis_pnp_init,
284
	.remove = tpm_tis_pnp_remove,
285 286 287
	.driver	= {
		.pm = &tpm_tis_pm,
	},
288 289
};

290
#define TIS_HID_USR_IDX (ARRAY_SIZE(tpm_pnp_tbl) - 2)
291 292 293
module_param_string(hid, tpm_pnp_tbl[TIS_HID_USR_IDX].id,
		    sizeof(tpm_pnp_tbl[TIS_HID_USR_IDX].id), 0444);
MODULE_PARM_DESC(hid, "Set additional specific HID for this driver to probe");
294

295 296 297 298 299 300 301 302 303 304 305 306 307 308
static struct platform_device *force_pdev;

static int tpm_tis_plat_probe(struct platform_device *pdev)
{
	struct tpm_info tpm_info = {};
	struct resource *res;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (res == NULL) {
		dev_err(&pdev->dev, "no memory resource defined\n");
		return -ENODEV;
	}
	tpm_info.res = *res;

309 310
	tpm_info.irq = platform_get_irq(pdev, 0);
	if (tpm_info.irq <= 0) {
311
		if (pdev != force_pdev)
312 313 314 315 316 317
			tpm_info.irq = -1;
		else
			/* When forcing auto probe the IRQ */
			tpm_info.irq = 0;
	}

318
	return tpm_tis_init(&pdev->dev, &tpm_info);
319 320 321 322 323
}

static int tpm_tis_plat_remove(struct platform_device *pdev)
{
	struct tpm_chip *chip = dev_get_drvdata(&pdev->dev);
324
	struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
325 326 327 328 329 330 331

	tpm_chip_unregister(chip);
	tpm_tis_remove(chip);

	return 0;
}

332 333 334 335 336 337 338 339
#ifdef CONFIG_OF
static const struct of_device_id tis_of_platform_match[] = {
	{.compatible = "tcg,tpm-tis-mmio"},
	{},
};
MODULE_DEVICE_TABLE(of, tis_of_platform_match);
#endif

340
static struct platform_driver tis_drv = {
341 342
	.probe = tpm_tis_plat_probe,
	.remove = tpm_tis_plat_remove,
343
	.driver = {
344
		.name		= "tpm_tis",
345
		.pm		= &tpm_tis_pm,
346
		.of_match_table = of_match_ptr(tis_of_platform_match),
347
		.acpi_match_table = ACPI_PTR(tpm_acpi_tbl),
348
	},
349 350
};

351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
static int tpm_tis_force_device(void)
{
	struct platform_device *pdev;
	static const struct resource x86_resources[] = {
		{
			.start = 0xFED40000,
			.end = 0xFED40000 + TIS_MEM_LEN - 1,
			.flags = IORESOURCE_MEM,
		},
	};

	if (!force)
		return 0;

	/* The driver core will match the name tpm_tis of the device to
	 * the tpm_tis platform driver and complete the setup via
	 * tpm_tis_plat_probe
	 */
	pdev = platform_device_register_simple("tpm_tis", -1, x86_resources,
					       ARRAY_SIZE(x86_resources));
	if (IS_ERR(pdev))
		return PTR_ERR(pdev);
	force_pdev = pdev;

	return 0;
}

378 379
static int __init init_tis(void)
{
380
	int rc;
381 382 383 384 385 386 387 388 389

	rc = tpm_tis_force_device();
	if (rc)
		goto err_force;

	rc = platform_driver_register(&tis_drv);
	if (rc)
		goto err_platform;

390

391 392 393 394
	if (IS_ENABLED(CONFIG_PNP)) {
		rc = pnp_register_driver(&tis_pnp_driver);
		if (rc)
			goto err_pnp;
395
	}
396

397
	return 0;
398 399

err_pnp:
400
	platform_driver_unregister(&tis_drv);
401 402 403 404
err_platform:
	if (force_pdev)
		platform_device_unregister(force_pdev);
err_force:
405
	return rc;
406 407 408 409
}

static void __exit cleanup_tis(void)
{
410
	pnp_unregister_driver(&tis_pnp_driver);
411
	platform_driver_unregister(&tis_drv);
412 413 414

	if (force_pdev)
		platform_device_unregister(force_pdev);
415 416 417 418 419 420 421 422
}

module_init(init_tis);
module_exit(cleanup_tis);
MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
MODULE_DESCRIPTION("TPM Driver");
MODULE_VERSION("2.0");
MODULE_LICENSE("GPL");