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