127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn/*
227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn * Copyright (C) 2005, 2006 IBM Corporation
327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn *
427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn * Authors:
527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn * Leendert van Doorn <leendert@watson.ibm.com>
627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn * Kylene Hall <kjhall@us.ibm.com>
727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn *
88e81cc13a88ce486a6b0a6ca56aba6985824917aKent Yoder * Maintained by: <tpmdd-devel@lists.sourceforge.net>
98e81cc13a88ce486a6b0a6ca56aba6985824917aKent Yoder *
1027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn * Device driver for TCG/TCPA TPM (trusted platform module).
1127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn * Specifications at www.trustedcomputinggroup.org
1227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn *
1327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn * This device driver implements the TPM interface as defined in
1427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn * the TCG TPM Interface Spec version 1.2, revision 1.0.
1527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn *
1627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn * This program is free software; you can redistribute it and/or
1727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn * modify it under the terms of the GNU General Public License as
1827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn * published by the Free Software Foundation, version 2 of the
1927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn * License.
2027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn */
215713556843aee24f484f445db6540f9fef976439Kylene Jo Hall#include <linux/init.h>
225713556843aee24f484f445db6540f9fef976439Kylene Jo Hall#include <linux/module.h>
235713556843aee24f484f445db6540f9fef976439Kylene Jo Hall#include <linux/moduleparam.h>
2427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn#include <linux/pnp.h>
255a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
2627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn#include <linux/interrupt.h>
2727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn#include <linux/wait.h>
283f0d3d016d89a5efb8b926d4707eb21fa13f3d27Matthew Garrett#include <linux/acpi.h>
2920b87bbfada971ae917cc2ff9dbc9dae05b94d25Stefan Berger#include <linux/freezer.h>
3027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn#include "tpm.h"
3127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
3227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornenum tis_access {
3327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	TPM_ACCESS_VALID = 0x80,
3427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
3527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	TPM_ACCESS_REQUEST_PENDING = 0x04,
3627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	TPM_ACCESS_REQUEST_USE = 0x02,
3727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn};
3827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
3927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornenum tis_status {
4027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	TPM_STS_VALID = 0x80,
4127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	TPM_STS_COMMAND_READY = 0x40,
4227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	TPM_STS_GO = 0x20,
4327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	TPM_STS_DATA_AVAIL = 0x10,
4427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	TPM_STS_DATA_EXPECT = 0x08,
4527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn};
4627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
4727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornenum tis_int_flags {
4827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	TPM_GLOBAL_INT_ENABLE = 0x80000000,
4927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	TPM_INTF_BURST_COUNT_STATIC = 0x100,
5027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	TPM_INTF_CMD_READY_INT = 0x080,
5127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	TPM_INTF_INT_EDGE_FALLING = 0x040,
5227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	TPM_INTF_INT_EDGE_RISING = 0x020,
5327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	TPM_INTF_INT_LEVEL_LOW = 0x010,
5427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	TPM_INTF_INT_LEVEL_HIGH = 0x008,
5527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	TPM_INTF_LOCALITY_CHANGE_INT = 0x004,
5627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	TPM_INTF_STS_VALID_INT = 0x002,
5727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	TPM_INTF_DATA_AVAIL_INT = 0x001,
5827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn};
5927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
6036b20020e537036c4f9eb5b69140c88ead5da7dcKylene Jo Hallenum tis_defaults {
612a7362f52a17e8dbeab57c00c3c45fcfeb0dff54Kylene Jo Hall	TIS_MEM_BASE = 0xFED40000,
62b09d53009db21228adde29b468eb4583e66cbe7cKylene Jo Hall	TIS_MEM_LEN = 0x5000,
63cb5354253af2bc30ed449b8be4b3bddf3b3a2746Kylene Jo Hall	TIS_SHORT_TIMEOUT = 750,	/* ms */
64cb5354253af2bc30ed449b8be4b3bddf3b3a2746Kylene Jo Hall	TIS_LONG_TIMEOUT = 2000,	/* 2 sec */
6536b20020e537036c4f9eb5b69140c88ead5da7dcKylene Jo Hall};
6636b20020e537036c4f9eb5b69140c88ead5da7dcKylene Jo Hall
6727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn#define	TPM_ACCESS(l)			(0x0000 | ((l) << 12))
6827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn#define	TPM_INT_ENABLE(l)		(0x0008 | ((l) << 12))
6927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn#define	TPM_INT_VECTOR(l)		(0x000C | ((l) << 12))
7027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn#define	TPM_INT_STATUS(l)		(0x0010 | ((l) << 12))
7127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn#define	TPM_INTF_CAPS(l)		(0x0014 | ((l) << 12))
7227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn#define	TPM_STS(l)			(0x0018 | ((l) << 12))
7327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn#define	TPM_DATA_FIFO(l)		(0x0024 | ((l) << 12))
7427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
7527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn#define	TPM_DID_VID(l)			(0x0F00 | ((l) << 12))
7627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn#define	TPM_RID(l)			(0x0F04 | ((l) << 12))
7727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
7827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic LIST_HEAD(tis_chips);
794e70daaf05a181b6968e29e72e9f1c16a183e92cJiri Kosinastatic DEFINE_MUTEX(tis_lock);
8027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
811560ffe62a9d53a51faeec7417becfba4f2a0d18Randy Dunlap#if defined(CONFIG_PNP) && defined(CONFIG_ACPI)
823f0d3d016d89a5efb8b926d4707eb21fa13f3d27Matthew Garrettstatic int is_itpm(struct pnp_dev *dev)
833f0d3d016d89a5efb8b926d4707eb21fa13f3d27Matthew Garrett{
843f0d3d016d89a5efb8b926d4707eb21fa13f3d27Matthew Garrett	struct acpi_device *acpi = pnp_acpi_device(dev);
853f0d3d016d89a5efb8b926d4707eb21fa13f3d27Matthew Garrett	struct acpi_hardware_id *id;
863f0d3d016d89a5efb8b926d4707eb21fa13f3d27Matthew Garrett
873f0d3d016d89a5efb8b926d4707eb21fa13f3d27Matthew Garrett	list_for_each_entry(id, &acpi->pnp.ids, list) {
883f0d3d016d89a5efb8b926d4707eb21fa13f3d27Matthew Garrett		if (!strcmp("INTC0102", id->id))
893f0d3d016d89a5efb8b926d4707eb21fa13f3d27Matthew Garrett			return 1;
903f0d3d016d89a5efb8b926d4707eb21fa13f3d27Matthew Garrett	}
913f0d3d016d89a5efb8b926d4707eb21fa13f3d27Matthew Garrett
923f0d3d016d89a5efb8b926d4707eb21fa13f3d27Matthew Garrett	return 0;
933f0d3d016d89a5efb8b926d4707eb21fa13f3d27Matthew Garrett}
941560ffe62a9d53a51faeec7417becfba4f2a0d18Randy Dunlap#else
951560ffe62a9d53a51faeec7417becfba4f2a0d18Randy Dunlapstatic inline int is_itpm(struct pnp_dev *dev)
961560ffe62a9d53a51faeec7417becfba4f2a0d18Randy Dunlap{
971560ffe62a9d53a51faeec7417becfba4f2a0d18Randy Dunlap	return 0;
981560ffe62a9d53a51faeec7417becfba4f2a0d18Randy Dunlap}
993f0d3d016d89a5efb8b926d4707eb21fa13f3d27Matthew Garrett#endif
1003f0d3d016d89a5efb8b926d4707eb21fa13f3d27Matthew Garrett
10127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic int check_locality(struct tpm_chip *chip, int l)
10227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn{
10327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if ((ioread8(chip->vendor.iobase + TPM_ACCESS(l)) &
10427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	     (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
10527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	    (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
10627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		return chip->vendor.locality = l;
10727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
10827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	return -1;
10927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn}
11027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
11127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic void release_locality(struct tpm_chip *chip, int l, int force)
11227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn{
11327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (force || (ioread8(chip->vendor.iobase + TPM_ACCESS(l)) &
11427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		      (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ==
11527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	    (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID))
11627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		iowrite8(TPM_ACCESS_ACTIVE_LOCALITY,
11727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			 chip->vendor.iobase + TPM_ACCESS(l));
11827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn}
11927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
12027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic int request_locality(struct tpm_chip *chip, int l)
12127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn{
12220b87bbfada971ae917cc2ff9dbc9dae05b94d25Stefan Berger	unsigned long stop, timeout;
12327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	long rc;
12427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
12527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (check_locality(chip, l) >= 0)
12627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		return l;
12727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
12827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	iowrite8(TPM_ACCESS_REQUEST_USE,
12927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		 chip->vendor.iobase + TPM_ACCESS(l));
13027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
13120b87bbfada971ae917cc2ff9dbc9dae05b94d25Stefan Berger	stop = jiffies + chip->vendor.timeout_a;
13220b87bbfada971ae917cc2ff9dbc9dae05b94d25Stefan Berger
13327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (chip->vendor.irq) {
13420b87bbfada971ae917cc2ff9dbc9dae05b94d25Stefan Bergeragain:
13520b87bbfada971ae917cc2ff9dbc9dae05b94d25Stefan Berger		timeout = stop - jiffies;
13620b87bbfada971ae917cc2ff9dbc9dae05b94d25Stefan Berger		if ((long)timeout <= 0)
13720b87bbfada971ae917cc2ff9dbc9dae05b94d25Stefan Berger			return -1;
13836b20020e537036c4f9eb5b69140c88ead5da7dcKylene Jo Hall		rc = wait_event_interruptible_timeout(chip->vendor.int_queue,
13927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn						      (check_locality
14027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn						       (chip, l) >= 0),
14120b87bbfada971ae917cc2ff9dbc9dae05b94d25Stefan Berger						      timeout);
14227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		if (rc > 0)
14327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			return l;
14420b87bbfada971ae917cc2ff9dbc9dae05b94d25Stefan Berger		if (rc == -ERESTARTSYS && freezing(current)) {
14520b87bbfada971ae917cc2ff9dbc9dae05b94d25Stefan Berger			clear_thread_flag(TIF_SIGPENDING);
14620b87bbfada971ae917cc2ff9dbc9dae05b94d25Stefan Berger			goto again;
14720b87bbfada971ae917cc2ff9dbc9dae05b94d25Stefan Berger		}
14827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	} else {
14927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		/* wait for burstcount */
15027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		do {
15127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			if (check_locality(chip, l) >= 0)
15227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn				return l;
15327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			msleep(TPM_TIMEOUT);
15427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		}
15527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		while (time_before(jiffies, stop));
15627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	}
15727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	return -1;
15827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn}
15927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
16027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic u8 tpm_tis_status(struct tpm_chip *chip)
16127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn{
16227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	return ioread8(chip->vendor.iobase +
16327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		       TPM_STS(chip->vendor.locality));
16427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn}
16527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
16627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic void tpm_tis_ready(struct tpm_chip *chip)
16727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn{
16827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	/* this causes the current command to be aborted */
16927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	iowrite8(TPM_STS_COMMAND_READY,
17027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		 chip->vendor.iobase + TPM_STS(chip->vendor.locality));
17127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn}
17227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
17327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic int get_burstcount(struct tpm_chip *chip)
17427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn{
17527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	unsigned long stop;
17627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	int burstcnt;
17727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
17827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	/* wait for burstcount */
17927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	/* which timeout value, spec has 2 answers (c & d) */
18036b20020e537036c4f9eb5b69140c88ead5da7dcKylene Jo Hall	stop = jiffies + chip->vendor.timeout_d;
18127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	do {
18227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		burstcnt = ioread8(chip->vendor.iobase +
18327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn				   TPM_STS(chip->vendor.locality) + 1);
18427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		burstcnt += ioread8(chip->vendor.iobase +
18527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn				    TPM_STS(chip->vendor.locality) +
18627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn				    2) << 8;
18727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		if (burstcnt)
18827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			return burstcnt;
18927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		msleep(TPM_TIMEOUT);
19027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	} while (time_before(jiffies, stop));
19127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	return -EBUSY;
19227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn}
19327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
194cb5354253af2bc30ed449b8be4b3bddf3b3a2746Kylene Jo Hallstatic int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
19527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn{
19627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	int size = 0, burstcnt;
19727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	while (size < count &&
198fd04886660208ab2e35baaca55588afa57d52c9cRajiv Andrade	       wait_for_tpm_stat(chip,
199fd04886660208ab2e35baaca55588afa57d52c9cRajiv Andrade				 TPM_STS_DATA_AVAIL | TPM_STS_VALID,
200fd04886660208ab2e35baaca55588afa57d52c9cRajiv Andrade				 chip->vendor.timeout_c,
201fd04886660208ab2e35baaca55588afa57d52c9cRajiv Andrade				 &chip->vendor.read_queue)
20227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	       == 0) {
20327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		burstcnt = get_burstcount(chip);
20427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		for (; burstcnt > 0 && size < count; burstcnt--)
20527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			buf[size++] = ioread8(chip->vendor.iobase +
20627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn					      TPM_DATA_FIFO(chip->vendor.
20727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn							    locality));
20827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	}
20927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	return size;
21027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn}
21127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
212cb5354253af2bc30ed449b8be4b3bddf3b3a2746Kylene Jo Hallstatic int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
21327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn{
21427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	int size = 0;
21527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	int expected, status;
21627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
21727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (count < TPM_HEADER_SIZE) {
21827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		size = -EIO;
21927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		goto out;
22027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	}
22127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
22227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	/* read first 10 bytes, including tag, paramsize, and result */
22327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if ((size =
22427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	     recv_data(chip, buf, TPM_HEADER_SIZE)) < TPM_HEADER_SIZE) {
22527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		dev_err(chip->dev, "Unable to read header\n");
22627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		goto out;
22727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	}
22827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
22927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	expected = be32_to_cpu(*(__be32 *) (buf + 2));
23027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (expected > count) {
23127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		size = -EIO;
23227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		goto out;
23327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	}
23427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
23527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if ((size +=
23627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	     recv_data(chip, &buf[TPM_HEADER_SIZE],
23727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		       expected - TPM_HEADER_SIZE)) < expected) {
23827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		dev_err(chip->dev, "Unable to read remainder of result\n");
23927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		size = -ETIME;
24027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		goto out;
24127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	}
24227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
243fd04886660208ab2e35baaca55588afa57d52c9cRajiv Andrade	wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
244fd04886660208ab2e35baaca55588afa57d52c9cRajiv Andrade			  &chip->vendor.int_queue);
24527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	status = tpm_tis_status(chip);
24627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (status & TPM_STS_DATA_AVAIL) {	/* retry? */
24727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		dev_err(chip->dev, "Error left over data\n");
24827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		size = -EIO;
24927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		goto out;
25027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	}
25127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
25227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornout:
25327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	tpm_tis_ready(chip);
25427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	release_locality(chip, chip->vendor.locality, 0);
25527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	return size;
25627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn}
25727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
25890ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool itpm;
2593507d612366a4e81226295f646410130a1f62a5cRajiv Andrademodule_param(itpm, bool, 0444);
2603507d612366a4e81226295f646410130a1f62a5cRajiv AndradeMODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)");
2613507d612366a4e81226295f646410130a1f62a5cRajiv Andrade
26227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn/*
26327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn * If interrupts are used (signaled by an irq set in the vendor structure)
26427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn * tpm.c can skip polling for the data to be available as the interrupt is
26527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn * waited for here
26627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn */
2679519de3f265f112e992aa7f446d905196bd608e8Stefan Bergerstatic int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
26827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn{
26927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	int rc, status, burstcnt;
27027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	size_t count = 0;
27127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
27227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (request_locality(chip, 0) < 0)
27327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		return -EBUSY;
27427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
27527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	status = tpm_tis_status(chip);
27627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if ((status & TPM_STS_COMMAND_READY) == 0) {
27727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		tpm_tis_ready(chip);
278fd04886660208ab2e35baaca55588afa57d52c9cRajiv Andrade		if (wait_for_tpm_stat
27927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		    (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b,
28027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		     &chip->vendor.int_queue) < 0) {
28127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			rc = -ETIME;
28227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			goto out_err;
28327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		}
28427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	}
28527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
28627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	while (count < len - 1) {
28727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		burstcnt = get_burstcount(chip);
28827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		for (; burstcnt > 0 && count < len - 1; burstcnt--) {
28927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			iowrite8(buf[count], chip->vendor.iobase +
29027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn				 TPM_DATA_FIFO(chip->vendor.locality));
29127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			count++;
29227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		}
29327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
294fd04886660208ab2e35baaca55588afa57d52c9cRajiv Andrade		wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
295fd04886660208ab2e35baaca55588afa57d52c9cRajiv Andrade				  &chip->vendor.int_queue);
29627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		status = tpm_tis_status(chip);
2973507d612366a4e81226295f646410130a1f62a5cRajiv Andrade		if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) {
29827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			rc = -EIO;
29927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			goto out_err;
30027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		}
30127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	}
30227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
30327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	/* write last byte */
30427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	iowrite8(buf[count],
3059519de3f265f112e992aa7f446d905196bd608e8Stefan Berger		 chip->vendor.iobase + TPM_DATA_FIFO(chip->vendor.locality));
306fd04886660208ab2e35baaca55588afa57d52c9cRajiv Andrade	wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
307fd04886660208ab2e35baaca55588afa57d52c9cRajiv Andrade			  &chip->vendor.int_queue);
30827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	status = tpm_tis_status(chip);
30927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if ((status & TPM_STS_DATA_EXPECT) != 0) {
31027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		rc = -EIO;
31127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		goto out_err;
31227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	}
31327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
3149519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	return 0;
3159519de3f265f112e992aa7f446d905196bd608e8Stefan Berger
3169519de3f265f112e992aa7f446d905196bd608e8Stefan Bergerout_err:
3179519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	tpm_tis_ready(chip);
3189519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	release_locality(chip, chip->vendor.locality, 0);
3199519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	return rc;
3209519de3f265f112e992aa7f446d905196bd608e8Stefan Berger}
3219519de3f265f112e992aa7f446d905196bd608e8Stefan Berger
3229519de3f265f112e992aa7f446d905196bd608e8Stefan Berger/*
3239519de3f265f112e992aa7f446d905196bd608e8Stefan Berger * If interrupts are used (signaled by an irq set in the vendor structure)
3249519de3f265f112e992aa7f446d905196bd608e8Stefan Berger * tpm.c can skip polling for the data to be available as the interrupt is
3259519de3f265f112e992aa7f446d905196bd608e8Stefan Berger * waited for here
3269519de3f265f112e992aa7f446d905196bd608e8Stefan Berger */
3279519de3f265f112e992aa7f446d905196bd608e8Stefan Bergerstatic int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
3289519de3f265f112e992aa7f446d905196bd608e8Stefan Berger{
3299519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	int rc;
3309519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	u32 ordinal;
3319519de3f265f112e992aa7f446d905196bd608e8Stefan Berger
3329519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	rc = tpm_tis_send_data(chip, buf, len);
3339519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	if (rc < 0)
3349519de3f265f112e992aa7f446d905196bd608e8Stefan Berger		return rc;
3359519de3f265f112e992aa7f446d905196bd608e8Stefan Berger
33627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	/* go and do it */
33727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	iowrite8(TPM_STS_GO,
33827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		 chip->vendor.iobase + TPM_STS(chip->vendor.locality));
33927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
34027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (chip->vendor.irq) {
34127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
342fd04886660208ab2e35baaca55588afa57d52c9cRajiv Andrade		if (wait_for_tpm_stat
34327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		    (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
34427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		     tpm_calc_ordinal_duration(chip, ordinal),
34527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		     &chip->vendor.read_queue) < 0) {
34627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			rc = -ETIME;
34727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			goto out_err;
34827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		}
34927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	}
35027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	return len;
35127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornout_err:
35227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	tpm_tis_ready(chip);
35327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	release_locality(chip, chip->vendor.locality, 0);
35427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	return rc;
35527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn}
35627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
3579519de3f265f112e992aa7f446d905196bd608e8Stefan Berger/*
3589519de3f265f112e992aa7f446d905196bd608e8Stefan Berger * Early probing for iTPM with STS_DATA_EXPECT flaw.
3599519de3f265f112e992aa7f446d905196bd608e8Stefan Berger * Try sending command without itpm flag set and if that
3609519de3f265f112e992aa7f446d905196bd608e8Stefan Berger * fails, repeat with itpm flag set.
3619519de3f265f112e992aa7f446d905196bd608e8Stefan Berger */
3629519de3f265f112e992aa7f446d905196bd608e8Stefan Bergerstatic int probe_itpm(struct tpm_chip *chip)
3639519de3f265f112e992aa7f446d905196bd608e8Stefan Berger{
3649519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	int rc = 0;
3659519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	u8 cmd_getticks[] = {
3669519de3f265f112e992aa7f446d905196bd608e8Stefan Berger		0x00, 0xc1, 0x00, 0x00, 0x00, 0x0a,
3679519de3f265f112e992aa7f446d905196bd608e8Stefan Berger		0x00, 0x00, 0x00, 0xf1
3689519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	};
3699519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	size_t len = sizeof(cmd_getticks);
370968de8e24d08fcc425e112ca465d4688b89b777bStefan Berger	bool rem_itpm = itpm;
3714e401fb028b79105ed87d85fc2220c77be277ed9Stefan Berger	u16 vendor = ioread16(chip->vendor.iobase + TPM_DID_VID(0));
3724e401fb028b79105ed87d85fc2220c77be277ed9Stefan Berger
3734e401fb028b79105ed87d85fc2220c77be277ed9Stefan Berger	/* probe only iTPMS */
3744e401fb028b79105ed87d85fc2220c77be277ed9Stefan Berger	if (vendor != TPM_VID_INTEL)
3754e401fb028b79105ed87d85fc2220c77be277ed9Stefan Berger		return 0;
3769519de3f265f112e992aa7f446d905196bd608e8Stefan Berger
3779519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	itpm = 0;
3789519de3f265f112e992aa7f446d905196bd608e8Stefan Berger
3799519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	rc = tpm_tis_send_data(chip, cmd_getticks, len);
3809519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	if (rc == 0)
3819519de3f265f112e992aa7f446d905196bd608e8Stefan Berger		goto out;
3829519de3f265f112e992aa7f446d905196bd608e8Stefan Berger
3839519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	tpm_tis_ready(chip);
3849519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	release_locality(chip, chip->vendor.locality, 0);
3859519de3f265f112e992aa7f446d905196bd608e8Stefan Berger
3869519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	itpm = 1;
3879519de3f265f112e992aa7f446d905196bd608e8Stefan Berger
3889519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	rc = tpm_tis_send_data(chip, cmd_getticks, len);
3899519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	if (rc == 0) {
3909519de3f265f112e992aa7f446d905196bd608e8Stefan Berger		dev_info(chip->dev, "Detected an iTPM.\n");
3919519de3f265f112e992aa7f446d905196bd608e8Stefan Berger		rc = 1;
3929519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	} else
3939519de3f265f112e992aa7f446d905196bd608e8Stefan Berger		rc = -EFAULT;
3949519de3f265f112e992aa7f446d905196bd608e8Stefan Berger
3959519de3f265f112e992aa7f446d905196bd608e8Stefan Bergerout:
3969519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	itpm = rem_itpm;
3979519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	tpm_tis_ready(chip);
3989519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	release_locality(chip, chip->vendor.locality, 0);
3999519de3f265f112e992aa7f446d905196bd608e8Stefan Berger
4009519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	return rc;
4019519de3f265f112e992aa7f446d905196bd608e8Stefan Berger}
4029519de3f265f112e992aa7f446d905196bd608e8Stefan Berger
40362322d2554d2f9680c8ace7bbf1f97d8fa84ad1aArjan van de Venstatic const struct file_operations tis_ops = {
40427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	.owner = THIS_MODULE,
40527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	.llseek = no_llseek,
40627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	.open = tpm_open,
40727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	.read = tpm_read,
40827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	.write = tpm_write,
40927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	.release = tpm_release,
41027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn};
41127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
41227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
41327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
41427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL);
41527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
41627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
41727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated,
41827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		   NULL);
41927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
42027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
42104ab2293bbd36fc04060da93058cef7789414585Stefan Bergerstatic DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
4226259210176510c64251a314ffb74834a790f09a0Stefan Bergerstatic DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
42327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
42427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic struct attribute *tis_attrs[] = {
42527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	&dev_attr_pubek.attr,
42627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	&dev_attr_pcrs.attr,
42727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	&dev_attr_enabled.attr,
42827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	&dev_attr_active.attr,
42927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	&dev_attr_owned.attr,
43027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	&dev_attr_temp_deactivated.attr,
43127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	&dev_attr_caps.attr,
43204ab2293bbd36fc04060da93058cef7789414585Stefan Berger	&dev_attr_cancel.attr,
4336259210176510c64251a314ffb74834a790f09a0Stefan Berger	&dev_attr_durations.attr,
4346259210176510c64251a314ffb74834a790f09a0Stefan Berger	&dev_attr_timeouts.attr, NULL,
43527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn};
43627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
43727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic struct attribute_group tis_attr_grp = {
43827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	.attrs = tis_attrs
43927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn};
44027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
44127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic struct tpm_vendor_specific tpm_tis = {
44227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	.status = tpm_tis_status,
44327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	.recv = tpm_tis_recv,
44427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	.send = tpm_tis_send,
44527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	.cancel = tpm_tis_ready,
44627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
44727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	.req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
44827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	.req_canceled = TPM_STS_COMMAND_READY,
44927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	.attr_group = &tis_attr_grp,
45027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	.miscdev = {
45127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		    .fops = &tis_ops,},
45227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn};
45327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
4547d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic irqreturn_t tis_int_probe(int irq, void *dev_id)
45527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn{
45606efcad0d43a5491602f7d7bfc1ce997cdb0d062Jeff Garzik	struct tpm_chip *chip = dev_id;
45727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	u32 interrupt;
45827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
45927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	interrupt = ioread32(chip->vendor.iobase +
46027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			     TPM_INT_STATUS(chip->vendor.locality));
46127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
46227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (interrupt == 0)
46327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		return IRQ_NONE;
46427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
465a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger	chip->vendor.probed_irq = irq;
46627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
46727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	/* Clear interrupts handled with TPM_EOI */
46827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	iowrite32(interrupt,
46927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		  chip->vendor.iobase +
47027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		  TPM_INT_STATUS(chip->vendor.locality));
47127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	return IRQ_HANDLED;
47227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn}
47327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
474a6f97b293b08877d945ea3f28926aa446dd7ca2eJeff Garzikstatic irqreturn_t tis_int_handler(int dummy, void *dev_id)
47527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn{
47606efcad0d43a5491602f7d7bfc1ce997cdb0d062Jeff Garzik	struct tpm_chip *chip = dev_id;
47727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	u32 interrupt;
47827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	int i;
47927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
48027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	interrupt = ioread32(chip->vendor.iobase +
48127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			     TPM_INT_STATUS(chip->vendor.locality));
48227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
48327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (interrupt == 0)
48427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		return IRQ_NONE;
48527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
48627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (interrupt & TPM_INTF_DATA_AVAIL_INT)
48727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		wake_up_interruptible(&chip->vendor.read_queue);
48827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (interrupt & TPM_INTF_LOCALITY_CHANGE_INT)
48927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		for (i = 0; i < 5; i++)
49027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			if (check_locality(chip, i) >= 0)
49127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn				break;
49227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (interrupt &
49327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	    (TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_STS_VALID_INT |
49427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	     TPM_INTF_CMD_READY_INT))
49527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		wake_up_interruptible(&chip->vendor.int_queue);
49627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
49727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	/* Clear interrupts handled with TPM_EOI */
49827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	iowrite32(interrupt,
49927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		  chip->vendor.iobase +
50027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		  TPM_INT_STATUS(chip->vendor.locality));
501cab091eaa4952777d3183b6d7ce203a213cddc12Kylene Jo Hall	ioread32(chip->vendor.iobase + TPM_INT_STATUS(chip->vendor.locality));
50227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	return IRQ_HANDLED;
50327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn}
50427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
50590ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool interrupts = 1;
5065713556843aee24f484f445db6540f9fef976439Kylene Jo Hallmodule_param(interrupts, bool, 0444);
5075713556843aee24f484f445db6540f9fef976439Kylene Jo HallMODULE_PARM_DESC(interrupts, "Enable interrupts");
5085713556843aee24f484f445db6540f9fef976439Kylene Jo Hall
509c3c36aa98f8e39544afb99025bb69bc1b48e9bf0Kylene Jo Hallstatic int tpm_tis_init(struct device *dev, resource_size_t start,
5107917ff9a4cefd0500aa4a1b1942da96dbce6999fBjorn Helgaas			resource_size_t len, unsigned int irq)
51127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn{
51227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	u32 vendor, intfcaps, intmask;
513968de8e24d08fcc425e112ca465d4688b89b777bStefan Berger	int rc, i, irq_s, irq_e, probe;
51427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	struct tpm_chip *chip;
51527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
5169e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall	if (!(chip = tpm_register_hardware(dev, &tpm_tis)))
51727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		return -ENODEV;
51827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
51927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	chip->vendor.iobase = ioremap(start, len);
52027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (!chip->vendor.iobase) {
52127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		rc = -EIO;
52227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		goto out_err;
52327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	}
52427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
525ec57935837a78f9661125b08a5d08b697568e040Jason Gunthorpe	/* Default timeouts */
526ec57935837a78f9661125b08a5d08b697568e040Jason Gunthorpe	chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
527ec57935837a78f9661125b08a5d08b697568e040Jason Gunthorpe	chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
528ec57935837a78f9661125b08a5d08b697568e040Jason Gunthorpe	chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
529ec57935837a78f9661125b08a5d08b697568e040Jason Gunthorpe	chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
530ec57935837a78f9661125b08a5d08b697568e040Jason Gunthorpe
53105a462afe80553550bc77afc724ce60b42ad587eMarcel Selhorst	if (request_locality(chip, 0) != 0) {
53205a462afe80553550bc77afc724ce60b42ad587eMarcel Selhorst		rc = -ENODEV;
53305a462afe80553550bc77afc724ce60b42ad587eMarcel Selhorst		goto out_err;
53405a462afe80553550bc77afc724ce60b42ad587eMarcel Selhorst	}
53505a462afe80553550bc77afc724ce60b42ad587eMarcel Selhorst
53627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
53727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
5389e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall	dev_info(dev,
53927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		 "1.2 TPM (device-id 0x%X, rev-id %d)\n",
54027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		 vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
54127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
5429519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	if (!itpm) {
543968de8e24d08fcc425e112ca465d4688b89b777bStefan Berger		probe = probe_itpm(chip);
544968de8e24d08fcc425e112ca465d4688b89b777bStefan Berger		if (probe < 0) {
5459519de3f265f112e992aa7f446d905196bd608e8Stefan Berger			rc = -ENODEV;
5469519de3f265f112e992aa7f446d905196bd608e8Stefan Berger			goto out_err;
5479519de3f265f112e992aa7f446d905196bd608e8Stefan Berger		}
548968de8e24d08fcc425e112ca465d4688b89b777bStefan Berger		itpm = (probe == 0) ? 0 : 1;
5499519de3f265f112e992aa7f446d905196bd608e8Stefan Berger	}
5509519de3f265f112e992aa7f446d905196bd608e8Stefan Berger
5513507d612366a4e81226295f646410130a1f62a5cRajiv Andrade	if (itpm)
5523507d612366a4e81226295f646410130a1f62a5cRajiv Andrade		dev_info(dev, "Intel iTPM workaround enabled\n");
5533507d612366a4e81226295f646410130a1f62a5cRajiv Andrade
5543507d612366a4e81226295f646410130a1f62a5cRajiv Andrade
55527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	/* Figure out the capabilities */
55627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	intfcaps =
55727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	    ioread32(chip->vendor.iobase +
55827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		     TPM_INTF_CAPS(chip->vendor.locality));
5599e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall	dev_dbg(dev, "TPM interface capabilities (0x%x):\n",
56027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		intfcaps);
56127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (intfcaps & TPM_INTF_BURST_COUNT_STATIC)
5629e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall		dev_dbg(dev, "\tBurst Count Static\n");
56327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (intfcaps & TPM_INTF_CMD_READY_INT)
5649e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall		dev_dbg(dev, "\tCommand Ready Int Support\n");
56527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (intfcaps & TPM_INTF_INT_EDGE_FALLING)
5669e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall		dev_dbg(dev, "\tInterrupt Edge Falling\n");
56727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (intfcaps & TPM_INTF_INT_EDGE_RISING)
5689e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall		dev_dbg(dev, "\tInterrupt Edge Rising\n");
56927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (intfcaps & TPM_INTF_INT_LEVEL_LOW)
5709e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall		dev_dbg(dev, "\tInterrupt Level Low\n");
57127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (intfcaps & TPM_INTF_INT_LEVEL_HIGH)
5729e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall		dev_dbg(dev, "\tInterrupt Level High\n");
57327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (intfcaps & TPM_INTF_LOCALITY_CHANGE_INT)
5749e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall		dev_dbg(dev, "\tLocality Change Int Support\n");
57527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (intfcaps & TPM_INTF_STS_VALID_INT)
5769e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall		dev_dbg(dev, "\tSts Valid Int Support\n");
57727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (intfcaps & TPM_INTF_DATA_AVAIL_INT)
5789e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall		dev_dbg(dev, "\tData Avail Int Support\n");
57927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
580a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger	/* get the timeouts before testing for irqs */
5817f326ed7ff221a109bf89288bf6c8f0142e3e75cStefan Berger	if (tpm_get_timeouts(chip)) {
5827f326ed7ff221a109bf89288bf6c8f0142e3e75cStefan Berger		dev_err(dev, "Could not get TPM timeouts and durations\n");
5837f326ed7ff221a109bf89288bf6c8f0142e3e75cStefan Berger		rc = -ENODEV;
5847f326ed7ff221a109bf89288bf6c8f0142e3e75cStefan Berger		goto out_err;
5857f326ed7ff221a109bf89288bf6c8f0142e3e75cStefan Berger	}
586a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger
58768d6e6713fcb2ea6278661aaaf5f1c9c821b3751Stefan Berger	if (tpm_do_selftest(chip)) {
58868d6e6713fcb2ea6278661aaaf5f1c9c821b3751Stefan Berger		dev_err(dev, "TPM self test failed\n");
58968d6e6713fcb2ea6278661aaaf5f1c9c821b3751Stefan Berger		rc = -ENODEV;
59068d6e6713fcb2ea6278661aaaf5f1c9c821b3751Stefan Berger		goto out_err;
59168d6e6713fcb2ea6278661aaaf5f1c9c821b3751Stefan Berger	}
59268d6e6713fcb2ea6278661aaaf5f1c9c821b3751Stefan Berger
59327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	/* INTERRUPT Setup */
59427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	init_waitqueue_head(&chip->vendor.read_queue);
59527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	init_waitqueue_head(&chip->vendor.int_queue);
59627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
59727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	intmask =
59827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	    ioread32(chip->vendor.iobase +
59927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		     TPM_INT_ENABLE(chip->vendor.locality));
60027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
60127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	intmask |= TPM_INTF_CMD_READY_INT
60227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	    | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT
60327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	    | TPM_INTF_STS_VALID_INT;
60427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
60527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	iowrite32(intmask,
60627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		  chip->vendor.iobase +
60727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		  TPM_INT_ENABLE(chip->vendor.locality));
6087917ff9a4cefd0500aa4a1b1942da96dbce6999fBjorn Helgaas	if (interrupts)
6097917ff9a4cefd0500aa4a1b1942da96dbce6999fBjorn Helgaas		chip->vendor.irq = irq;
6107917ff9a4cefd0500aa4a1b1942da96dbce6999fBjorn Helgaas	if (interrupts && !chip->vendor.irq) {
611a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger		irq_s =
6125713556843aee24f484f445db6540f9fef976439Kylene Jo Hall		    ioread8(chip->vendor.iobase +
6135713556843aee24f484f445db6540f9fef976439Kylene Jo Hall			    TPM_INT_VECTOR(chip->vendor.locality));
614a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger		if (irq_s) {
615a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger			irq_e = irq_s;
616a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger		} else {
617a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger			irq_s = 3;
618a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger			irq_e = 15;
619a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger		}
6205713556843aee24f484f445db6540f9fef976439Kylene Jo Hall
621a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger		for (i = irq_s; i <= irq_e && chip->vendor.irq == 0; i++) {
6225713556843aee24f484f445db6540f9fef976439Kylene Jo Hall			iowrite8(i, chip->vendor.iobase +
623a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger				 TPM_INT_VECTOR(chip->vendor.locality));
6245713556843aee24f484f445db6540f9fef976439Kylene Jo Hall			if (request_irq
6250f2ed4c6bae23d2b7ef0ea2d272377e3de700c0cThomas Gleixner			    (i, tis_int_probe, IRQF_SHARED,
6265713556843aee24f484f445db6540f9fef976439Kylene Jo Hall			     chip->vendor.miscdev.name, chip) != 0) {
6275713556843aee24f484f445db6540f9fef976439Kylene Jo Hall				dev_info(chip->dev,
6285713556843aee24f484f445db6540f9fef976439Kylene Jo Hall					 "Unable to request irq: %d for probe\n",
6295713556843aee24f484f445db6540f9fef976439Kylene Jo Hall					 i);
6305713556843aee24f484f445db6540f9fef976439Kylene Jo Hall				continue;
6315713556843aee24f484f445db6540f9fef976439Kylene Jo Hall			}
63227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
6335713556843aee24f484f445db6540f9fef976439Kylene Jo Hall			/* Clear all existing */
6345713556843aee24f484f445db6540f9fef976439Kylene Jo Hall			iowrite32(ioread32
6355713556843aee24f484f445db6540f9fef976439Kylene Jo Hall				  (chip->vendor.iobase +
6365713556843aee24f484f445db6540f9fef976439Kylene Jo Hall				   TPM_INT_STATUS(chip->vendor.locality)),
6375713556843aee24f484f445db6540f9fef976439Kylene Jo Hall				  chip->vendor.iobase +
6385713556843aee24f484f445db6540f9fef976439Kylene Jo Hall				  TPM_INT_STATUS(chip->vendor.locality));
6395713556843aee24f484f445db6540f9fef976439Kylene Jo Hall
6405713556843aee24f484f445db6540f9fef976439Kylene Jo Hall			/* Turn on */
6415713556843aee24f484f445db6540f9fef976439Kylene Jo Hall			iowrite32(intmask | TPM_GLOBAL_INT_ENABLE,
6425713556843aee24f484f445db6540f9fef976439Kylene Jo Hall				  chip->vendor.iobase +
6435713556843aee24f484f445db6540f9fef976439Kylene Jo Hall				  TPM_INT_ENABLE(chip->vendor.locality));
6445713556843aee24f484f445db6540f9fef976439Kylene Jo Hall
645a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger			chip->vendor.probed_irq = 0;
646a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger
6475713556843aee24f484f445db6540f9fef976439Kylene Jo Hall			/* Generate Interrupts */
6485713556843aee24f484f445db6540f9fef976439Kylene Jo Hall			tpm_gen_interrupt(chip);
6495713556843aee24f484f445db6540f9fef976439Kylene Jo Hall
650a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger			chip->vendor.irq = chip->vendor.probed_irq;
651a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger
652a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger			/* free_irq will call into tis_int_probe;
653a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger			   clear all irqs we haven't seen while doing
654a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger			   tpm_gen_interrupt */
655a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger			iowrite32(ioread32
656a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger				  (chip->vendor.iobase +
657a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger				   TPM_INT_STATUS(chip->vendor.locality)),
658a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger				  chip->vendor.iobase +
659a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger				  TPM_INT_STATUS(chip->vendor.locality));
660a7b66822b20f67f106690d0acee3d0ba667fd9bbStefan Berger
6615713556843aee24f484f445db6540f9fef976439Kylene Jo Hall			/* Turn off */
6625713556843aee24f484f445db6540f9fef976439Kylene Jo Hall			iowrite32(intmask,
6635713556843aee24f484f445db6540f9fef976439Kylene Jo Hall				  chip->vendor.iobase +
6645713556843aee24f484f445db6540f9fef976439Kylene Jo Hall				  TPM_INT_ENABLE(chip->vendor.locality));
6655713556843aee24f484f445db6540f9fef976439Kylene Jo Hall			free_irq(i, chip);
66627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		}
66727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	}
66827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (chip->vendor.irq) {
66927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		iowrite8(chip->vendor.irq,
67027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			 chip->vendor.iobase +
67127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			 TPM_INT_VECTOR(chip->vendor.locality));
67227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		if (request_irq
6730f2ed4c6bae23d2b7ef0ea2d272377e3de700c0cThomas Gleixner		    (chip->vendor.irq, tis_int_handler, IRQF_SHARED,
67427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		     chip->vendor.miscdev.name, chip) != 0) {
67527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			dev_info(chip->dev,
6765713556843aee24f484f445db6540f9fef976439Kylene Jo Hall				 "Unable to request irq: %d for use\n",
6775713556843aee24f484f445db6540f9fef976439Kylene Jo Hall				 chip->vendor.irq);
67827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			chip->vendor.irq = 0;
67927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		} else {
68027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			/* Clear all existing */
68127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			iowrite32(ioread32
68227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn				  (chip->vendor.iobase +
68327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn				   TPM_INT_STATUS(chip->vendor.locality)),
68427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn				  chip->vendor.iobase +
68527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn				  TPM_INT_STATUS(chip->vendor.locality));
68627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
68727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			/* Turn on */
68827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			iowrite32(intmask | TPM_GLOBAL_INT_ENABLE,
68927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn				  chip->vendor.iobase +
69027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn				  TPM_INT_ENABLE(chip->vendor.locality));
69127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		}
69227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	}
69327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
69427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	INIT_LIST_HEAD(&chip->vendor.list);
6954e70daaf05a181b6968e29e72e9f1c16a183e92cJiri Kosina	mutex_lock(&tis_lock);
69627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	list_add(&chip->vendor.list, &tis_chips);
6974e70daaf05a181b6968e29e72e9f1c16a183e92cJiri Kosina	mutex_unlock(&tis_lock);
69827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
69927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
70027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	return 0;
70127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornout_err:
70227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	if (chip->vendor.iobase)
70327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		iounmap(chip->vendor.iobase);
70427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	tpm_remove_hardware(chip->dev);
70527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	return rc;
70627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn}
707968543100a75bef892f52eb86e92e83b3b7bc581Stefan Berger
708968543100a75bef892f52eb86e92e83b3b7bc581Stefan Bergerstatic void tpm_tis_reenable_interrupts(struct tpm_chip *chip)
709968543100a75bef892f52eb86e92e83b3b7bc581Stefan Berger{
710968543100a75bef892f52eb86e92e83b3b7bc581Stefan Berger	u32 intmask;
711968543100a75bef892f52eb86e92e83b3b7bc581Stefan Berger
712968543100a75bef892f52eb86e92e83b3b7bc581Stefan Berger	/* reenable interrupts that device may have lost or
713968543100a75bef892f52eb86e92e83b3b7bc581Stefan Berger	   BIOS/firmware may have disabled */
714968543100a75bef892f52eb86e92e83b3b7bc581Stefan Berger	iowrite8(chip->vendor.irq, chip->vendor.iobase +
715968543100a75bef892f52eb86e92e83b3b7bc581Stefan Berger		 TPM_INT_VECTOR(chip->vendor.locality));
716968543100a75bef892f52eb86e92e83b3b7bc581Stefan Berger
717968543100a75bef892f52eb86e92e83b3b7bc581Stefan Berger	intmask =
718968543100a75bef892f52eb86e92e83b3b7bc581Stefan Berger	    ioread32(chip->vendor.iobase +
719968543100a75bef892f52eb86e92e83b3b7bc581Stefan Berger		     TPM_INT_ENABLE(chip->vendor.locality));
720968543100a75bef892f52eb86e92e83b3b7bc581Stefan Berger
721968543100a75bef892f52eb86e92e83b3b7bc581Stefan Berger	intmask |= TPM_INTF_CMD_READY_INT
722968543100a75bef892f52eb86e92e83b3b7bc581Stefan Berger	    | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT
723968543100a75bef892f52eb86e92e83b3b7bc581Stefan Berger	    | TPM_INTF_STS_VALID_INT | TPM_GLOBAL_INT_ENABLE;
724968543100a75bef892f52eb86e92e83b3b7bc581Stefan Berger
725968543100a75bef892f52eb86e92e83b3b7bc581Stefan Berger	iowrite32(intmask,
726968543100a75bef892f52eb86e92e83b3b7bc581Stefan Berger		  chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality));
727968543100a75bef892f52eb86e92e83b3b7bc581Stefan Berger}
728968543100a75bef892f52eb86e92e83b3b7bc581Stefan Berger
729968543100a75bef892f52eb86e92e83b3b7bc581Stefan Berger
7307f2ab000c6f2ae46070807a3bf645c45d8639460Rajiv Andrade#ifdef CONFIG_PNP
7319e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hallstatic int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev,
7329e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall				      const struct pnp_device_id *pnp_id)
7339e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall{
734c3c36aa98f8e39544afb99025bb69bc1b48e9bf0Kylene Jo Hall	resource_size_t start, len;
7357917ff9a4cefd0500aa4a1b1942da96dbce6999fBjorn Helgaas	unsigned int irq = 0;
7367917ff9a4cefd0500aa4a1b1942da96dbce6999fBjorn Helgaas
7379e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall	start = pnp_mem_start(pnp_dev, 0);
7389e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall	len = pnp_mem_len(pnp_dev, 0);
7399e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall
7407917ff9a4cefd0500aa4a1b1942da96dbce6999fBjorn Helgaas	if (pnp_irq_valid(pnp_dev, 0))
7417917ff9a4cefd0500aa4a1b1942da96dbce6999fBjorn Helgaas		irq = pnp_irq(pnp_dev, 0);
7427917ff9a4cefd0500aa4a1b1942da96dbce6999fBjorn Helgaas	else
7437917ff9a4cefd0500aa4a1b1942da96dbce6999fBjorn Helgaas		interrupts = 0;
7447917ff9a4cefd0500aa4a1b1942da96dbce6999fBjorn Helgaas
745e5cce6c13c25d9ac56955a3ae2fd562719848172Olof Johansson	if (is_itpm(pnp_dev))
746e5cce6c13c25d9ac56955a3ae2fd562719848172Olof Johansson		itpm = 1;
747e5cce6c13c25d9ac56955a3ae2fd562719848172Olof Johansson
7487917ff9a4cefd0500aa4a1b1942da96dbce6999fBjorn Helgaas	return tpm_tis_init(&pnp_dev->dev, start, len, irq);
7499e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall}
7509e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall
75127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic int tpm_tis_pnp_suspend(struct pnp_dev *dev, pm_message_t msg)
75227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn{
75327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	return tpm_pm_suspend(&dev->dev, msg);
75427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn}
75527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
75627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic int tpm_tis_pnp_resume(struct pnp_dev *dev)
75727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn{
75859f6fbe4291fcc078ba26ce4edf8373a7620a13aRajiv Andrade	struct tpm_chip *chip = pnp_get_drvdata(dev);
75959f6fbe4291fcc078ba26ce4edf8373a7620a13aRajiv Andrade	int ret;
76059f6fbe4291fcc078ba26ce4edf8373a7620a13aRajiv Andrade
76145baa1d1fa3926510ead93c96e6b0baa5ad79bd3Stefan Berger	if (chip->vendor.irq)
76245baa1d1fa3926510ead93c96e6b0baa5ad79bd3Stefan Berger		tpm_tis_reenable_interrupts(chip);
76345baa1d1fa3926510ead93c96e6b0baa5ad79bd3Stefan Berger
76459f6fbe4291fcc078ba26ce4edf8373a7620a13aRajiv Andrade	ret = tpm_pm_resume(&dev->dev);
76559f6fbe4291fcc078ba26ce4edf8373a7620a13aRajiv Andrade	if (!ret)
76668d6e6713fcb2ea6278661aaaf5f1c9c821b3751Stefan Berger		tpm_do_selftest(chip);
76759f6fbe4291fcc078ba26ce4edf8373a7620a13aRajiv Andrade
76859f6fbe4291fcc078ba26ce4edf8373a7620a13aRajiv Andrade	return ret;
76927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn}
77027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
77127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic struct pnp_device_id tpm_pnp_tbl[] __devinitdata = {
77227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	{"PNP0C31", 0},		/* TPM */
77393e1b7d42e1edb4ddde6257e9a02513fef26f715Kylene Jo Hall	{"ATM1200", 0},		/* Atmel */
77493e1b7d42e1edb4ddde6257e9a02513fef26f715Kylene Jo Hall	{"IFX0102", 0},		/* Infineon */
77593e1b7d42e1edb4ddde6257e9a02513fef26f715Kylene Jo Hall	{"BCM0101", 0},		/* Broadcom */
776061991ec6edceda48d60f7a53e17b8d3416266aeLE DISEZ Erwan	{"BCM0102", 0},		/* Broadcom */
77793e1b7d42e1edb4ddde6257e9a02513fef26f715Kylene Jo Hall	{"NSC1200", 0},		/* National */
778fb0e7e11d017beb5f0b1fa25bc51e49e65c46d67Marcin Obara	{"ICO0102", 0},		/* Intel */
77993e1b7d42e1edb4ddde6257e9a02513fef26f715Kylene Jo Hall	/* Add new here */
78093e1b7d42e1edb4ddde6257e9a02513fef26f715Kylene Jo Hall	{"", 0},		/* User Specified */
78193e1b7d42e1edb4ddde6257e9a02513fef26f715Kylene Jo Hall	{"", 0}			/* Terminator */
78227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn};
78331bde71c202722a76686c3cf69a254c8a912275aMatt DomschMODULE_DEVICE_TABLE(pnp, tpm_pnp_tbl);
78427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
785253115b71fa06330bd58afbe01ccaf763a8a0cf1Rajiv Andradestatic __devexit void tpm_tis_pnp_remove(struct pnp_dev *dev)
786253115b71fa06330bd58afbe01ccaf763a8a0cf1Rajiv Andrade{
787253115b71fa06330bd58afbe01ccaf763a8a0cf1Rajiv Andrade	struct tpm_chip *chip = pnp_get_drvdata(dev);
788253115b71fa06330bd58afbe01ccaf763a8a0cf1Rajiv Andrade
789253115b71fa06330bd58afbe01ccaf763a8a0cf1Rajiv Andrade	tpm_dev_vendor_release(chip);
790253115b71fa06330bd58afbe01ccaf763a8a0cf1Rajiv Andrade
791253115b71fa06330bd58afbe01ccaf763a8a0cf1Rajiv Andrade	kfree(chip);
792253115b71fa06330bd58afbe01ccaf763a8a0cf1Rajiv Andrade}
793253115b71fa06330bd58afbe01ccaf763a8a0cf1Rajiv Andrade
794253115b71fa06330bd58afbe01ccaf763a8a0cf1Rajiv Andrade
79527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic struct pnp_driver tis_pnp_driver = {
79627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	.name = "tpm_tis",
79727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	.id_table = tpm_pnp_tbl,
79827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	.probe = tpm_tis_pnp_init,
79927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	.suspend = tpm_tis_pnp_suspend,
80027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	.resume = tpm_tis_pnp_resume,
801253115b71fa06330bd58afbe01ccaf763a8a0cf1Rajiv Andrade	.remove = tpm_tis_pnp_remove,
80227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn};
80327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
80493e1b7d42e1edb4ddde6257e9a02513fef26f715Kylene Jo Hall#define TIS_HID_USR_IDX sizeof(tpm_pnp_tbl)/sizeof(struct pnp_device_id) -2
80593e1b7d42e1edb4ddde6257e9a02513fef26f715Kylene Jo Hallmodule_param_string(hid, tpm_pnp_tbl[TIS_HID_USR_IDX].id,
80693e1b7d42e1edb4ddde6257e9a02513fef26f715Kylene Jo Hall		    sizeof(tpm_pnp_tbl[TIS_HID_USR_IDX].id), 0444);
80793e1b7d42e1edb4ddde6257e9a02513fef26f715Kylene Jo HallMODULE_PARM_DESC(hid, "Set additional specific HID for this driver to probe");
8087f2ab000c6f2ae46070807a3bf645c45d8639460Rajiv Andrade#endif
8097a192ec334cab9fafe3a8665a65af398b0e24730Ming Leistatic int tpm_tis_suspend(struct platform_device *dev, pm_message_t msg)
8107a192ec334cab9fafe3a8665a65af398b0e24730Ming Lei{
8117a192ec334cab9fafe3a8665a65af398b0e24730Ming Lei	return tpm_pm_suspend(&dev->dev, msg);
8127a192ec334cab9fafe3a8665a65af398b0e24730Ming Lei}
8137a192ec334cab9fafe3a8665a65af398b0e24730Ming Lei
8147a192ec334cab9fafe3a8665a65af398b0e24730Ming Leistatic int tpm_tis_resume(struct platform_device *dev)
8157a192ec334cab9fafe3a8665a65af398b0e24730Ming Lei{
81645baa1d1fa3926510ead93c96e6b0baa5ad79bd3Stefan Berger	struct tpm_chip *chip = dev_get_drvdata(&dev->dev);
81745baa1d1fa3926510ead93c96e6b0baa5ad79bd3Stefan Berger
81845baa1d1fa3926510ead93c96e6b0baa5ad79bd3Stefan Berger	if (chip->vendor.irq)
81945baa1d1fa3926510ead93c96e6b0baa5ad79bd3Stefan Berger		tpm_tis_reenable_interrupts(chip);
82045baa1d1fa3926510ead93c96e6b0baa5ad79bd3Stefan Berger
8217a192ec334cab9fafe3a8665a65af398b0e24730Ming Lei	return tpm_pm_resume(&dev->dev);
8227a192ec334cab9fafe3a8665a65af398b0e24730Ming Lei}
8237a192ec334cab9fafe3a8665a65af398b0e24730Ming Leistatic struct platform_driver tis_drv = {
8247a192ec334cab9fafe3a8665a65af398b0e24730Ming Lei	.driver = {
8257a192ec334cab9fafe3a8665a65af398b0e24730Ming Lei		.name = "tpm_tis",
8267a192ec334cab9fafe3a8665a65af398b0e24730Ming Lei		.owner		= THIS_MODULE,
8277a192ec334cab9fafe3a8665a65af398b0e24730Ming Lei	},
8287a192ec334cab9fafe3a8665a65af398b0e24730Ming Lei	.suspend = tpm_tis_suspend,
8297a192ec334cab9fafe3a8665a65af398b0e24730Ming Lei	.resume = tpm_tis_resume,
8309e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall};
8319e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall
8329e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hallstatic struct platform_device *pdev;
8339e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall
83490ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool force;
8359e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hallmodule_param(force, bool, 0444);
8369e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo HallMODULE_PARM_DESC(force, "Force device probe rather than using ACPI entry");
83727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic int __init init_tis(void)
83827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn{
8399e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall	int rc;
8407f2ab000c6f2ae46070807a3bf645c45d8639460Rajiv Andrade#ifdef CONFIG_PNP
8417f2ab000c6f2ae46070807a3bf645c45d8639460Rajiv Andrade	if (!force)
8427f2ab000c6f2ae46070807a3bf645c45d8639460Rajiv Andrade		return pnp_register_driver(&tis_pnp_driver);
8437f2ab000c6f2ae46070807a3bf645c45d8639460Rajiv Andrade#endif
8449e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall
8457f2ab000c6f2ae46070807a3bf645c45d8639460Rajiv Andrade	rc = platform_driver_register(&tis_drv);
8467f2ab000c6f2ae46070807a3bf645c45d8639460Rajiv Andrade	if (rc < 0)
8479e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall		return rc;
8487f2ab000c6f2ae46070807a3bf645c45d8639460Rajiv Andrade	if (IS_ERR(pdev=platform_device_register_simple("tpm_tis", -1, NULL, 0)))
8497f2ab000c6f2ae46070807a3bf645c45d8639460Rajiv Andrade		return PTR_ERR(pdev);
8507f2ab000c6f2ae46070807a3bf645c45d8639460Rajiv Andrade	if((rc=tpm_tis_init(&pdev->dev, TIS_MEM_BASE, TIS_MEM_LEN, 0)) != 0) {
8517f2ab000c6f2ae46070807a3bf645c45d8639460Rajiv Andrade		platform_device_unregister(pdev);
8527f2ab000c6f2ae46070807a3bf645c45d8639460Rajiv Andrade		platform_driver_unregister(&tis_drv);
8539e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall	}
8547f2ab000c6f2ae46070807a3bf645c45d8639460Rajiv Andrade	return rc;
85527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn}
85627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
85727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornstatic void __exit cleanup_tis(void)
85827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn{
85927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	struct tpm_vendor_specific *i, *j;
86027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	struct tpm_chip *chip;
8614e70daaf05a181b6968e29e72e9f1c16a183e92cJiri Kosina	mutex_lock(&tis_lock);
86227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	list_for_each_entry_safe(i, j, &tis_chips, list) {
86327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		chip = to_tpm_chip(i);
864253115b71fa06330bd58afbe01ccaf763a8a0cf1Rajiv Andrade		tpm_remove_hardware(chip->dev);
86527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		iowrite32(~TPM_GLOBAL_INT_ENABLE &
86627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			  ioread32(chip->vendor.iobase +
86727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn				   TPM_INT_ENABLE(chip->vendor.
86827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn						  locality)),
86927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			  chip->vendor.iobase +
87027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			  TPM_INT_ENABLE(chip->vendor.locality));
87127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		release_locality(chip, chip->vendor.locality, 1);
87227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		if (chip->vendor.irq)
87327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn			free_irq(chip->vendor.irq, chip);
87427084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		iounmap(i->iobase);
87527084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn		list_del(&i->list);
87627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn	}
8774e70daaf05a181b6968e29e72e9f1c16a183e92cJiri Kosina	mutex_unlock(&tis_lock);
8787f2ab000c6f2ae46070807a3bf645c45d8639460Rajiv Andrade#ifdef CONFIG_PNP
8797f2ab000c6f2ae46070807a3bf645c45d8639460Rajiv Andrade	if (!force) {
8809e323d3ee0ba9381af494641e1e87a8d372f916bKylene Jo Hall		pnp_unregister_driver(&tis_pnp_driver);
8817f2ab000c6f2ae46070807a3bf645c45d8639460Rajiv Andrade		return;
8827f2ab000c6f2ae46070807a3bf645c45d8639460Rajiv Andrade	}
8837f2ab000c6f2ae46070807a3bf645c45d8639460Rajiv Andrade#endif
8847f2ab000c6f2ae46070807a3bf645c45d8639460Rajiv Andrade	platform_device_unregister(pdev);
8857f2ab000c6f2ae46070807a3bf645c45d8639460Rajiv Andrade	platform_driver_unregister(&tis_drv);
88627084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn}
88727084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doorn
88827084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornmodule_init(init_tis);
88927084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van Doornmodule_exit(cleanup_tis);
89027084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van DoornMODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
89127084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van DoornMODULE_DESCRIPTION("TPM Driver");
89227084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van DoornMODULE_VERSION("2.0");
89327084efee0c3dc0eb15b5ed750aa9f1adb3983c3Leendert van DoornMODULE_LICENSE("GPL");
894