xref: /linux/drivers/char/tpm/tpm_tis.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
1b886d83cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
227084efeSLeendert van Doorn /*
327084efeSLeendert van Doorn  * Copyright (C) 2005, 2006 IBM Corporation
4399235dcSJarkko Sakkinen  * Copyright (C) 2014, 2015 Intel Corporation
527084efeSLeendert van Doorn  *
627084efeSLeendert van Doorn  * Authors:
727084efeSLeendert van Doorn  * Leendert van Doorn <leendert@watson.ibm.com>
827084efeSLeendert van Doorn  * Kylene Hall <kjhall@us.ibm.com>
927084efeSLeendert van Doorn  *
108e81cc13SKent Yoder  * Maintained by: <tpmdd-devel@lists.sourceforge.net>
118e81cc13SKent Yoder  *
1227084efeSLeendert van Doorn  * Device driver for TCG/TCPA TPM (trusted platform module).
1327084efeSLeendert van Doorn  * Specifications at www.trustedcomputinggroup.org
1427084efeSLeendert van Doorn  *
1527084efeSLeendert van Doorn  * This device driver implements the TPM interface as defined in
1627084efeSLeendert van Doorn  * the TCG TPM Interface Spec version 1.2, revision 1.0.
1727084efeSLeendert van Doorn  */
1857135568SKylene Jo Hall #include <linux/init.h>
1957135568SKylene Jo Hall #include <linux/module.h>
2057135568SKylene Jo Hall #include <linux/moduleparam.h>
2127084efeSLeendert van Doorn #include <linux/pnp.h>
225a0e3ad6STejun Heo #include <linux/slab.h>
2327084efeSLeendert van Doorn #include <linux/interrupt.h>
2427084efeSLeendert van Doorn #include <linux/wait.h>
253f0d3d01SMatthew Garrett #include <linux/acpi.h>
2620b87bbfSStefan Berger #include <linux/freezer.h>
27420d4398SJason Gunthorpe #include <linux/of.h>
2833957a10SJérémy Lefaure #include <linux/kernel.h>
2927084efeSLeendert van Doorn #include "tpm.h"
3057dacc2bSChristophe Ricard #include "tpm_tis_core.h"
3127084efeSLeendert van Doorn 
32399235dcSJarkko Sakkinen struct tpm_info {
3351dd43dfSJason Gunthorpe 	struct resource res;
34ef7b81dcSJason Gunthorpe 	/* irq > 0 means: use irq $irq;
35ef7b81dcSJason Gunthorpe 	 * irq = 0 means: autoprobe for an irq;
36ef7b81dcSJason Gunthorpe 	 * irq = -1 means: no irq support
37ef7b81dcSJason Gunthorpe 	 */
38ef7b81dcSJason Gunthorpe 	int irq;
39399235dcSJarkko Sakkinen };
40399235dcSJarkko Sakkinen 
4157dacc2bSChristophe Ricard struct tpm_tis_tcg_phy {
4257dacc2bSChristophe Ricard 	struct tpm_tis_data priv;
434eea703cSChristophe Ricard 	void __iomem *iobase;
44448e9c55SScot Doyle };
45448e9c55SScot Doyle 
to_tpm_tis_tcg_phy(struct tpm_tis_data * data)4657dacc2bSChristophe Ricard static inline struct tpm_tis_tcg_phy *to_tpm_tis_tcg_phy(struct tpm_tis_data *data)
4757dacc2bSChristophe Ricard {
4857dacc2bSChristophe Ricard 	return container_of(data, struct tpm_tis_tcg_phy, priv);
4957dacc2bSChristophe Ricard }
5057dacc2bSChristophe Ricard 
5177218e83SHaris Okanovic #ifdef CONFIG_PREEMPT_RT
5277218e83SHaris Okanovic /*
5377218e83SHaris Okanovic  * Flush previous write operations with a dummy read operation to the
5477218e83SHaris Okanovic  * TPM MMIO base address.
5577218e83SHaris Okanovic  */
tpm_tis_flush(void __iomem * iobase)5677218e83SHaris Okanovic static inline void tpm_tis_flush(void __iomem *iobase)
5777218e83SHaris Okanovic {
5877218e83SHaris Okanovic 	ioread8(iobase + TPM_ACCESS(0));
5977218e83SHaris Okanovic }
6077218e83SHaris Okanovic #else
6177218e83SHaris Okanovic #define tpm_tis_flush(iobase) do { } while (0)
6277218e83SHaris Okanovic #endif
6377218e83SHaris Okanovic 
6477218e83SHaris Okanovic /*
6577218e83SHaris Okanovic  * Write a byte word to the TPM MMIO address, and flush the write queue.
6677218e83SHaris Okanovic  * The flush ensures that the data is sent immediately over the bus and not
6777218e83SHaris Okanovic  * aggregated with further requests and transferred later in a batch. The large
6877218e83SHaris Okanovic  * write requests can lead to unwanted latency spikes by blocking the CPU until
6977218e83SHaris Okanovic  * the complete batch has been transferred.
7077218e83SHaris Okanovic  */
tpm_tis_iowrite8(u8 b,void __iomem * iobase,u32 addr)7177218e83SHaris Okanovic static inline void tpm_tis_iowrite8(u8 b, void __iomem *iobase, u32 addr)
7277218e83SHaris Okanovic {
7377218e83SHaris Okanovic 	iowrite8(b, iobase + addr);
7477218e83SHaris Okanovic 	tpm_tis_flush(iobase);
7577218e83SHaris Okanovic }
7677218e83SHaris Okanovic 
7777218e83SHaris Okanovic /*
7877218e83SHaris Okanovic  * Write a 32-bit word to the TPM MMIO address, and flush the write queue.
7977218e83SHaris Okanovic  * The flush ensures that the data is sent immediately over the bus and not
8077218e83SHaris Okanovic  * aggregated with further requests and transferred later in a batch. The large
8177218e83SHaris Okanovic  * write requests can lead to unwanted latency spikes by blocking the CPU until
8277218e83SHaris Okanovic  * the complete batch has been transferred.
8377218e83SHaris Okanovic  */
tpm_tis_iowrite32(u32 b,void __iomem * iobase,u32 addr)8477218e83SHaris Okanovic static inline void tpm_tis_iowrite32(u32 b, void __iomem *iobase, u32 addr)
8577218e83SHaris Okanovic {
8677218e83SHaris Okanovic 	iowrite32(b, iobase + addr);
8777218e83SHaris Okanovic 	tpm_tis_flush(iobase);
8877218e83SHaris Okanovic }
8977218e83SHaris Okanovic 
90bff24699SJarkko Sakkinen static bool interrupts;
91bff24699SJarkko Sakkinen module_param(interrupts, bool, 0444);
9241a5e1cfSChristophe Ricard MODULE_PARM_DESC(interrupts, "Enable interrupts");
9341a5e1cfSChristophe Ricard 
9441a5e1cfSChristophe Ricard static bool itpm;
9541a5e1cfSChristophe Ricard module_param(itpm, bool, 0444);
9641a5e1cfSChristophe Ricard MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)");
9741a5e1cfSChristophe Ricard 
9841a5e1cfSChristophe Ricard static bool force;
9941a5e1cfSChristophe Ricard #ifdef CONFIG_X86
10041a5e1cfSChristophe Ricard module_param(force, bool, 0444);
10141a5e1cfSChristophe Ricard MODULE_PARM_DESC(force, "Force device probe rather than using ACPI entry");
10241a5e1cfSChristophe Ricard #endif
10341a5e1cfSChristophe Ricard 
1041560ffe6SRandy Dunlap #if defined(CONFIG_PNP) && defined(CONFIG_ACPI)
has_hid(struct acpi_device * dev,const char * hid)105399235dcSJarkko Sakkinen static int has_hid(struct acpi_device *dev, const char *hid)
1063f0d3d01SMatthew Garrett {
1073f0d3d01SMatthew Garrett 	struct acpi_hardware_id *id;
1083f0d3d01SMatthew Garrett 
109399235dcSJarkko Sakkinen 	list_for_each_entry(id, &dev->pnp.ids, list)
110399235dcSJarkko Sakkinen 		if (!strcmp(hid, id->id))
111399235dcSJarkko Sakkinen 			return 1;
112399235dcSJarkko Sakkinen 
113399235dcSJarkko Sakkinen 	return 0;
114399235dcSJarkko Sakkinen }
115399235dcSJarkko Sakkinen 
is_itpm(struct acpi_device * dev)116399235dcSJarkko Sakkinen static inline int is_itpm(struct acpi_device *dev)
117399235dcSJarkko Sakkinen {
1184cb586a1SJason Gunthorpe 	if (!dev)
1194cb586a1SJason Gunthorpe 		return 0;
120399235dcSJarkko Sakkinen 	return has_hid(dev, "INTC0102");
121399235dcSJarkko Sakkinen }
1221560ffe6SRandy Dunlap #else
is_itpm(struct acpi_device * dev)123399235dcSJarkko Sakkinen static inline int is_itpm(struct acpi_device *dev)
1241560ffe6SRandy Dunlap {
1251560ffe6SRandy Dunlap 	return 0;
1261560ffe6SRandy Dunlap }
1273f0d3d01SMatthew Garrett #endif
1283f0d3d01SMatthew Garrett 
1294cb586a1SJason Gunthorpe #if defined(CONFIG_ACPI)
1304cb586a1SJason Gunthorpe #define DEVICE_IS_TPM2 1
1314cb586a1SJason Gunthorpe 
1324cb586a1SJason Gunthorpe static const struct acpi_device_id tpm_acpi_tbl[] = {
1334cb586a1SJason Gunthorpe 	{"MSFT0101", DEVICE_IS_TPM2},
1344cb586a1SJason Gunthorpe 	{},
1354cb586a1SJason Gunthorpe };
1364cb586a1SJason Gunthorpe MODULE_DEVICE_TABLE(acpi, tpm_acpi_tbl);
1374cb586a1SJason Gunthorpe 
check_acpi_tpm2(struct device * dev)1384cb586a1SJason Gunthorpe static int check_acpi_tpm2(struct device *dev)
1394cb586a1SJason Gunthorpe {
1404cb586a1SJason Gunthorpe 	const struct acpi_device_id *aid = acpi_match_device(tpm_acpi_tbl, dev);
1414cb586a1SJason Gunthorpe 	struct acpi_table_tpm2 *tbl;
1424cb586a1SJason Gunthorpe 	acpi_status st;
143db9622f7SHanjun Guo 	int ret = 0;
1444cb586a1SJason Gunthorpe 
1454cb586a1SJason Gunthorpe 	if (!aid || aid->driver_data != DEVICE_IS_TPM2)
1464cb586a1SJason Gunthorpe 		return 0;
1474cb586a1SJason Gunthorpe 
1484cb586a1SJason Gunthorpe 	/* If the ACPI TPM2 signature is matched then a global ACPI_SIG_TPM2
1494cb586a1SJason Gunthorpe 	 * table is mandatory
1504cb586a1SJason Gunthorpe 	 */
151db9622f7SHanjun Guo 	st = acpi_get_table(ACPI_SIG_TPM2, 1, (struct acpi_table_header **)&tbl);
1524cb586a1SJason Gunthorpe 	if (ACPI_FAILURE(st) || tbl->header.length < sizeof(*tbl)) {
1534cb586a1SJason Gunthorpe 		dev_err(dev, FW_BUG "failed to get TPM2 ACPI table\n");
1544cb586a1SJason Gunthorpe 		return -EINVAL;
1554cb586a1SJason Gunthorpe 	}
1564cb586a1SJason Gunthorpe 
1574cb586a1SJason Gunthorpe 	/* The tpm2_crb driver handles this device */
1584cb586a1SJason Gunthorpe 	if (tbl->start_method != ACPI_TPM2_MEMORY_MAPPED)
159db9622f7SHanjun Guo 		ret = -ENODEV;
1604cb586a1SJason Gunthorpe 
161db9622f7SHanjun Guo 	acpi_put_table((struct acpi_table_header *)tbl);
162db9622f7SHanjun Guo 	return ret;
1634cb586a1SJason Gunthorpe }
1644cb586a1SJason Gunthorpe #else
check_acpi_tpm2(struct device * dev)1654cb586a1SJason Gunthorpe static int check_acpi_tpm2(struct device *dev)
1664cb586a1SJason Gunthorpe {
1674cb586a1SJason Gunthorpe 	return 0;
1684cb586a1SJason Gunthorpe }
1694cb586a1SJason Gunthorpe #endif
1704cb586a1SJason Gunthorpe 
tpm_tcg_read_bytes(struct tpm_tis_data * data,u32 addr,u16 len,u8 * result,enum tpm_tis_io_mode io_mode)1711107d065SChristophe Ricard static int tpm_tcg_read_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
1726422cbd3SJohannes Holland 			      u8 *result, enum tpm_tis_io_mode io_mode)
1731107d065SChristophe Ricard {
1741107d065SChristophe Ricard 	struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
1756422cbd3SJohannes Holland 	__le16 result_le16;
1766422cbd3SJohannes Holland 	__le32 result_le32;
1771107d065SChristophe Ricard 
1786422cbd3SJohannes Holland 	switch (io_mode) {
1796422cbd3SJohannes Holland 	case TPM_TIS_PHYS_8:
1801107d065SChristophe Ricard 		while (len--)
1811107d065SChristophe Ricard 			*result++ = ioread8(phy->iobase + addr);
1826422cbd3SJohannes Holland 		break;
1836422cbd3SJohannes Holland 	case TPM_TIS_PHYS_16:
1846422cbd3SJohannes Holland 		result_le16 = cpu_to_le16(ioread16(phy->iobase + addr));
1856422cbd3SJohannes Holland 		memcpy(result, &result_le16, sizeof(u16));
1866422cbd3SJohannes Holland 		break;
1876422cbd3SJohannes Holland 	case TPM_TIS_PHYS_32:
1886422cbd3SJohannes Holland 		result_le32 = cpu_to_le32(ioread32(phy->iobase + addr));
1896422cbd3SJohannes Holland 		memcpy(result, &result_le32, sizeof(u32));
1906422cbd3SJohannes Holland 		break;
1916422cbd3SJohannes Holland 	}
1925e572cabSAzhar Shaikh 
1931107d065SChristophe Ricard 	return 0;
1941107d065SChristophe Ricard }
1951107d065SChristophe Ricard 
tpm_tcg_write_bytes(struct tpm_tis_data * data,u32 addr,u16 len,const u8 * value,enum tpm_tis_io_mode io_mode)1961107d065SChristophe Ricard static int tpm_tcg_write_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
1976422cbd3SJohannes Holland 			       const u8 *value, enum tpm_tis_io_mode io_mode)
1981107d065SChristophe Ricard {
1991107d065SChristophe Ricard 	struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
2001107d065SChristophe Ricard 
2016422cbd3SJohannes Holland 	switch (io_mode) {
2026422cbd3SJohannes Holland 	case TPM_TIS_PHYS_8:
2031107d065SChristophe Ricard 		while (len--)
20477218e83SHaris Okanovic 			tpm_tis_iowrite8(*value++, phy->iobase, addr);
2056422cbd3SJohannes Holland 		break;
2066422cbd3SJohannes Holland 	case TPM_TIS_PHYS_16:
2076422cbd3SJohannes Holland 		return -EINVAL;
2086422cbd3SJohannes Holland 	case TPM_TIS_PHYS_32:
20977218e83SHaris Okanovic 		tpm_tis_iowrite32(le32_to_cpu(*((__le32 *)value)), phy->iobase, addr);
2106422cbd3SJohannes Holland 		break;
2111107d065SChristophe Ricard 	}
2121107d065SChristophe Ricard 
2131107d065SChristophe Ricard 	return 0;
2141107d065SChristophe Ricard }
2151107d065SChristophe Ricard 
2161107d065SChristophe Ricard static const struct tpm_tis_phy_ops tpm_tcg = {
2171107d065SChristophe Ricard 	.read_bytes = tpm_tcg_read_bytes,
2181107d065SChristophe Ricard 	.write_bytes = tpm_tcg_write_bytes,
2191107d065SChristophe Ricard };
2201107d065SChristophe Ricard 
tpm_tis_init(struct device * dev,struct tpm_info * tpm_info)2214cb586a1SJason Gunthorpe static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info)
22227084efeSLeendert van Doorn {
22357dacc2bSChristophe Ricard 	struct tpm_tis_tcg_phy *phy;
22441a5e1cfSChristophe Ricard 	int irq = -1;
2254cb586a1SJason Gunthorpe 	int rc;
2264cb586a1SJason Gunthorpe 
2274cb586a1SJason Gunthorpe 	rc = check_acpi_tpm2(dev);
2284cb586a1SJason Gunthorpe 	if (rc)
2294cb586a1SJason Gunthorpe 		return rc;
23027084efeSLeendert van Doorn 
23157dacc2bSChristophe Ricard 	phy = devm_kzalloc(dev, sizeof(struct tpm_tis_tcg_phy), GFP_KERNEL);
23257dacc2bSChristophe Ricard 	if (phy == NULL)
233448e9c55SScot Doyle 		return -ENOMEM;
234afb5abc2SJarkko Sakkinen 
23557dacc2bSChristophe Ricard 	phy->iobase = devm_ioremap_resource(dev, &tpm_info->res);
23657dacc2bSChristophe Ricard 	if (IS_ERR(phy->iobase))
23757dacc2bSChristophe Ricard 		return PTR_ERR(phy->iobase);
23827084efeSLeendert van Doorn 
23941a5e1cfSChristophe Ricard 	if (interrupts)
24041a5e1cfSChristophe Ricard 		irq = tpm_info->irq;
2419519de3fSStefan Berger 
2424cb586a1SJason Gunthorpe 	if (itpm || is_itpm(ACPI_COMPANION(dev)))
243858e8b79SLino Sanfilippo 		set_bit(TPM_TIS_ITPM_WORKAROUND, &phy->priv.flags);
2443507d612SRajiv Andrade 
24541a5e1cfSChristophe Ricard 	return tpm_tis_core_init(dev, &phy->priv, irq, &tpm_tcg,
2464cb586a1SJason Gunthorpe 				 ACPI_HANDLE(dev));
24725112048SJason Gunthorpe }
24825112048SJason Gunthorpe 
249a2fa3fb0SShuah Khan static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume);
250a2fa3fb0SShuah Khan 
tpm_tis_pnp_init(struct pnp_dev * pnp_dev,const struct pnp_device_id * pnp_id)251afc6d369SBill Pemberton static int tpm_tis_pnp_init(struct pnp_dev *pnp_dev,
2529e323d3eSKylene Jo Hall 			    const struct pnp_device_id *pnp_id)
2539e323d3eSKylene Jo Hall {
254ef7b81dcSJason Gunthorpe 	struct tpm_info tpm_info = {};
25551dd43dfSJason Gunthorpe 	struct resource *res;
2567917ff9aSBjorn Helgaas 
25751dd43dfSJason Gunthorpe 	res = pnp_get_resource(pnp_dev, IORESOURCE_MEM, 0);
25851dd43dfSJason Gunthorpe 	if (!res)
25951dd43dfSJason Gunthorpe 		return -ENODEV;
26051dd43dfSJason Gunthorpe 	tpm_info.res = *res;
2619e323d3eSKylene Jo Hall 
2627917ff9aSBjorn Helgaas 	if (pnp_irq_valid(pnp_dev, 0))
263399235dcSJarkko Sakkinen 		tpm_info.irq = pnp_irq(pnp_dev, 0);
2647917ff9aSBjorn Helgaas 	else
265ef7b81dcSJason Gunthorpe 		tpm_info.irq = -1;
2667917ff9aSBjorn Helgaas 
2674cb586a1SJason Gunthorpe 	return tpm_tis_init(&pnp_dev->dev, &tpm_info);
2689e323d3eSKylene Jo Hall }
2699e323d3eSKylene Jo Hall 
270786a2aa2SJarkko Sakkinen /*
271786a2aa2SJarkko Sakkinen  * There is a known bug caused by 93e1b7d42e1e ("[PATCH] tpm: add HID module
272786a2aa2SJarkko Sakkinen  * parameter"). This commit added IFX0102 device ID, which is also used by
273786a2aa2SJarkko Sakkinen  * tpm_infineon but ignored to add quirks to probe which driver ought to be
274786a2aa2SJarkko Sakkinen  * used.
275786a2aa2SJarkko Sakkinen  */
276786a2aa2SJarkko Sakkinen 
2770bbed20eSBill Pemberton static struct pnp_device_id tpm_pnp_tbl[] = {
27827084efeSLeendert van Doorn 	{"PNP0C31", 0},		/* TPM */
27993e1b7d4SKylene Jo Hall 	{"ATM1200", 0},		/* Atmel */
280786a2aa2SJarkko Sakkinen 	{"IFX0102", 0},		/* Infineon */
28193e1b7d4SKylene Jo Hall 	{"BCM0101", 0},		/* Broadcom */
282061991ecSLE DISEZ Erwan 	{"BCM0102", 0},		/* Broadcom */
28393e1b7d4SKylene Jo Hall 	{"NSC1200", 0},		/* National */
284fb0e7e11SMarcin Obara 	{"ICO0102", 0},		/* Intel */
28593e1b7d4SKylene Jo Hall 	/* Add new here */
28693e1b7d4SKylene Jo Hall 	{"", 0},		/* User Specified */
28793e1b7d4SKylene Jo Hall 	{"", 0}			/* Terminator */
28827084efeSLeendert van Doorn };
28931bde71cSMatt Domsch MODULE_DEVICE_TABLE(pnp, tpm_pnp_tbl);
29027084efeSLeendert van Doorn 
tpm_tis_pnp_remove(struct pnp_dev * dev)29139af33fcSBill Pemberton static void tpm_tis_pnp_remove(struct pnp_dev *dev)
292253115b7SRajiv Andrade {
293253115b7SRajiv Andrade 	struct tpm_chip *chip = pnp_get_drvdata(dev);
294399235dcSJarkko Sakkinen 
295afb5abc2SJarkko Sakkinen 	tpm_chip_unregister(chip);
296afb5abc2SJarkko Sakkinen 	tpm_tis_remove(chip);
297253115b7SRajiv Andrade }
298253115b7SRajiv Andrade 
29927084efeSLeendert van Doorn static struct pnp_driver tis_pnp_driver = {
30027084efeSLeendert van Doorn 	.name = "tpm_tis",
30127084efeSLeendert van Doorn 	.id_table = tpm_pnp_tbl,
30227084efeSLeendert van Doorn 	.probe = tpm_tis_pnp_init,
303253115b7SRajiv Andrade 	.remove = tpm_tis_pnp_remove,
304a2fa3fb0SShuah Khan 	.driver	= {
305a2fa3fb0SShuah Khan 		.pm = &tpm_tis_pm,
306a2fa3fb0SShuah Khan 	},
30727084efeSLeendert van Doorn };
30827084efeSLeendert van Doorn 
30933957a10SJérémy Lefaure #define TIS_HID_USR_IDX (ARRAY_SIZE(tpm_pnp_tbl) - 2)
31093e1b7d4SKylene Jo Hall module_param_string(hid, tpm_pnp_tbl[TIS_HID_USR_IDX].id,
31193e1b7d4SKylene Jo Hall 		    sizeof(tpm_pnp_tbl[TIS_HID_USR_IDX].id), 0444);
31293e1b7d4SKylene Jo Hall MODULE_PARM_DESC(hid, "Set additional specific HID for this driver to probe");
3137a192ec3SMing Lei 
31400194826SJason Gunthorpe static struct platform_device *force_pdev;
31500194826SJason Gunthorpe 
tpm_tis_plat_probe(struct platform_device * pdev)31600194826SJason Gunthorpe static int tpm_tis_plat_probe(struct platform_device *pdev)
31700194826SJason Gunthorpe {
31800194826SJason Gunthorpe 	struct tpm_info tpm_info = {};
31900194826SJason Gunthorpe 	struct resource *res;
32000194826SJason Gunthorpe 
32100194826SJason Gunthorpe 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
32200194826SJason Gunthorpe 	if (res == NULL) {
32300194826SJason Gunthorpe 		dev_err(&pdev->dev, "no memory resource defined\n");
32400194826SJason Gunthorpe 		return -ENODEV;
32500194826SJason Gunthorpe 	}
32600194826SJason Gunthorpe 	tpm_info.res = *res;
32700194826SJason Gunthorpe 
3289c8c5742SHans de Goede 	tpm_info.irq = platform_get_irq_optional(pdev, 0);
329fc0e1322SJason Gunthorpe 	if (tpm_info.irq <= 0) {
330d27f81f0SJason Gunthorpe 		if (pdev != force_pdev)
33100194826SJason Gunthorpe 			tpm_info.irq = -1;
33200194826SJason Gunthorpe 		else
33300194826SJason Gunthorpe 			/* When forcing auto probe the IRQ */
33400194826SJason Gunthorpe 			tpm_info.irq = 0;
33500194826SJason Gunthorpe 	}
33600194826SJason Gunthorpe 
3374cb586a1SJason Gunthorpe 	return tpm_tis_init(&pdev->dev, &tpm_info);
33800194826SJason Gunthorpe }
33900194826SJason Gunthorpe 
tpm_tis_plat_remove(struct platform_device * pdev)340c3da2c6eSUwe Kleine-König static void tpm_tis_plat_remove(struct platform_device *pdev)
34100194826SJason Gunthorpe {
34200194826SJason Gunthorpe 	struct tpm_chip *chip = dev_get_drvdata(&pdev->dev);
34300194826SJason Gunthorpe 
34400194826SJason Gunthorpe 	tpm_chip_unregister(chip);
34500194826SJason Gunthorpe 	tpm_tis_remove(chip);
34600194826SJason Gunthorpe }
34700194826SJason Gunthorpe 
348420d4398SJason Gunthorpe #ifdef CONFIG_OF
349420d4398SJason Gunthorpe static const struct of_device_id tis_of_platform_match[] = {
35068bf59c3SLukas Wunner 	{.compatible = "atmel,at97sc3204"},
351420d4398SJason Gunthorpe 	{.compatible = "tcg,tpm-tis-mmio"},
352420d4398SJason Gunthorpe 	{},
353420d4398SJason Gunthorpe };
354420d4398SJason Gunthorpe MODULE_DEVICE_TABLE(of, tis_of_platform_match);
355420d4398SJason Gunthorpe #endif
356420d4398SJason Gunthorpe 
3577a192ec3SMing Lei static struct platform_driver tis_drv = {
35800194826SJason Gunthorpe 	.probe = tpm_tis_plat_probe,
359c3da2c6eSUwe Kleine-König 	.remove_new = tpm_tis_plat_remove,
3607a192ec3SMing Lei 	.driver = {
3619e323d3eSKylene Jo Hall 		.name		= "tpm_tis",
362b633f050SRafael J. Wysocki 		.pm		= &tpm_tis_pm,
363420d4398SJason Gunthorpe 		.of_match_table = of_match_ptr(tis_of_platform_match),
3644cb586a1SJason Gunthorpe 		.acpi_match_table = ACPI_PTR(tpm_acpi_tbl),
3657a192ec3SMing Lei 	},
3669e323d3eSKylene Jo Hall };
3679e323d3eSKylene Jo Hall 
tpm_tis_force_device(void)36800194826SJason Gunthorpe static int tpm_tis_force_device(void)
36927084efeSLeendert van Doorn {
37000194826SJason Gunthorpe 	struct platform_device *pdev;
37100194826SJason Gunthorpe 	static const struct resource x86_resources[] = {
3725a118a39SZhen Lei 		DEFINE_RES_MEM(0xFED40000, TIS_MEM_LEN)
37300194826SJason Gunthorpe 	};
37400194826SJason Gunthorpe 
375399235dcSJarkko Sakkinen 	if (!force)
376399235dcSJarkko Sakkinen 		return 0;
3779e323d3eSKylene Jo Hall 
37800194826SJason Gunthorpe 	/* The driver core will match the name tpm_tis of the device to
37900194826SJason Gunthorpe 	 * the tpm_tis platform driver and complete the setup via
38000194826SJason Gunthorpe 	 * tpm_tis_plat_probe
38100194826SJason Gunthorpe 	 */
38200194826SJason Gunthorpe 	pdev = platform_device_register_simple("tpm_tis", -1, x86_resources,
38300194826SJason Gunthorpe 					       ARRAY_SIZE(x86_resources));
38400194826SJason Gunthorpe 	if (IS_ERR(pdev))
38500194826SJason Gunthorpe 		return PTR_ERR(pdev);
38600194826SJason Gunthorpe 	force_pdev = pdev;
38700194826SJason Gunthorpe 
3884fba3c3bSWei Yongjun 	return 0;
38900194826SJason Gunthorpe }
39000194826SJason Gunthorpe 
init_tis(void)39100194826SJason Gunthorpe static int __init init_tis(void)
39200194826SJason Gunthorpe {
39300194826SJason Gunthorpe 	int rc;
39400194826SJason Gunthorpe 
39500194826SJason Gunthorpe 	rc = tpm_tis_force_device();
39600194826SJason Gunthorpe 	if (rc)
39700194826SJason Gunthorpe 		goto err_force;
39800194826SJason Gunthorpe 
39900194826SJason Gunthorpe 	rc = platform_driver_register(&tis_drv);
40000194826SJason Gunthorpe 	if (rc)
40100194826SJason Gunthorpe 		goto err_platform;
40200194826SJason Gunthorpe 
40300194826SJason Gunthorpe 
40400194826SJason Gunthorpe 	if (IS_ENABLED(CONFIG_PNP)) {
40500194826SJason Gunthorpe 		rc = pnp_register_driver(&tis_pnp_driver);
40600194826SJason Gunthorpe 		if (rc)
40700194826SJason Gunthorpe 			goto err_pnp;
40800194826SJason Gunthorpe 	}
40900194826SJason Gunthorpe 
41000194826SJason Gunthorpe 	return 0;
41100194826SJason Gunthorpe 
41200194826SJason Gunthorpe err_pnp:
4135939eaf4SWei Yongjun 	platform_driver_unregister(&tis_drv);
41400194826SJason Gunthorpe err_platform:
41500194826SJason Gunthorpe 	if (force_pdev)
41600194826SJason Gunthorpe 		platform_device_unregister(force_pdev);
41700194826SJason Gunthorpe err_force:
4189e323d3eSKylene Jo Hall 	return rc;
4199e323d3eSKylene Jo Hall }
4209e323d3eSKylene Jo Hall 
cleanup_tis(void)42127084efeSLeendert van Doorn static void __exit cleanup_tis(void)
42227084efeSLeendert van Doorn {
42300194826SJason Gunthorpe 	pnp_unregister_driver(&tis_pnp_driver);
4247a192ec3SMing Lei 	platform_driver_unregister(&tis_drv);
42500194826SJason Gunthorpe 
42600194826SJason Gunthorpe 	if (force_pdev)
42700194826SJason Gunthorpe 		platform_device_unregister(force_pdev);
42827084efeSLeendert van Doorn }
42927084efeSLeendert van Doorn 
43027084efeSLeendert van Doorn module_init(init_tis);
43127084efeSLeendert van Doorn module_exit(cleanup_tis);
432*6a57a219SAhelenia Ziemiańska MODULE_AUTHOR("Leendert van Doorn <leendert@watson.ibm.com>");
43327084efeSLeendert van Doorn MODULE_DESCRIPTION("TPM Driver");
43427084efeSLeendert van Doorn MODULE_VERSION("2.0");
43527084efeSLeendert van Doorn MODULE_LICENSE("GPL");
436