From patchwork Sun Mar 20 19:34:43 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Christophe Ricard X-Patchwork-Id: 8628681 Return-Path: X-Original-To: patchwork-tpmdd-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 73EBE9F3D1 for ; Sun, 20 Mar 2016 19:35:25 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id F24CC20204 for ; Sun, 20 Mar 2016 19:35:23 +0000 (UTC) Received: from lists.sourceforge.net (lists.sourceforge.net [216.34.181.88]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 03076201FE for ; Sun, 20 Mar 2016 19:35:22 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=sfs-ml-1.v29.ch3.sourceforge.com) by sfs-ml-1.v29.ch3.sourceforge.com with esmtp (Exim 4.76) (envelope-from ) id 1ahj8B-000652-D5; Sun, 20 Mar 2016 19:35:19 +0000 Received: from sog-mx-3.v43.ch3.sourceforge.com ([172.29.43.193] helo=mx.sourceforge.net) by sfs-ml-1.v29.ch3.sourceforge.com with esmtp (Exim 4.76) (envelope-from ) id 1ahj8A-00064g-07 for tpmdd-devel@lists.sourceforge.net; Sun, 20 Mar 2016 19:35:18 +0000 Received-SPF: pass (sog-mx-3.v43.ch3.sourceforge.com: domain of gmail.com designates 74.125.82.41 as permitted sender) client-ip=74.125.82.41; envelope-from=christophe.ricard@gmail.com; helo=mail-wm0-f41.google.com; Received: from mail-wm0-f41.google.com ([74.125.82.41]) by sog-mx-3.v43.ch3.sourceforge.com with esmtps (TLSv1:RC4-SHA:128) (Exim 4.76) id 1ahj88-0007B9-8v for tpmdd-devel@lists.sourceforge.net; Sun, 20 Mar 2016 19:35:17 +0000 Received: by mail-wm0-f41.google.com with SMTP id r129so27220329wmr.1 for ; Sun, 20 Mar 2016 12:35:16 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=0N6pew03H7tsbcTqb7RXY7iW31zClGgNuFLLX8ff45I=; b=idNaSl3vxwkysNbaAPohYAM+RyTkVvlBCZbO9hehqIY8tytKPSgk2Af21fqUbeyCFu ax1n3aSLlbUOa9dwFGFuWHJop2l31exDhat28+4bYMlUl45GYby7siY2BYBDGX5Ze0cx a2+UnbUMwnhf6pBy9Gx2xitYAY0qamzgILHJEgesOhq1Ev+KFjyu/fGQZaYCMj98JNzR KkelybnkUMpyFb3m9cnSas6qxh6MQxLwmtVUPhSbVytKSeK9mXqmQyH8W6RBzwehyok3 AIY3tbtA99ODGCeyt2gRvaJqHy8pYn0NT9rosvTrpFCP1b1REvTuQzxRaFE+JbpXd1G7 EWmg== X-Gm-Message-State: AD7BkJLW+Mjqu225+BQAYFLbMbfdSpq92mYkZGXAnan5fn4NFK5/CflCojJEFGbvgs5vpQ== X-Received: by 10.28.220.215 with SMTP id t206mr9919961wmg.68.1458502510187; Sun, 20 Mar 2016 12:35:10 -0700 (PDT) Received: from localhost.localdomain (AMarseille-654-1-498-218.w86-210.abo.wanadoo.fr. [86.210.138.218]) by smtp.gmail.com with ESMTPSA id w203sm7560646wmg.14.2016.03.20.12.35.08 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sun, 20 Mar 2016 12:35:09 -0700 (PDT) From: Christophe Ricard X-Google-Original-From: Christophe Ricard To: jarkko.sakkinen@linux.intel.com Date: Sun, 20 Mar 2016 20:34:43 +0100 Message-Id: <1458502483-16887-13-git-send-email-christophe-h.ricard@st.com> X-Mailer: git-send-email 2.5.0 In-Reply-To: <1458502483-16887-1-git-send-email-christophe-h.ricard@st.com> References: <1458502483-16887-1-git-send-email-christophe-h.ricard@st.com> MIME-Version: 1.0 X-Spam-Score: -1.6 (-) X-Headers-End: 1ahj88-0007B9-8v Cc: jean-luc.blanc@st.com, ashley@ashleylai.com, tpmdd-devel@lists.sourceforge.net, christophe-h.ricard@st.com, benoit.houyere@st.com Subject: [tpmdd-devel] [PATCH 12/12] tpm/tpm_tis_i2c: Add support for i2c phy X-BeenThere: tpmdd-devel@lists.sourceforge.net X-Mailman-Version: 2.1.9 Precedence: list List-Id: Tpm Device Driver maintainance List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: tpmdd-devel-bounces@lists.sourceforge.net X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, FREEMAIL_FROM,HK_RANDOM_ENVFROM,RCVD_IN_DNSWL_HI,RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP A i2c protocol standardized by the TCG is now supported by most of TPM vendors. Signed-off-by: Christophe Ricard --- .../bindings/security/tpm/tpm_tis_i2c.txt | 30 ++ drivers/char/tpm/Kconfig | 14 +- drivers/char/tpm/Makefile | 1 + drivers/char/tpm/tpm_tis_i2c.c | 499 +++++++++++++++++++++ 4 files changed, 543 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/security/tpm/tpm_tis_i2c.txt create mode 100644 drivers/char/tpm/tpm_tis_i2c.c diff --git a/Documentation/devicetree/bindings/security/tpm/tpm_tis_i2c.txt b/Documentation/devicetree/bindings/security/tpm/tpm_tis_i2c.txt new file mode 100644 index 0000000..a09f922 --- /dev/null +++ b/Documentation/devicetree/bindings/security/tpm/tpm_tis_i2c.txt @@ -0,0 +1,30 @@ +Required properties: +- compatible: Should be "st,st33htpm-i2c" or "tcg,tpm_tis-i2c" +- clock-frequency: I²C work frequency. +- reg: address on the bus + +Optional TPM_TIS Properties: +- interrupt-parent: phandle for the interrupt gpio controller +- interrupts: GPIO interrupt to which the chip is connected + +Optional SoC Specific Properties: +- pinctrl-names: Contains only one value - "default". +- pintctrl-0: Specifies the pin control groups used for this controller. + +Example (for ARM-based BeagleBoard xM with TPM_TIS on I2C2): + +&i2c2 { + + status = "okay"; + + tpm_tis: tpm_tis@17 { + + compatible = "st,tpm_tis-i2c"; + + reg = <0x17>; + clock-frequency = <400000>; + + interrupt-parent = <&gpio5>; + interrupts = <7 IRQ_TYPE_LEVEL_HIGH>; + }; +}; diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 6fbe7468..56afc2f 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -72,7 +72,19 @@ config TCG_TIS_SPI TCG TIS 1.3 TPM specification (TPM1.2) or the TCG PTP FIFO specification (TPM2.0) say Yes and it will be accessible from within Linux. To compile this driver as a module, choose M here; - the module will be called tpm_spi_tis. + the module will be called tpm_tis_spi. + +config TCG_TIS_I2C + tristate "TPM Interface Specification 1.3 Interface / TPM 2.0 FIFO Interface - (I2C)" + depends on I2C + depends on CRC_CCITT + ---help--- + If you have a TPM security chip which is connected to a regular, + non-tcg I2C master (i.e. most embedded platforms) that is compliant with the + TCG TIS 1.3 TPM specification (TPM1.2) or the TCG PTP FIFO + specification (TPM2.0) say Yes and it will be accessible from + within Linux. To compile this driver as a module, choose M here; + the module will be called tpm_tis_i2c. config TCG_NSC tristate "National Semiconductor TPM Interface" diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index 69508ef..eec3793 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_TCG_TIS_I2C_ATMEL) += tpm_i2c_atmel.o obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o obj-$(CONFIG_TCG_TIS_I2C_NUVOTON) += tpm_i2c_nuvoton.o obj-$(CONFIG_TCG_TIS_SPI) += tpm_tis_spi.o +obj-$(CONFIG_TCG_TIS_I2C) += tpm_tis_i2c.o obj-$(CONFIG_TCG_NSC) += tpm_nsc.o obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o diff --git a/drivers/char/tpm/tpm_tis_i2c.c b/drivers/char/tpm/tpm_tis_i2c.c new file mode 100644 index 0000000..0591d13 --- /dev/null +++ b/drivers/char/tpm/tpm_tis_i2c.c @@ -0,0 +1,499 @@ +/* + * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24 + * Copyright (C) 2016 STMicroelectronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "tpm.h" +#include "tpm_tis_core.h" + +#define TPM_LOC_SEL 0x04 +#define TPM_I2C_INTERFACE_CAPABILITY 0x30 +#define TPM_I2C_DEVICE_ADDRESS 0x38 +#define TPM_DATA_CSUM_ENABLE 0x40 +#define TPM_DATA_CSUM 0x44 +#define TPM_I2C_DID_VID 0x48 +#define TPM_I2C_RID 0x4C + +#define TPM_I2C_DEFAULT_GUARD_TIME 0xFA + +enum tpm_tis_i2c_operation { + TPM_I2C_NONE, + TPM_I2C_RECV, + TPM_I2C_SEND, +}; + +#define TPM_I2C_DEVADRCHANGE(x) ((0x18000000 & x) >> 27) +#define TPM_I2C_READ_READ(x) ((0x00100000 & x) >> 20) +#define TPM_I2C_READ_WRITE(x) ((0x00080000 & x) >> 19) +#define TPM_I2C_WRITE_READ(x) ((0x00040000 & x) >> 18) +#define TPM_I2C_WRITE_WRITE(x) ((0x00020000 & x) >> 17) +#define TPM_I2C_GUARD_TIME(x) ((0x0001FE00 & x) >> 9) + +struct tpm_tis_i2c_phy { + struct i2c_client *client; + u8 buf[TPM_BUFSIZE + 1]; + u8 last_i2c_ops; + + struct timer_list guard_timer; + struct mutex phy_lock; + + bool data_csum; + bool devadrchange; + bool read_read; + bool read_write; + bool write_read; + bool write_write; + u8 guard_time; +}; + +static int tpm_tis_i2c_ptp_register_mapper(u32 addr, u8 *i2c_reg) +{ + *i2c_reg = (u8)(0x000000ff & addr); + + switch (addr) { + case TPM_ACCESS(0): + *i2c_reg = TPM_LOC_SEL; + break; + case TPM_LOC_SEL: + *i2c_reg = TPM_ACCESS(0); + break; + case TPM_DID_VID(0): + *i2c_reg = TPM_I2C_DID_VID; + break; + case TPM_RID(0): + *i2c_reg = TPM_I2C_RID; + break; + case TPM_INT_VECTOR(0): + return -1; + } + + return 0; +} + +static void tpm_tis_i2c_guard_time_timeout(unsigned long data) +{ + struct tpm_tis_i2c_phy *phy = (struct tpm_tis_i2c_phy *)data; + + pr_debug("\n"); + + /* GUARD_TIME expired */ + phy->last_i2c_ops = TPM_I2C_NONE; +} + +static void tpm_tis_i2c_sleep_guard_time(struct tpm_tis_i2c_phy *phy, + u8 i2c_operation) +{ + del_timer_sync(&phy->guard_timer); + switch (i2c_operation) { + case TPM_I2C_RECV: + switch (phy->last_i2c_ops) { + case TPM_I2C_RECV: + if (phy->read_read) + udelay(phy->guard_time); + break; + case TPM_I2C_SEND: + if (phy->write_read) + udelay(phy->guard_time); + break; + } + break; + case TPM_I2C_SEND: + switch (phy->last_i2c_ops) { + case TPM_I2C_RECV: + if (phy->read_write) + udelay(phy->guard_time); + break; + case TPM_I2C_SEND: + if (phy->write_write) + udelay(phy->guard_time); + break; + } + break; + } + phy->last_i2c_ops = i2c_operation; +} + +static int tpm_tis_i2c_read_bytes(struct tpm_chip *chip, u32 addr, size_t len, + u8 size, u8 *result) +{ + struct tpm_tis_i2c_phy *phy = tpm_get_vendordata(chip); + int i, ret; + u8 i2c_reg; + + mutex_lock(&phy->phy_lock); + ret = tpm_tis_i2c_ptp_register_mapper(addr, &i2c_reg); + if (ret < 0) { + /* If we don't have any register equivalence in i2c + * ignore the sequence. + */ + ret = len; + goto exit; + } + ret = -1; + + for (i = 0; i < TPM_RETRY && ret < 0; i++) { + tpm_tis_i2c_sleep_guard_time(phy, TPM_I2C_SEND); + ret = i2c_master_send(phy->client, &i2c_reg, 1); + mod_timer(&phy->guard_timer, phy->guard_time); + } + + if (ret < 0) + goto exit; + + ret = -1; + for (i = 0; i < TPM_RETRY && ret < 0; i++) { + tpm_tis_i2c_sleep_guard_time(phy, TPM_I2C_RECV); + ret = i2c_master_recv(phy->client, result, len * size); + mod_timer(&phy->guard_timer, phy->guard_time); + } + +exit: + mutex_unlock(&phy->phy_lock); + return ret; +} + +static int tpm_tis_i2c_write_bytes(struct tpm_chip *chip, u32 addr, size_t len, + u8 size, u8 *value) +{ + struct tpm_tis_i2c_phy *phy = tpm_get_vendordata(chip); + int i, ret; + u8 i2c_reg; + + mutex_lock(&phy->phy_lock); + ret = tpm_tis_i2c_ptp_register_mapper(addr, &i2c_reg); + if (ret < 0) { + /* If we don't have any register equivalence in i2c + * ignore the sequence. + */ + ret = len * size; + goto exit; + } + + ret = -1; + phy->buf[0] = i2c_reg; + memcpy(phy->buf + 1, value, len * size); + + for (i = 0; i < TPM_RETRY && (ret < 0 || ret < len + 1); i++) { + tpm_tis_i2c_sleep_guard_time(phy, TPM_I2C_SEND); + ret = i2c_master_send(phy->client, phy->buf, len * size + 1); + mod_timer(&phy->guard_timer, phy->guard_time); + } + +exit: + mutex_unlock(&phy->phy_lock); + return ret; +} + +static bool tpm_tis_i2c_check_data(struct tpm_chip *chip, u8 *buf, size_t len) +{ + struct tpm_tis_i2c_phy *phy = tpm_get_vendordata(chip); + u16 crc, crc_tpm; + + if (phy->data_csum) { + crc = crc_ccitt(0x0000, buf, len); + + crc_tpm = tpm_read_word(chip, TPM_DATA_CSUM); + crc_tpm = be16_to_cpu(crc_tpm); + + return crc == crc_tpm; + } + return true; +} + +static const struct tpm_class_ops tpm_tis = { + .status = tpm_tis_status, + .recv = tpm_tis_recv, + .send = tpm_tis_send, + .cancel = tpm_tis_ready, + .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, + .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, + .req_canceled = tpm_tis_req_canceled, + .read_bytes = tpm_tis_i2c_read_bytes, + .write_bytes = tpm_tis_i2c_write_bytes, + .check_data = tpm_tis_i2c_check_data, +}; + +static SIMPLE_DEV_PM_OPS(tpm_tis_i2c_pm, tpm_pm_suspend, tpm_tis_resume); + +static ssize_t i2c_addr_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (client) + return sprintf(buf, "0x%.2x\n", client->addr); + + return 0; +} + +static ssize_t i2c_addr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_tis_i2c_phy *phy; + long new_addr; + u16 cur_addr; + int ret = 0; + + if (!chip) + goto exit; + + phy = tpm_get_vendordata(chip); + if (!phy || !phy->client || !phy->devadrchange) + goto exit; + + /* Base string automatically detected */ + ret = kstrtol(buf, 0, &new_addr); + if (ret < 0) + goto exit; + + ret = tpm_write_word(chip, TPM_I2C_DEVICE_ADDRESS, new_addr); + if (ret < 0) + goto exit; + + cur_addr = tpm_read_word(chip, TPM_I2C_DEVICE_ADDRESS); + if (cur_addr == new_addr) { + phy->client->addr = new_addr & 0x00ff; + return count; + } + + return -EINVAL; +exit: + return ret; +} +static DEVICE_ATTR_RW(i2c_addr); + +static ssize_t csum_state_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_tis_i2c_phy *phy; + + if (!chip) + goto exit; + + phy = tpm_get_vendordata(chip); + if (!phy || !phy->client) + goto exit; + + return sprintf(buf, "%x\n", phy->data_csum); +exit: + return 0; +} + +static ssize_t csum_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_tis_i2c_phy *phy; + long new_state; + u8 cur_state; + int ret = 0; + + if (!chip) + goto exit; + + phy = tpm_get_vendordata(chip); + if (!phy || !phy->client) + goto exit; + + ret = kstrtol(buf, 2, &new_state); + if (ret < 0) + goto exit; + + ret = tpm_write_byte(chip, TPM_DATA_CSUM_ENABLE, new_state); + if (ret < 0) + goto exit; + + cur_state = tpm_read_byte(chip, TPM_DATA_CSUM_ENABLE); + if (new_state == cur_state) { + phy->data_csum = cur_state; + return count; + } + + return -EINVAL; +exit: + return ret; +} +static DEVICE_ATTR_RW(csum_state); + +static struct attribute *tpm_tis_i2c_attrs[] = { + &dev_attr_i2c_addr.attr, + &dev_attr_csum_state.attr, + NULL, +}; + +static struct attribute_group tpm_tis_i2c_attr_group = { + .attrs = tpm_tis_i2c_attrs, +}; + +static int tpm_tis_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tpm_tis_i2c_phy *phy; + struct tpm_chip *chip; + unsigned int irq_polarity = IRQ_TYPE_NONE; + int ret, irq = -1; + u32 tmp; + + if (!client) { + pr_err("%s: i2c client is NULL. Device not accessible.\n", + __func__); + return -ENODEV; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_info(&client->dev, "client not i2c capable\n"); + return -ENODEV; + } + + phy = devm_kzalloc(&client->dev, sizeof(struct tpm_tis_i2c_phy), + GFP_KERNEL); + if (!phy) + return -ENOMEM; + + chip = tpmm_chip_alloc(&client->dev, &tpm_tis); + if (IS_ERR(chip)) + return PTR_ERR(chip); + + phy->client = client; + mutex_init(&phy->phy_lock); + tpm_set_vendordata(chip, phy); + i2c_set_clientdata(client, chip); + + phy->guard_time = TPM_I2C_DEFAULT_GUARD_TIME; + phy->read_read = true; + phy->read_write = true; + phy->write_read = true; + phy->write_write = true; + + /* initialize timer */ + init_timer(&phy->guard_timer); + phy->guard_timer.data = (unsigned long)phy; + phy->guard_timer.function = tpm_tis_i2c_guard_time_timeout; + + ret = tpm_write_byte(chip, TPM_LOC_SEL, 0); + if (ret < 0) + goto out_err; + + phy->data_csum = tpm_read_byte(chip, TPM_DATA_CSUM_ENABLE); + + if (client->irq > 0) { + irq = client->irq; + irq_polarity = irq_get_trigger_type(irq); + if (irq_polarity > 0) + irq_polarity |= IRQF_ONESHOT; + else + irq = -1; + } + + ret = tpm_tis_init_core(&client->dev, chip, irq, irq_polarity); + if (ret < 0) + goto out_err; + + tmp = tpm_read_dword(chip, TPM_I2C_INTERFACE_CAPABILITY); + + tmp = be32_to_cpu((__force __be32)tmp); + + phy->devadrchange = TPM_I2C_DEVADRCHANGE(tmp); + phy->read_read = TPM_I2C_READ_READ(tmp); + phy->read_write = TPM_I2C_READ_WRITE(tmp); + phy->write_read = TPM_I2C_WRITE_READ(tmp); + phy->write_write = TPM_I2C_WRITE_WRITE(tmp); + phy->guard_time = TPM_I2C_GUARD_TIME(tmp); + + ret = sysfs_create_group(&client->dev.kobj, &tpm_tis_i2c_attr_group); + if (ret < 0) { + dev_err(&chip->dev, + "failed to create sysfs attributes, %d\n", ret); + goto deinit_err; + } + + return ret; + +deinit_err: + tpm_chip_unregister(chip); +out_err: + tpm_tis_remove(chip); + return ret; +} + +static int tpm_tis_i2c_remove(struct i2c_client *client) +{ + struct tpm_chip *chip = i2c_get_clientdata(client); + + tpm_chip_unregister(chip); + tpm_tis_remove(chip); + sysfs_remove_group(&client->dev.kobj, &tpm_tis_i2c_attr_group); + return 0; +} + +static const struct i2c_device_id tpm_tis_i2c_id[] = { + {"tpm_tis_i2c", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, tpm_tis_i2c_id); + +static const struct of_device_id of_tis_i2c_match[] = { + { .compatible = "st,st33htpm-i2c", }, + { .compatible = "tcg,tpm_tis-i2c", }, + {} +}; +MODULE_DEVICE_TABLE(of, of_tis_i2c_match); + +static const struct acpi_device_id acpi_tis_i2c_match[] = { + {"SMO0768", 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, acpi_tis_i2c_match); + +static struct i2c_driver tpm_tis_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tpm_tis_i2c", + .pm = &tpm_tis_i2c_pm, + .of_match_table = of_match_ptr(of_tis_i2c_match), + .acpi_match_table = ACPI_PTR(acpi_tis_i2c_match), + }, + .probe = tpm_tis_i2c_probe, + .remove = tpm_tis_i2c_remove, + .id_table = tpm_tis_i2c_id, +}; + +module_i2c_driver(tpm_tis_i2c_driver); + +MODULE_DESCRIPTION("TPM Driver for native I2C access"); +MODULE_LICENSE("GPL");