xref: /linux/drivers/char/tpm/tpm-chip.c (revision a0339404fd2753c042eb7ea11bd3288dbfc38107)
1b886d83cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2afb5abc2SJarkko Sakkinen /*
3afb5abc2SJarkko Sakkinen  * Copyright (C) 2004 IBM Corporation
4afb5abc2SJarkko Sakkinen  * Copyright (C) 2014 Intel Corporation
5afb5abc2SJarkko Sakkinen  *
6afb5abc2SJarkko Sakkinen  * Authors:
7afb5abc2SJarkko Sakkinen  * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
8afb5abc2SJarkko Sakkinen  * Leendert van Doorn <leendert@watson.ibm.com>
9afb5abc2SJarkko Sakkinen  * Dave Safford <safford@watson.ibm.com>
10afb5abc2SJarkko Sakkinen  * Reiner Sailer <sailer@watson.ibm.com>
11afb5abc2SJarkko Sakkinen  * Kylene Hall <kjhall@us.ibm.com>
12afb5abc2SJarkko Sakkinen  *
13afb5abc2SJarkko Sakkinen  * Maintained by: <tpmdd-devel@lists.sourceforge.net>
14afb5abc2SJarkko Sakkinen  *
15afb5abc2SJarkko Sakkinen  * TPM chip management routines.
16afb5abc2SJarkko Sakkinen  */
17afb5abc2SJarkko Sakkinen 
18afb5abc2SJarkko Sakkinen #include <linux/poll.h>
19afb5abc2SJarkko Sakkinen #include <linux/slab.h>
20afb5abc2SJarkko Sakkinen #include <linux/mutex.h>
21afb5abc2SJarkko Sakkinen #include <linux/spinlock.h>
22afb5abc2SJarkko Sakkinen #include <linux/freezer.h>
23313d21eeSJarkko Sakkinen #include <linux/major.h>
24fd3ec366SThiebaud Weksteen #include <linux/tpm_eventlog.h>
256e592a06SJason Gunthorpe #include <linux/hw_random.h>
26afb5abc2SJarkko Sakkinen #include "tpm.h"
27afb5abc2SJarkko Sakkinen 
2815516788SStefan Berger DEFINE_IDR(dev_nums_idr);
2915516788SStefan Berger static DEFINE_MUTEX(idr_lock);
30afb5abc2SJarkko Sakkinen 
31d2e8071bSIvan Orlov const struct class tpm_class = {
32d2e8071bSIvan Orlov 	.name = "tpm",
33d2e8071bSIvan Orlov 	.shutdown_pre = tpm_class_shutdown,
34d2e8071bSIvan Orlov };
35d2e8071bSIvan Orlov const struct class tpmrm_class = {
36ea72883aSJustin M. Forbes 	.name = "tpmrm",
37d2e8071bSIvan Orlov };
38313d21eeSJarkko Sakkinen dev_t tpm_devt;
39313d21eeSJarkko Sakkinen 
tpm_request_locality(struct tpm_chip * chip)4047a6c28bSJarkko Sakkinen static int tpm_request_locality(struct tpm_chip *chip)
41719b7d81SJarkko Sakkinen {
42719b7d81SJarkko Sakkinen 	int rc;
43719b7d81SJarkko Sakkinen 
44719b7d81SJarkko Sakkinen 	if (!chip->ops->request_locality)
45719b7d81SJarkko Sakkinen 		return 0;
46719b7d81SJarkko Sakkinen 
47719b7d81SJarkko Sakkinen 	rc = chip->ops->request_locality(chip, 0);
48719b7d81SJarkko Sakkinen 	if (rc < 0)
49719b7d81SJarkko Sakkinen 		return rc;
50719b7d81SJarkko Sakkinen 
51719b7d81SJarkko Sakkinen 	chip->locality = rc;
52719b7d81SJarkko Sakkinen 	return 0;
53719b7d81SJarkko Sakkinen }
54719b7d81SJarkko Sakkinen 
tpm_relinquish_locality(struct tpm_chip * chip)5547a6c28bSJarkko Sakkinen static void tpm_relinquish_locality(struct tpm_chip *chip)
56719b7d81SJarkko Sakkinen {
57719b7d81SJarkko Sakkinen 	int rc;
58719b7d81SJarkko Sakkinen 
59719b7d81SJarkko Sakkinen 	if (!chip->ops->relinquish_locality)
60719b7d81SJarkko Sakkinen 		return;
61719b7d81SJarkko Sakkinen 
62719b7d81SJarkko Sakkinen 	rc = chip->ops->relinquish_locality(chip, chip->locality);
63719b7d81SJarkko Sakkinen 	if (rc)
64719b7d81SJarkko Sakkinen 		dev_err(&chip->dev, "%s: : error %d\n", __func__, rc);
65719b7d81SJarkko Sakkinen 
66719b7d81SJarkko Sakkinen 	chip->locality = -1;
67719b7d81SJarkko Sakkinen }
68719b7d81SJarkko Sakkinen 
tpm_cmd_ready(struct tpm_chip * chip)6947a6c28bSJarkko Sakkinen static int tpm_cmd_ready(struct tpm_chip *chip)
70719b7d81SJarkko Sakkinen {
71719b7d81SJarkko Sakkinen 	if (!chip->ops->cmd_ready)
72719b7d81SJarkko Sakkinen 		return 0;
73719b7d81SJarkko Sakkinen 
74719b7d81SJarkko Sakkinen 	return chip->ops->cmd_ready(chip);
75719b7d81SJarkko Sakkinen }
76719b7d81SJarkko Sakkinen 
tpm_go_idle(struct tpm_chip * chip)7747a6c28bSJarkko Sakkinen static int tpm_go_idle(struct tpm_chip *chip)
78719b7d81SJarkko Sakkinen {
79719b7d81SJarkko Sakkinen 	if (!chip->ops->go_idle)
80719b7d81SJarkko Sakkinen 		return 0;
81719b7d81SJarkko Sakkinen 
82719b7d81SJarkko Sakkinen 	return chip->ops->go_idle(chip);
83719b7d81SJarkko Sakkinen }
84719b7d81SJarkko Sakkinen 
tpm_clk_enable(struct tpm_chip * chip)851e5ac630SMilan Broz static void tpm_clk_enable(struct tpm_chip *chip)
861e5ac630SMilan Broz {
871e5ac630SMilan Broz 	if (chip->ops->clk_enable)
881e5ac630SMilan Broz 		chip->ops->clk_enable(chip, true);
891e5ac630SMilan Broz }
901e5ac630SMilan Broz 
tpm_clk_disable(struct tpm_chip * chip)911e5ac630SMilan Broz static void tpm_clk_disable(struct tpm_chip *chip)
921e5ac630SMilan Broz {
931e5ac630SMilan Broz 	if (chip->ops->clk_enable)
941e5ac630SMilan Broz 		chip->ops->clk_enable(chip, false);
951e5ac630SMilan Broz }
961e5ac630SMilan Broz 
97719b7d81SJarkko Sakkinen /**
98719b7d81SJarkko Sakkinen  * tpm_chip_start() - power on the TPM
99719b7d81SJarkko Sakkinen  * @chip:	a TPM chip to use
100719b7d81SJarkko Sakkinen  *
101719b7d81SJarkko Sakkinen  * Return:
102719b7d81SJarkko Sakkinen  * * The response length	- OK
103719b7d81SJarkko Sakkinen  * * -errno			- A system error
104719b7d81SJarkko Sakkinen  */
tpm_chip_start(struct tpm_chip * chip)10547a6c28bSJarkko Sakkinen int tpm_chip_start(struct tpm_chip *chip)
106719b7d81SJarkko Sakkinen {
107719b7d81SJarkko Sakkinen 	int ret;
108719b7d81SJarkko Sakkinen 
1091e5ac630SMilan Broz 	tpm_clk_enable(chip);
110719b7d81SJarkko Sakkinen 
111719b7d81SJarkko Sakkinen 	if (chip->locality == -1) {
11247a6c28bSJarkko Sakkinen 		ret = tpm_request_locality(chip);
113719b7d81SJarkko Sakkinen 		if (ret) {
1141e5ac630SMilan Broz 			tpm_clk_disable(chip);
115719b7d81SJarkko Sakkinen 			return ret;
116719b7d81SJarkko Sakkinen 		}
117719b7d81SJarkko Sakkinen 	}
118719b7d81SJarkko Sakkinen 
11947a6c28bSJarkko Sakkinen 	ret = tpm_cmd_ready(chip);
120719b7d81SJarkko Sakkinen 	if (ret) {
12147a6c28bSJarkko Sakkinen 		tpm_relinquish_locality(chip);
1221e5ac630SMilan Broz 		tpm_clk_disable(chip);
123719b7d81SJarkko Sakkinen 		return ret;
124719b7d81SJarkko Sakkinen 	}
125719b7d81SJarkko Sakkinen 
126719b7d81SJarkko Sakkinen 	return 0;
127719b7d81SJarkko Sakkinen }
128719b7d81SJarkko Sakkinen EXPORT_SYMBOL_GPL(tpm_chip_start);
129719b7d81SJarkko Sakkinen 
130719b7d81SJarkko Sakkinen /**
131719b7d81SJarkko Sakkinen  * tpm_chip_stop() - power off the TPM
132719b7d81SJarkko Sakkinen  * @chip:	a TPM chip to use
133719b7d81SJarkko Sakkinen  *
134719b7d81SJarkko Sakkinen  * Return:
135719b7d81SJarkko Sakkinen  * * The response length	- OK
136719b7d81SJarkko Sakkinen  * * -errno			- A system error
137719b7d81SJarkko Sakkinen  */
tpm_chip_stop(struct tpm_chip * chip)13847a6c28bSJarkko Sakkinen void tpm_chip_stop(struct tpm_chip *chip)
139719b7d81SJarkko Sakkinen {
14047a6c28bSJarkko Sakkinen 	tpm_go_idle(chip);
14147a6c28bSJarkko Sakkinen 	tpm_relinquish_locality(chip);
1421e5ac630SMilan Broz 	tpm_clk_disable(chip);
143719b7d81SJarkko Sakkinen }
144719b7d81SJarkko Sakkinen EXPORT_SYMBOL_GPL(tpm_chip_stop);
145719b7d81SJarkko Sakkinen 
1464e26195fSJason Gunthorpe /**
1474e26195fSJason Gunthorpe  * tpm_try_get_ops() - Get a ref to the tpm_chip
1484e26195fSJason Gunthorpe  * @chip: Chip to ref
1494e26195fSJason Gunthorpe  *
1504e26195fSJason Gunthorpe  * The caller must already have some kind of locking to ensure that chip is
1514e26195fSJason Gunthorpe  * valid. This function will lock the chip so that the ops member can be
1524e26195fSJason Gunthorpe  * accessed safely. The locking prevents tpm_chip_unregister from
1534e26195fSJason Gunthorpe  * completing, so it should not be held for long periods.
1544e26195fSJason Gunthorpe  *
1554e26195fSJason Gunthorpe  * Returns -ERRNO if the chip could not be got.
1564e26195fSJason Gunthorpe  */
tpm_try_get_ops(struct tpm_chip * chip)1574e26195fSJason Gunthorpe int tpm_try_get_ops(struct tpm_chip *chip)
1584e26195fSJason Gunthorpe {
1594e26195fSJason Gunthorpe 	int rc = -EIO;
1604e26195fSJason Gunthorpe 
161eb24c978SJames Bottomley 	if (chip->flags & TPM_CHIP_FLAG_DISABLE)
162eb24c978SJames Bottomley 		return rc;
163eb24c978SJames Bottomley 
1644e26195fSJason Gunthorpe 	get_device(&chip->dev);
1654e26195fSJason Gunthorpe 
1664e26195fSJason Gunthorpe 	down_read(&chip->ops_sem);
1674e26195fSJason Gunthorpe 	if (!chip->ops)
168a3fbfae8SJarkko Sakkinen 		goto out_ops;
1694e26195fSJason Gunthorpe 
1702f257402SJarkko Sakkinen 	mutex_lock(&chip->tpm_mutex);
17147a6c28bSJarkko Sakkinen 	rc = tpm_chip_start(chip);
172a3fbfae8SJarkko Sakkinen 	if (rc)
173a3fbfae8SJarkko Sakkinen 		goto out_lock;
174a3fbfae8SJarkko Sakkinen 
1754e26195fSJason Gunthorpe 	return 0;
1764e26195fSJason Gunthorpe out_lock:
177a3fbfae8SJarkko Sakkinen 	mutex_unlock(&chip->tpm_mutex);
178a3fbfae8SJarkko Sakkinen out_ops:
1794e26195fSJason Gunthorpe 	up_read(&chip->ops_sem);
1804e26195fSJason Gunthorpe 	put_device(&chip->dev);
1814e26195fSJason Gunthorpe 	return rc;
1824e26195fSJason Gunthorpe }
1834e26195fSJason Gunthorpe EXPORT_SYMBOL_GPL(tpm_try_get_ops);
1844e26195fSJason Gunthorpe 
1854e26195fSJason Gunthorpe /**
1864e26195fSJason Gunthorpe  * tpm_put_ops() - Release a ref to the tpm_chip
1874e26195fSJason Gunthorpe  * @chip: Chip to put
1884e26195fSJason Gunthorpe  *
1894e26195fSJason Gunthorpe  * This is the opposite pair to tpm_try_get_ops(). After this returns chip may
1904e26195fSJason Gunthorpe  * be kfree'd.
1914e26195fSJason Gunthorpe  */
tpm_put_ops(struct tpm_chip * chip)1924e26195fSJason Gunthorpe void tpm_put_ops(struct tpm_chip *chip)
1934e26195fSJason Gunthorpe {
19447a6c28bSJarkko Sakkinen 	tpm_chip_stop(chip);
1952f257402SJarkko Sakkinen 	mutex_unlock(&chip->tpm_mutex);
1964e26195fSJason Gunthorpe 	up_read(&chip->ops_sem);
1974e26195fSJason Gunthorpe 	put_device(&chip->dev);
1984e26195fSJason Gunthorpe }
1994e26195fSJason Gunthorpe EXPORT_SYMBOL_GPL(tpm_put_ops);
2004e26195fSJason Gunthorpe 
2014e26195fSJason Gunthorpe /**
202aaae8153SStefan Berger  * tpm_default_chip() - find a TPM chip and get a reference to it
203aaae8153SStefan Berger  */
tpm_default_chip(void)204aaae8153SStefan Berger struct tpm_chip *tpm_default_chip(void)
205aaae8153SStefan Berger {
206aaae8153SStefan Berger 	struct tpm_chip *chip, *res = NULL;
207aaae8153SStefan Berger 	int chip_num = 0;
208aaae8153SStefan Berger 	int chip_prev;
209aaae8153SStefan Berger 
210aaae8153SStefan Berger 	mutex_lock(&idr_lock);
211aaae8153SStefan Berger 
212aaae8153SStefan Berger 	do {
213aaae8153SStefan Berger 		chip_prev = chip_num;
214aaae8153SStefan Berger 		chip = idr_get_next(&dev_nums_idr, &chip_num);
215aaae8153SStefan Berger 		if (chip) {
216aaae8153SStefan Berger 			get_device(&chip->dev);
217aaae8153SStefan Berger 			res = chip;
218aaae8153SStefan Berger 			break;
219aaae8153SStefan Berger 		}
220aaae8153SStefan Berger 	} while (chip_prev != chip_num);
221aaae8153SStefan Berger 
222aaae8153SStefan Berger 	mutex_unlock(&idr_lock);
223aaae8153SStefan Berger 
224aaae8153SStefan Berger 	return res;
225aaae8153SStefan Berger }
226aaae8153SStefan Berger EXPORT_SYMBOL_GPL(tpm_default_chip);
227aaae8153SStefan Berger 
228aaae8153SStefan Berger /**
229fc1d52b7SStefan Berger  * tpm_find_get_ops() - find and reserve a TPM chip
230aad887f6SJarkko Sakkinen  * @chip:	a &struct tpm_chip instance, %NULL for the default chip
2314e26195fSJason Gunthorpe  *
232aad887f6SJarkko Sakkinen  * Finds a TPM chip and reserves its class device and operations. The chip must
233fc1d52b7SStefan Berger  * be released with tpm_put_ops() after use.
234fc1d52b7SStefan Berger  * This function is for internal use only. It supports existing TPM callers
235fc1d52b7SStefan Berger  * by accepting NULL, but those callers should be converted to pass in a chip
236fc1d52b7SStefan Berger  * directly.
237aad887f6SJarkko Sakkinen  *
238aad887f6SJarkko Sakkinen  * Return:
239aad887f6SJarkko Sakkinen  * A reserved &struct tpm_chip instance.
240aad887f6SJarkko Sakkinen  * %NULL if a chip is not found.
241aad887f6SJarkko Sakkinen  * %NULL if the chip is not available.
242afb5abc2SJarkko Sakkinen  */
tpm_find_get_ops(struct tpm_chip * chip)243fc1d52b7SStefan Berger struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip)
244afb5abc2SJarkko Sakkinen {
245eccc9bb8SStefan Berger 	int rc;
246afb5abc2SJarkko Sakkinen 
247eccc9bb8SStefan Berger 	if (chip) {
248aad887f6SJarkko Sakkinen 		if (!tpm_try_get_ops(chip))
249eccc9bb8SStefan Berger 			return chip;
250eccc9bb8SStefan Berger 		return NULL;
25115516788SStefan Berger 	}
25215516788SStefan Berger 
253eccc9bb8SStefan Berger 	chip = tpm_default_chip();
254eccc9bb8SStefan Berger 	if (!chip)
255eccc9bb8SStefan Berger 		return NULL;
256eccc9bb8SStefan Berger 	rc = tpm_try_get_ops(chip);
257eccc9bb8SStefan Berger 	/* release additional reference we got from tpm_default_chip() */
258eccc9bb8SStefan Berger 	put_device(&chip->dev);
259eccc9bb8SStefan Berger 	if (rc)
260eccc9bb8SStefan Berger 		return NULL;
261eccc9bb8SStefan Berger 	return chip;
262afb5abc2SJarkko Sakkinen }
263afb5abc2SJarkko Sakkinen 
264afb5abc2SJarkko Sakkinen /**
265313d21eeSJarkko Sakkinen  * tpm_dev_release() - free chip memory and the device number
266313d21eeSJarkko Sakkinen  * @dev: the character device for the TPM chip
267afb5abc2SJarkko Sakkinen  *
268313d21eeSJarkko Sakkinen  * This is used as the release function for the character device.
269afb5abc2SJarkko Sakkinen  */
tpm_dev_release(struct device * dev)270313d21eeSJarkko Sakkinen static void tpm_dev_release(struct device *dev)
271afb5abc2SJarkko Sakkinen {
272313d21eeSJarkko Sakkinen 	struct tpm_chip *chip = container_of(dev, struct tpm_chip, dev);
273afb5abc2SJarkko Sakkinen 
27415516788SStefan Berger 	mutex_lock(&idr_lock);
27515516788SStefan Berger 	idr_remove(&dev_nums_idr, chip->dev_num);
27615516788SStefan Berger 	mutex_unlock(&idr_lock);
27715516788SStefan Berger 
278745b361eSJarkko Sakkinen 	kfree(chip->work_space.context_buf);
2794d57856aSJames Bottomley 	kfree(chip->work_space.session_buf);
280bcfff838SRoberto Sassu 	kfree(chip->allocated_banks);
281699e3efdSJames Bottomley #ifdef CONFIG_TCG_TPM2_HMAC
282699e3efdSJames Bottomley 	kfree(chip->auth);
283699e3efdSJames Bottomley #endif
284afb5abc2SJarkko Sakkinen 	kfree(chip);
285afb5abc2SJarkko Sakkinen }
286afb5abc2SJarkko Sakkinen 
287afb5abc2SJarkko Sakkinen /**
288d1bd4a79SJosh Zimmerman  * tpm_class_shutdown() - prepare the TPM device for loss of power.
289d1bd4a79SJosh Zimmerman  * @dev: device to which the chip is associated.
290d1bd4a79SJosh Zimmerman  *
291d1bd4a79SJosh Zimmerman  * Issues a TPM2_Shutdown command prior to loss of power, as required by the
29228eba2fdSJarkko Sakkinen  * TPM 2.0 spec. Then, calls bus- and device- specific shutdown code.
293d1bd4a79SJosh Zimmerman  *
29428eba2fdSJarkko Sakkinen  * Return: always 0 (i.e. success)
295d1bd4a79SJosh Zimmerman  */
tpm_class_shutdown(struct device * dev)296a010eb88SGreg Kroah-Hartman int tpm_class_shutdown(struct device *dev)
297d1bd4a79SJosh Zimmerman {
298d1bd4a79SJosh Zimmerman 	struct tpm_chip *chip = container_of(dev, struct tpm_chip, dev);
299d1bd4a79SJosh Zimmerman 
300d1bd4a79SJosh Zimmerman 	down_write(&chip->ops_sem);
301db4d8cb9SVadim Sukhomlinov 	if (chip->flags & TPM_CHIP_FLAG_TPM2) {
30247a6c28bSJarkko Sakkinen 		if (!tpm_chip_start(chip)) {
303d1bd4a79SJosh Zimmerman 			tpm2_shutdown(chip, TPM2_SU_CLEAR);
30447a6c28bSJarkko Sakkinen 			tpm_chip_stop(chip);
305a3fbfae8SJarkko Sakkinen 		}
306db4d8cb9SVadim Sukhomlinov 	}
307d1bd4a79SJosh Zimmerman 	chip->ops = NULL;
308d1bd4a79SJosh Zimmerman 	up_write(&chip->ops_sem);
3097521621eSMichal Suchanek 
310d1bd4a79SJosh Zimmerman 	return 0;
311d1bd4a79SJosh Zimmerman }
312d1bd4a79SJosh Zimmerman 
313d1bd4a79SJosh Zimmerman /**
3143897cd9cSJason Gunthorpe  * tpm_chip_alloc() - allocate a new struct tpm_chip instance
3153897cd9cSJason Gunthorpe  * @pdev: device to which the chip is associated
3163897cd9cSJason Gunthorpe  *        At this point pdev mst be initialized, but does not have to
3173897cd9cSJason Gunthorpe  *        be registered
318afb5abc2SJarkko Sakkinen  * @ops: struct tpm_class_ops instance
319afb5abc2SJarkko Sakkinen  *
320afb5abc2SJarkko Sakkinen  * Allocates a new struct tpm_chip instance and assigns a free
3213897cd9cSJason Gunthorpe  * device number for it. Must be paired with put_device(&chip->dev).
322afb5abc2SJarkko Sakkinen  */
tpm_chip_alloc(struct device * pdev,const struct tpm_class_ops * ops)3232998b02bSWinkler, Tomas struct tpm_chip *tpm_chip_alloc(struct device *pdev,
324afb5abc2SJarkko Sakkinen 				const struct tpm_class_ops *ops)
325afb5abc2SJarkko Sakkinen {
326afb5abc2SJarkko Sakkinen 	struct tpm_chip *chip;
3274f3b193dSJarkko Sakkinen 	int rc;
328afb5abc2SJarkko Sakkinen 
329afb5abc2SJarkko Sakkinen 	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
330afb5abc2SJarkko Sakkinen 	if (chip == NULL)
331afb5abc2SJarkko Sakkinen 		return ERR_PTR(-ENOMEM);
332afb5abc2SJarkko Sakkinen 
333afb5abc2SJarkko Sakkinen 	mutex_init(&chip->tpm_mutex);
3344e26195fSJason Gunthorpe 	init_rwsem(&chip->ops_sem);
335afb5abc2SJarkko Sakkinen 
336afb5abc2SJarkko Sakkinen 	chip->ops = ops;
337afb5abc2SJarkko Sakkinen 
33815516788SStefan Berger 	mutex_lock(&idr_lock);
33915516788SStefan Berger 	rc = idr_alloc(&dev_nums_idr, NULL, 0, TPM_NUM_DEVICES, GFP_KERNEL);
34015516788SStefan Berger 	mutex_unlock(&idr_lock);
34115516788SStefan Berger 	if (rc < 0) {
3422998b02bSWinkler, Tomas 		dev_err(pdev, "No available tpm device numbers\n");
343afb5abc2SJarkko Sakkinen 		kfree(chip);
34415516788SStefan Berger 		return ERR_PTR(rc);
345afb5abc2SJarkko Sakkinen 	}
34615516788SStefan Berger 	chip->dev_num = rc;
347afb5abc2SJarkko Sakkinen 
3483635e2ecSJason Gunthorpe 	device_initialize(&chip->dev);
349afb5abc2SJarkko Sakkinen 
350d2e8071bSIvan Orlov 	chip->dev.class = &tpm_class;
351313d21eeSJarkko Sakkinen 	chip->dev.release = tpm_dev_release;
3522998b02bSWinkler, Tomas 	chip->dev.parent = pdev;
3539b774d5cSJarkko Sakkinen 	chip->dev.groups = chip->groups;
354313d21eeSJarkko Sakkinen 
355313d21eeSJarkko Sakkinen 	if (chip->dev_num == 0)
356313d21eeSJarkko Sakkinen 		chip->dev.devt = MKDEV(MISC_MAJOR, TPM_MINOR);
357313d21eeSJarkko Sakkinen 	else
358313d21eeSJarkko Sakkinen 		chip->dev.devt = MKDEV(MAJOR(tpm_devt), chip->dev_num);
359313d21eeSJarkko Sakkinen 
3603635e2ecSJason Gunthorpe 	rc = dev_set_name(&chip->dev, "tpm%d", chip->dev_num);
3613635e2ecSJason Gunthorpe 	if (rc)
3623635e2ecSJason Gunthorpe 		goto out;
363313d21eeSJarkko Sakkinen 
3642998b02bSWinkler, Tomas 	if (!pdev)
3652f9f5377SStefan Berger 		chip->flags |= TPM_CHIP_FLAG_VIRTUAL;
3662f9f5377SStefan Berger 
367313d21eeSJarkko Sakkinen 	cdev_init(&chip->cdev, &tpm_fops);
3682072df40SStefan Berger 	chip->cdev.owner = THIS_MODULE;
369313d21eeSJarkko Sakkinen 
3706c4e79d9SJarkko Sakkinen 	rc = tpm2_init_space(&chip->work_space, TPM2_SPACE_BUFFER_SIZE);
3716c4e79d9SJarkko Sakkinen 	if (rc) {
3724d57856aSJames Bottomley 		rc = -ENOMEM;
3734d57856aSJames Bottomley 		goto out;
3744d57856aSJames Bottomley 	}
375745b361eSJarkko Sakkinen 
376877c57d0SJarkko Sakkinen 	chip->locality = -1;
377afb5abc2SJarkko Sakkinen 	return chip;
3783635e2ecSJason Gunthorpe 
3793635e2ecSJason Gunthorpe out:
3803635e2ecSJason Gunthorpe 	put_device(&chip->dev);
3813635e2ecSJason Gunthorpe 	return ERR_PTR(rc);
382afb5abc2SJarkko Sakkinen }
3833897cd9cSJason Gunthorpe EXPORT_SYMBOL_GPL(tpm_chip_alloc);
3843897cd9cSJason Gunthorpe 
tpm_put_device(void * dev)385e10de46bSArd Biesheuvel static void tpm_put_device(void *dev)
386e10de46bSArd Biesheuvel {
387e10de46bSArd Biesheuvel 	put_device(dev);
388e10de46bSArd Biesheuvel }
389e10de46bSArd Biesheuvel 
3903897cd9cSJason Gunthorpe /**
3913897cd9cSJason Gunthorpe  * tpmm_chip_alloc() - allocate a new struct tpm_chip instance
3923897cd9cSJason Gunthorpe  * @pdev: parent device to which the chip is associated
3933897cd9cSJason Gunthorpe  * @ops: struct tpm_class_ops instance
3943897cd9cSJason Gunthorpe  *
3953897cd9cSJason Gunthorpe  * Same as tpm_chip_alloc except devm is used to do the put_device
3963897cd9cSJason Gunthorpe  */
tpmm_chip_alloc(struct device * pdev,const struct tpm_class_ops * ops)3973897cd9cSJason Gunthorpe struct tpm_chip *tpmm_chip_alloc(struct device *pdev,
3983897cd9cSJason Gunthorpe 				 const struct tpm_class_ops *ops)
3993897cd9cSJason Gunthorpe {
4003897cd9cSJason Gunthorpe 	struct tpm_chip *chip;
4013897cd9cSJason Gunthorpe 	int rc;
4023897cd9cSJason Gunthorpe 
4033897cd9cSJason Gunthorpe 	chip = tpm_chip_alloc(pdev, ops);
4043897cd9cSJason Gunthorpe 	if (IS_ERR(chip))
4053897cd9cSJason Gunthorpe 		return chip;
4063897cd9cSJason Gunthorpe 
4072b88cd96SSudip Mukherjee 	rc = devm_add_action_or_reset(pdev,
408e10de46bSArd Biesheuvel 				      tpm_put_device,
4092b88cd96SSudip Mukherjee 				      &chip->dev);
4102b88cd96SSudip Mukherjee 	if (rc)
4113897cd9cSJason Gunthorpe 		return ERR_PTR(rc);
4123897cd9cSJason Gunthorpe 
4133897cd9cSJason Gunthorpe 	dev_set_drvdata(pdev, chip);
4143897cd9cSJason Gunthorpe 
4153897cd9cSJason Gunthorpe 	return chip;
4163897cd9cSJason Gunthorpe }
417afb5abc2SJarkko Sakkinen EXPORT_SYMBOL_GPL(tpmm_chip_alloc);
418afb5abc2SJarkko Sakkinen 
tpm_add_char_device(struct tpm_chip * chip)41972c91ce8SJarkko Sakkinen static int tpm_add_char_device(struct tpm_chip *chip)
420313d21eeSJarkko Sakkinen {
421313d21eeSJarkko Sakkinen 	int rc;
422313d21eeSJarkko Sakkinen 
4238dbbf582SLogan Gunthorpe 	rc = cdev_device_add(&chip->cdev, &chip->dev);
424313d21eeSJarkko Sakkinen 	if (rc) {
425313d21eeSJarkko Sakkinen 		dev_err(&chip->dev,
4268dbbf582SLogan Gunthorpe 			"unable to cdev_device_add() %s, major %d, minor %d, err=%d\n",
4273635e2ecSJason Gunthorpe 			dev_name(&chip->dev), MAJOR(chip->dev.devt),
428313d21eeSJarkko Sakkinen 			MINOR(chip->dev.devt), rc);
429313d21eeSJarkko Sakkinen 		return rc;
430313d21eeSJarkko Sakkinen 	}
431313d21eeSJarkko Sakkinen 
4320aa69878Saxelj 	if (chip->flags & TPM_CHIP_FLAG_TPM2 && !tpm_is_firmware_upgrade(chip)) {
4337e0438f8SLino Sanfilippo 		rc = tpm_devs_add(chip);
4347e0438f8SLino Sanfilippo 		if (rc)
4357e0438f8SLino Sanfilippo 			goto err_del_cdev;
436fdc915f7SJames Bottomley 	}
437fdc915f7SJames Bottomley 
43815516788SStefan Berger 	/* Make the chip available. */
43915516788SStefan Berger 	mutex_lock(&idr_lock);
44015516788SStefan Berger 	idr_replace(&dev_nums_idr, chip, chip->dev_num);
44115516788SStefan Berger 	mutex_unlock(&idr_lock);
44215516788SStefan Berger 
4437e0438f8SLino Sanfilippo 	return 0;
4447e0438f8SLino Sanfilippo 
4457e0438f8SLino Sanfilippo err_del_cdev:
4467e0438f8SLino Sanfilippo 	cdev_device_del(&chip->cdev, &chip->dev);
447313d21eeSJarkko Sakkinen 	return rc;
448313d21eeSJarkko Sakkinen }
449313d21eeSJarkko Sakkinen 
tpm_del_char_device(struct tpm_chip * chip)45072c91ce8SJarkko Sakkinen static void tpm_del_char_device(struct tpm_chip *chip)
451313d21eeSJarkko Sakkinen {
4528dbbf582SLogan Gunthorpe 	cdev_device_del(&chip->cdev, &chip->dev);
45315516788SStefan Berger 
45415516788SStefan Berger 	/* Make the chip unavailable. */
45515516788SStefan Berger 	mutex_lock(&idr_lock);
45615516788SStefan Berger 	idr_replace(&dev_nums_idr, NULL, chip->dev_num);
45715516788SStefan Berger 	mutex_unlock(&idr_lock);
4584e26195fSJason Gunthorpe 
4594e26195fSJason Gunthorpe 	/* Make the driver uncallable. */
4604e26195fSJason Gunthorpe 	down_write(&chip->ops_sem);
461eabad7baSLino Sanfilippo 
462eabad7baSLino Sanfilippo 	/*
463eabad7baSLino Sanfilippo 	 * Check if chip->ops is still valid: In case that the controller
464eabad7baSLino Sanfilippo 	 * drivers shutdown handler unregisters the controller in its
465eabad7baSLino Sanfilippo 	 * shutdown handler we are called twice and chip->ops to NULL.
466eabad7baSLino Sanfilippo 	 */
467eabad7baSLino Sanfilippo 	if (chip->ops) {
468a3fbfae8SJarkko Sakkinen 		if (chip->flags & TPM_CHIP_FLAG_TPM2) {
46947a6c28bSJarkko Sakkinen 			if (!tpm_chip_start(chip)) {
470dcbeab19SJarkko Sakkinen 				tpm2_shutdown(chip, TPM2_SU_CLEAR);
47147a6c28bSJarkko Sakkinen 				tpm_chip_stop(chip);
472a3fbfae8SJarkko Sakkinen 			}
473a3fbfae8SJarkko Sakkinen 		}
4744e26195fSJason Gunthorpe 		chip->ops = NULL;
475eabad7baSLino Sanfilippo 	}
4764e26195fSJason Gunthorpe 	up_write(&chip->ops_sem);
477313d21eeSJarkko Sakkinen }
478313d21eeSJarkko Sakkinen 
tpm_del_legacy_sysfs(struct tpm_chip * chip)479062807f2SJason Gunthorpe static void tpm_del_legacy_sysfs(struct tpm_chip *chip)
480062807f2SJason Gunthorpe {
481062807f2SJason Gunthorpe 	struct attribute **i;
482062807f2SJason Gunthorpe 
4830aa69878Saxelj 	if (chip->flags & (TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL) ||
4840aa69878Saxelj 	    tpm_is_firmware_upgrade(chip))
485062807f2SJason Gunthorpe 		return;
486062807f2SJason Gunthorpe 
487062807f2SJason Gunthorpe 	sysfs_remove_link(&chip->dev.parent->kobj, "ppi");
488062807f2SJason Gunthorpe 
489062807f2SJason Gunthorpe 	for (i = chip->groups[0]->attrs; *i != NULL; ++i)
490062807f2SJason Gunthorpe 		sysfs_remove_link(&chip->dev.parent->kobj, (*i)->name);
491062807f2SJason Gunthorpe }
492062807f2SJason Gunthorpe 
493062807f2SJason Gunthorpe /* For compatibility with legacy sysfs paths we provide symlinks from the
494062807f2SJason Gunthorpe  * parent dev directory to selected names within the tpm chip directory. Old
495062807f2SJason Gunthorpe  * kernel versions created these files directly under the parent.
496062807f2SJason Gunthorpe  */
tpm_add_legacy_sysfs(struct tpm_chip * chip)497062807f2SJason Gunthorpe static int tpm_add_legacy_sysfs(struct tpm_chip *chip)
498062807f2SJason Gunthorpe {
499062807f2SJason Gunthorpe 	struct attribute **i;
500062807f2SJason Gunthorpe 	int rc;
501062807f2SJason Gunthorpe 
5020aa69878Saxelj 	if (chip->flags & (TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL) ||
5030aa69878Saxelj 		tpm_is_firmware_upgrade(chip))
504062807f2SJason Gunthorpe 		return 0;
505062807f2SJason Gunthorpe 
50670fbdfefSLinus Torvalds 	rc = compat_only_sysfs_link_entry_to_kobj(
50770fbdfefSLinus Torvalds 		    &chip->dev.parent->kobj, &chip->dev.kobj, "ppi", NULL);
508062807f2SJason Gunthorpe 	if (rc && rc != -ENOENT)
509062807f2SJason Gunthorpe 		return rc;
510062807f2SJason Gunthorpe 
511062807f2SJason Gunthorpe 	/* All the names from tpm-sysfs */
512062807f2SJason Gunthorpe 	for (i = chip->groups[0]->attrs; *i != NULL; ++i) {
51370fbdfefSLinus Torvalds 		rc = compat_only_sysfs_link_entry_to_kobj(
51470fbdfefSLinus Torvalds 		    &chip->dev.parent->kobj, &chip->dev.kobj, (*i)->name, NULL);
515062807f2SJason Gunthorpe 		if (rc) {
516062807f2SJason Gunthorpe 			tpm_del_legacy_sysfs(chip);
517062807f2SJason Gunthorpe 			return rc;
518062807f2SJason Gunthorpe 		}
519062807f2SJason Gunthorpe 	}
520062807f2SJason Gunthorpe 
521062807f2SJason Gunthorpe 	return 0;
522062807f2SJason Gunthorpe }
5236e592a06SJason Gunthorpe 
tpm_hwrng_read(struct hwrng * rng,void * data,size_t max,bool wait)5246e592a06SJason Gunthorpe static int tpm_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait)
5256e592a06SJason Gunthorpe {
5266e592a06SJason Gunthorpe 	struct tpm_chip *chip = container_of(rng, struct tpm_chip, hwrng);
5276e592a06SJason Gunthorpe 
5286e592a06SJason Gunthorpe 	return tpm_get_random(chip, data, max);
5296e592a06SJason Gunthorpe }
5306e592a06SJason Gunthorpe 
tpm_is_hwrng_enabled(struct tpm_chip * chip)531cacc6e22SMario Limonciello static bool tpm_is_hwrng_enabled(struct tpm_chip *chip)
532cacc6e22SMario Limonciello {
533cacc6e22SMario Limonciello 	if (!IS_ENABLED(CONFIG_HW_RANDOM_TPM))
534cacc6e22SMario Limonciello 		return false;
535cacc6e22SMario Limonciello 	if (tpm_is_firmware_upgrade(chip))
536cacc6e22SMario Limonciello 		return false;
537cacc6e22SMario Limonciello 	if (chip->flags & TPM_CHIP_FLAG_HWRNG_DISABLED)
538cacc6e22SMario Limonciello 		return false;
539cacc6e22SMario Limonciello 	return true;
540cacc6e22SMario Limonciello }
541cacc6e22SMario Limonciello 
tpm_add_hwrng(struct tpm_chip * chip)5426e592a06SJason Gunthorpe static int tpm_add_hwrng(struct tpm_chip *chip)
5436e592a06SJason Gunthorpe {
544cacc6e22SMario Limonciello 	if (!tpm_is_hwrng_enabled(chip))
5456e592a06SJason Gunthorpe 		return 0;
5466e592a06SJason Gunthorpe 
5476e592a06SJason Gunthorpe 	snprintf(chip->hwrng_name, sizeof(chip->hwrng_name),
5486e592a06SJason Gunthorpe 		 "tpm-rng-%d", chip->dev_num);
5496e592a06SJason Gunthorpe 	chip->hwrng.name = chip->hwrng_name;
5506e592a06SJason Gunthorpe 	chip->hwrng.read = tpm_hwrng_read;
5516e592a06SJason Gunthorpe 	return hwrng_register(&chip->hwrng);
5526e592a06SJason Gunthorpe }
5536e592a06SJason Gunthorpe 
tpm_get_pcr_allocation(struct tpm_chip * chip)554fa4f99c0SNayna Jain static int tpm_get_pcr_allocation(struct tpm_chip *chip)
555fa4f99c0SNayna Jain {
556fa4f99c0SNayna Jain 	int rc;
557fa4f99c0SNayna Jain 
5580aa69878Saxelj 	if (tpm_is_firmware_upgrade(chip))
5590aa69878Saxelj 		return 0;
5600aa69878Saxelj 
561fa4f99c0SNayna Jain 	rc = (chip->flags & TPM_CHIP_FLAG_TPM2) ?
562fa4f99c0SNayna Jain 	     tpm2_get_pcr_allocation(chip) :
563fa4f99c0SNayna Jain 	     tpm1_get_pcr_allocation(chip);
564fa4f99c0SNayna Jain 
565fa4f99c0SNayna Jain 	if (rc > 0)
566fa4f99c0SNayna Jain 		return -ENODEV;
567fa4f99c0SNayna Jain 
568fa4f99c0SNayna Jain 	return rc;
569fa4f99c0SNayna Jain }
570fa4f99c0SNayna Jain 
571afb5abc2SJarkko Sakkinen /*
5720c8862deSJarkko Sakkinen  * tpm_chip_bootstrap() - Boostrap TPM chip after power on
573548eb516SLino Sanfilippo  * @chip: TPM chip to use.
5740c8862deSJarkko Sakkinen  *
5750c8862deSJarkko Sakkinen  * Initialize TPM chip after power on. This a one-shot function: subsequent
5760c8862deSJarkko Sakkinen  * calls will have no effect.
577548eb516SLino Sanfilippo  */
tpm_chip_bootstrap(struct tpm_chip * chip)5780c8862deSJarkko Sakkinen int tpm_chip_bootstrap(struct tpm_chip *chip)
579548eb516SLino Sanfilippo {
580548eb516SLino Sanfilippo 	int rc;
581548eb516SLino Sanfilippo 
5820c8862deSJarkko Sakkinen 	if (chip->flags & TPM_CHIP_FLAG_BOOTSTRAPPED)
5830c8862deSJarkko Sakkinen 		return 0;
5840c8862deSJarkko Sakkinen 
585548eb516SLino Sanfilippo 	rc = tpm_chip_start(chip);
586548eb516SLino Sanfilippo 	if (rc)
587548eb516SLino Sanfilippo 		return rc;
588548eb516SLino Sanfilippo 
589548eb516SLino Sanfilippo 	rc = tpm_auto_startup(chip);
590548eb516SLino Sanfilippo 	if (rc)
591548eb516SLino Sanfilippo 		goto stop;
592548eb516SLino Sanfilippo 
593548eb516SLino Sanfilippo 	rc = tpm_get_pcr_allocation(chip);
594548eb516SLino Sanfilippo stop:
595548eb516SLino Sanfilippo 	tpm_chip_stop(chip);
596548eb516SLino Sanfilippo 
5970c8862deSJarkko Sakkinen 	/*
5980c8862deSJarkko Sakkinen 	 * Unconditionally set, as driver initialization should cease, when the
5990c8862deSJarkko Sakkinen 	 * boostrapping process fails.
6000c8862deSJarkko Sakkinen 	 */
6010c8862deSJarkko Sakkinen 	chip->flags |= TPM_CHIP_FLAG_BOOTSTRAPPED;
6020c8862deSJarkko Sakkinen 
603548eb516SLino Sanfilippo 	return rc;
604548eb516SLino Sanfilippo }
6050c8862deSJarkko Sakkinen EXPORT_SYMBOL_GPL(tpm_chip_bootstrap);
606548eb516SLino Sanfilippo 
607548eb516SLino Sanfilippo /*
608313d21eeSJarkko Sakkinen  * tpm_chip_register() - create a character device for the TPM chip
609afb5abc2SJarkko Sakkinen  * @chip: TPM chip to use.
610afb5abc2SJarkko Sakkinen  *
611d972b052SJarkko Sakkinen  * Creates a character device for the TPM chip and adds sysfs attributes for
612d972b052SJarkko Sakkinen  * the device. As the last step this function adds the chip to the list of TPM
613d972b052SJarkko Sakkinen  * chips available for in-kernel use.
614afb5abc2SJarkko Sakkinen  *
615d972b052SJarkko Sakkinen  * This function should be only called after the chip initialization is
616d972b052SJarkko Sakkinen  * complete.
617afb5abc2SJarkko Sakkinen  */
tpm_chip_register(struct tpm_chip * chip)618afb5abc2SJarkko Sakkinen int tpm_chip_register(struct tpm_chip *chip)
619afb5abc2SJarkko Sakkinen {
620afb5abc2SJarkko Sakkinen 	int rc;
621afb5abc2SJarkko Sakkinen 
6220c8862deSJarkko Sakkinen 	rc = tpm_chip_bootstrap(chip);
6230c8862deSJarkko Sakkinen 	if (rc)
6240c8862deSJarkko Sakkinen 		return rc;
6250c8862deSJarkko Sakkinen 
6267518a21aSJarkko Sakkinen 	tpm_sysfs_add_device(chip);
6277518a21aSJarkko Sakkinen 
628805fa88eSMatthew Garrett 	tpm_bios_log_setup(chip);
629afb5abc2SJarkko Sakkinen 
6309b774d5cSJarkko Sakkinen 	tpm_add_ppi(chip);
6319b774d5cSJarkko Sakkinen 
6326e592a06SJason Gunthorpe 	rc = tpm_add_hwrng(chip);
6336e592a06SJason Gunthorpe 	if (rc)
6346e592a06SJason Gunthorpe 		goto out_ppi;
6356e592a06SJason Gunthorpe 
63672c91ce8SJarkko Sakkinen 	rc = tpm_add_char_device(chip);
6376e592a06SJason Gunthorpe 	if (rc)
6386e592a06SJason Gunthorpe 		goto out_hwrng;
639d972b052SJarkko Sakkinen 
640062807f2SJason Gunthorpe 	rc = tpm_add_legacy_sysfs(chip);
641062807f2SJason Gunthorpe 	if (rc) {
642d56e4f75SJarkko Sakkinen 		tpm_chip_unregister(chip);
643d56e4f75SJarkko Sakkinen 		return rc;
644d56e4f75SJarkko Sakkinen 	}
645d56e4f75SJarkko Sakkinen 
646afb5abc2SJarkko Sakkinen 	return 0;
6476e592a06SJason Gunthorpe 
6486e592a06SJason Gunthorpe out_hwrng:
649cacc6e22SMario Limonciello 	if (tpm_is_hwrng_enabled(chip))
6506e592a06SJason Gunthorpe 		hwrng_unregister(&chip->hwrng);
6516e592a06SJason Gunthorpe out_ppi:
6526e592a06SJason Gunthorpe 	tpm_bios_log_teardown(chip);
6536e592a06SJason Gunthorpe 
6546e592a06SJason Gunthorpe 	return rc;
655afb5abc2SJarkko Sakkinen }
656afb5abc2SJarkko Sakkinen EXPORT_SYMBOL_GPL(tpm_chip_register);
657afb5abc2SJarkko Sakkinen 
658afb5abc2SJarkko Sakkinen /*
659afb5abc2SJarkko Sakkinen  * tpm_chip_unregister() - release the TPM driver
660afb5abc2SJarkko Sakkinen  * @chip: TPM chip to use.
661afb5abc2SJarkko Sakkinen  *
662afb5abc2SJarkko Sakkinen  * Takes the chip first away from the list of available TPM chips and then
663afb5abc2SJarkko Sakkinen  * cleans up all the resources reserved by tpm_chip_register().
664afb5abc2SJarkko Sakkinen  *
6654e26195fSJason Gunthorpe  * Once this function returns the driver call backs in 'op's will not be
6664e26195fSJason Gunthorpe  * running and will no longer start.
6674e26195fSJason Gunthorpe  *
668afb5abc2SJarkko Sakkinen  * NOTE: This function should be only called before deinitializing chip
669afb5abc2SJarkko Sakkinen  * resources.
670afb5abc2SJarkko Sakkinen  */
tpm_chip_unregister(struct tpm_chip * chip)671afb5abc2SJarkko Sakkinen void tpm_chip_unregister(struct tpm_chip *chip)
672afb5abc2SJarkko Sakkinen {
673*df745e25SJarkko Sakkinen #ifdef CONFIG_TCG_TPM2_HMAC
674*df745e25SJarkko Sakkinen 	int rc;
675*df745e25SJarkko Sakkinen 
676*df745e25SJarkko Sakkinen 	rc = tpm_try_get_ops(chip);
677*df745e25SJarkko Sakkinen 	if (!rc) {
678*df745e25SJarkko Sakkinen 		tpm2_end_auth_session(chip);
679*df745e25SJarkko Sakkinen 		tpm_put_ops(chip);
680*df745e25SJarkko Sakkinen 	}
681*df745e25SJarkko Sakkinen #endif
682*df745e25SJarkko Sakkinen 
683062807f2SJason Gunthorpe 	tpm_del_legacy_sysfs(chip);
684cacc6e22SMario Limonciello 	if (tpm_is_hwrng_enabled(chip))
6856e592a06SJason Gunthorpe 		hwrng_unregister(&chip->hwrng);
6867518a21aSJarkko Sakkinen 	tpm_bios_log_teardown(chip);
6870aa69878Saxelj 	if (chip->flags & TPM_CHIP_FLAG_TPM2 && !tpm_is_firmware_upgrade(chip))
6887e0438f8SLino Sanfilippo 		tpm_devs_remove(chip);
68972c91ce8SJarkko Sakkinen 	tpm_del_char_device(chip);
690afb5abc2SJarkko Sakkinen }
691afb5abc2SJarkko Sakkinen EXPORT_SYMBOL_GPL(tpm_chip_unregister);
692