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