aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason Gunthorpe <jgunthorpe@obsidianresearch.com>2013-10-06 14:43:36 -0400
committerPeter Huewe <peterhuewe@gmx.de>2013-10-22 13:43:07 -0400
commita2871c62e1865c45f87a9343de76f727fb7a0ffd (patch)
treeca319f1a69800b161c002edf667120143fda7c84
parent4c336e4b1556f4b722ba597bc6e3df786968a600 (diff)
tpm: Add support for Atmel I2C TPMs
This is based on the work of Teddy Reed <teddy@prosauce.org> published on GitHub: https://github.com/theopolis/tpm-i2c-atmel.git 34894b988b67e0ae55088d6388e77b0dbf10c07d That driver was never merged, I have taken it as a starting port, forward ported, tested and revised the driver: - Make it broadly textually similar to the Infineon and Nuvoton I2C driver - Place everything in a format suitable for mainline inclusion - Use high level I2C functions i2c_master_send and i2c_master_recv for data xfer - Use the timeout system from the core code, by faking out a status register - Only I2C transfer the number of bytes in the reply, not a fixed message size. - checkpatch cleanups - Testing on ARM Kirkwood, with this device tree, using a AT97SC3204T-X1A180 tpm@29 { compatible = "atmel,at97sc3204t"; reg = <0x29>; }; Signed-off-by: Teddy Reed <teddy@prosauce.org> [jgg: revised and tested] Signed-off-by: Jason Gunthorpe <jgunthorpe@obsidianresearch.com> [phuewe: minor whitespace changes] Signed-off-by: Peter Huewe <peterhuewe@gmx.de>
-rw-r--r--Documentation/devicetree/bindings/i2c/trivial-devices.txt1
-rw-r--r--drivers/char/tpm/Kconfig9
-rw-r--r--drivers/char/tpm/Makefile1
-rw-r--r--drivers/char/tpm/tpm_i2c_atmel.c284
4 files changed, 295 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
index 58454bdfa20e..f1fb26eed0e9 100644
--- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt
+++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
@@ -15,6 +15,7 @@ adi,adt7461 +/-1C TDM Extended Temp Range I.C
15adt7461 +/-1C TDM Extended Temp Range I.C 15adt7461 +/-1C TDM Extended Temp Range I.C
16at,24c08 i2c serial eeprom (24cxx) 16at,24c08 i2c serial eeprom (24cxx)
17atmel,24c02 i2c serial eeprom (24cxx) 17atmel,24c02 i2c serial eeprom (24cxx)
18atmel,at97sc3204t i2c trusted platform module (TPM)
18catalyst,24c32 i2c serial eeprom 19catalyst,24c32 i2c serial eeprom
19dallas,ds1307 64 x 8, Serial, I2C Real-Time Clock 20dallas,ds1307 64 x 8, Serial, I2C Real-Time Clock
20dallas,ds1338 I2C RTC with 56-Byte NV RAM 21dallas,ds1338 I2C RTC with 56-Byte NV RAM
diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index ade71c134a1a..f9085860b716 100644
--- a/drivers/char/tpm/Kconfig
+++ b/drivers/char/tpm/Kconfig
@@ -33,6 +33,15 @@ config TCG_TIS
33 from within Linux. To compile this driver as a module, choose 33 from within Linux. To compile this driver as a module, choose
34 M here; the module will be called tpm_tis. 34 M here; the module will be called tpm_tis.
35 35
36config TCG_TIS_I2C_ATMEL
37 tristate "TPM Interface Specification 1.2 Interface (I2C - Atmel)"
38 depends on I2C
39 ---help---
40 If you have an Atmel I2C TPM security chip say Yes and it will be
41 accessible from within Linux.
42 To compile this driver as a module, choose M here; the module will
43 be called tpm_tis_i2c_atmel.
44
36config TCG_TIS_I2C_INFINEON 45config TCG_TIS_I2C_INFINEON
37 tristate "TPM Interface Specification 1.2 Interface (I2C - Infineon)" 46 tristate "TPM Interface Specification 1.2 Interface (I2C - Infineon)"
38 depends on I2C 47 depends on I2C
diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index 07ee67cf3341..b80a4000daee 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -13,6 +13,7 @@ ifdef CONFIG_TCG_IBMVTPM
13endif 13endif
14endif 14endif
15obj-$(CONFIG_TCG_TIS) += tpm_tis.o 15obj-$(CONFIG_TCG_TIS) += tpm_tis.o
16obj-$(CONFIG_TCG_TIS_I2C_ATMEL) += tpm_i2c_atmel.o
16obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o 17obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o
17obj-$(CONFIG_TCG_TIS_I2C_NUVOTON) += tpm_i2c_nuvoton.o 18obj-$(CONFIG_TCG_TIS_I2C_NUVOTON) += tpm_i2c_nuvoton.o
18obj-$(CONFIG_TCG_NSC) += tpm_nsc.o 19obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c
new file mode 100644
index 000000000000..c3cd7fe481a1
--- /dev/null
+++ b/drivers/char/tpm/tpm_i2c_atmel.c
@@ -0,0 +1,284 @@
1/*
2 * ATMEL I2C TPM AT97SC3204T
3 *
4 * Copyright (C) 2012 V Lab Technologies
5 * Teddy Reed <teddy@prosauce.org>
6 * Copyright (C) 2013, Obsidian Research Corp.
7 * Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
8 * Device driver for ATMEL I2C TPMs.
9 *
10 * Teddy Reed determined the basic I2C command flow, unlike other I2C TPM
11 * devices the raw TCG formatted TPM command data is written via I2C and then
12 * raw TCG formatted TPM command data is returned via I2C.
13 *
14 * TGC status/locality/etc functions seen in the LPC implementation do not
15 * seem to be present.
16 *
17 * This program is free software: you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation, either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program. If not, see http://www.gnu.org/licenses/>.
29 */
30#include <linux/init.h>
31#include <linux/module.h>
32#include <linux/moduleparam.h>
33#include <linux/slab.h>
34#include <linux/i2c.h>
35#include "tpm.h"
36
37#define I2C_DRIVER_NAME "tpm_i2c_atmel"
38
39#define TPM_I2C_SHORT_TIMEOUT 750 /* ms */
40#define TPM_I2C_LONG_TIMEOUT 2000 /* 2 sec */
41
42#define ATMEL_STS_OK 1
43
44struct priv_data {
45 size_t len;
46 /* This is the amount we read on the first try. 25 was chosen to fit a
47 * fair number of read responses in the buffer so a 2nd retry can be
48 * avoided in small message cases. */
49 u8 buffer[sizeof(struct tpm_output_header) + 25];
50};
51
52static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len)
53{
54 struct priv_data *priv = chip->vendor.priv;
55 struct i2c_client *client = to_i2c_client(chip->dev);
56 s32 status;
57
58 priv->len = 0;
59
60 if (len <= 2)
61 return -EIO;
62
63 status = i2c_master_send(client, buf, len);
64
65 dev_dbg(chip->dev,
66 "%s(buf=%*ph len=%0zx) -> sts=%d\n", __func__,
67 (int)min_t(size_t, 64, len), buf, len, status);
68 return status;
69}
70
71static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
72{
73 struct priv_data *priv = chip->vendor.priv;
74 struct i2c_client *client = to_i2c_client(chip->dev);
75 struct tpm_output_header *hdr =
76 (struct tpm_output_header *)priv->buffer;
77 u32 expected_len;
78 int rc;
79
80 if (priv->len == 0)
81 return -EIO;
82
83 /* Get the message size from the message header, if we didn't get the
84 * whole message in read_status then we need to re-read the
85 * message. */
86 expected_len = be32_to_cpu(hdr->length);
87 if (expected_len > count)
88 return -ENOMEM;
89
90 if (priv->len >= expected_len) {
91 dev_dbg(chip->dev,
92 "%s early(buf=%*ph count=%0zx) -> ret=%d\n", __func__,
93 (int)min_t(size_t, 64, expected_len), buf, count,
94 expected_len);
95 memcpy(buf, priv->buffer, expected_len);
96 return expected_len;
97 }
98
99 rc = i2c_master_recv(client, buf, expected_len);
100 dev_dbg(chip->dev,
101 "%s reread(buf=%*ph count=%0zx) -> ret=%d\n", __func__,
102 (int)min_t(size_t, 64, expected_len), buf, count,
103 expected_len);
104 return rc;
105}
106
107static void i2c_atmel_cancel(struct tpm_chip *chip)
108{
109 dev_err(chip->dev, "TPM operation cancellation was requested, but is not supported");
110}
111
112static u8 i2c_atmel_read_status(struct tpm_chip *chip)
113{
114 struct priv_data *priv = chip->vendor.priv;
115 struct i2c_client *client = to_i2c_client(chip->dev);
116 int rc;
117
118 /* The TPM fails the I2C read until it is ready, so we do the entire
119 * transfer here and buffer it locally. This way the common code can
120 * properly handle the timeouts. */
121 priv->len = 0;
122 memset(priv->buffer, 0, sizeof(priv->buffer));
123
124
125 /* Once the TPM has completed the command the command remains readable
126 * until another command is issued. */
127 rc = i2c_master_recv(client, priv->buffer, sizeof(priv->buffer));
128 dev_dbg(chip->dev,
129 "%s: sts=%d", __func__, rc);
130 if (rc <= 0)
131 return 0;
132
133 priv->len = rc;
134
135 return ATMEL_STS_OK;
136}
137
138static const struct file_operations i2c_atmel_ops = {
139 .owner = THIS_MODULE,
140 .llseek = no_llseek,
141 .open = tpm_open,
142 .read = tpm_read,
143 .write = tpm_write,
144 .release = tpm_release,
145};
146
147static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
148static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
149static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL);
150static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
151static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
152static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, NULL);
153static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL);
154static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
155static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
156static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
157
158static struct attribute *i2c_atmel_attrs[] = {
159 &dev_attr_pubek.attr,
160 &dev_attr_pcrs.attr,
161 &dev_attr_enabled.attr,
162 &dev_attr_active.attr,
163 &dev_attr_owned.attr,
164 &dev_attr_temp_deactivated.attr,
165 &dev_attr_caps.attr,
166 &dev_attr_cancel.attr,
167 &dev_attr_durations.attr,
168 &dev_attr_timeouts.attr,
169 NULL,
170};
171
172static struct attribute_group i2c_atmel_attr_grp = {
173 .attrs = i2c_atmel_attrs
174};
175
176static bool i2c_atmel_req_canceled(struct tpm_chip *chip, u8 status)
177{
178 return 0;
179}
180
181static const struct tpm_vendor_specific i2c_atmel = {
182 .status = i2c_atmel_read_status,
183 .recv = i2c_atmel_recv,
184 .send = i2c_atmel_send,
185 .cancel = i2c_atmel_cancel,
186 .req_complete_mask = ATMEL_STS_OK,
187 .req_complete_val = ATMEL_STS_OK,
188 .req_canceled = i2c_atmel_req_canceled,
189 .attr_group = &i2c_atmel_attr_grp,
190 .miscdev.fops = &i2c_atmel_ops,
191};
192
193static int i2c_atmel_probe(struct i2c_client *client,
194 const struct i2c_device_id *id)
195{
196 int rc;
197 struct tpm_chip *chip;
198 struct device *dev = &client->dev;
199
200 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
201 return -ENODEV;
202
203 chip = tpm_register_hardware(dev, &i2c_atmel);
204 if (!chip) {
205 dev_err(dev, "%s() error in tpm_register_hardware\n", __func__);
206 return -ENODEV;
207 }
208
209 chip->vendor.priv = devm_kzalloc(dev, sizeof(struct priv_data),
210 GFP_KERNEL);
211
212 /* Default timeouts */
213 chip->vendor.timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
214 chip->vendor.timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT);
215 chip->vendor.timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
216 chip->vendor.timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
217 chip->vendor.irq = 0;
218
219 /* There is no known way to probe for this device, and all version
220 * information seems to be read via TPM commands. Thus we rely on the
221 * TPM startup process in the common code to detect the device. */
222 if (tpm_get_timeouts(chip)) {
223 rc = -ENODEV;
224 goto out_err;
225 }
226
227 if (tpm_do_selftest(chip)) {
228 rc = -ENODEV;
229 goto out_err;
230 }
231
232 return 0;
233
234out_err:
235 tpm_dev_vendor_release(chip);
236 tpm_remove_hardware(chip->dev);
237 return rc;
238}
239
240static int i2c_atmel_remove(struct i2c_client *client)
241{
242 struct device *dev = &(client->dev);
243 struct tpm_chip *chip = dev_get_drvdata(dev);
244
245 if (chip)
246 tpm_dev_vendor_release(chip);
247 tpm_remove_hardware(dev);
248 kfree(chip);
249 return 0;
250}
251
252static const struct i2c_device_id i2c_atmel_id[] = {
253 {I2C_DRIVER_NAME, 0},
254 {}
255};
256MODULE_DEVICE_TABLE(i2c, i2c_atmel_id);
257
258#ifdef CONFIG_OF
259static const struct of_device_id i2c_atmel_of_match[] = {
260 {.compatible = "atmel,at97sc3204t"},
261 {},
262};
263MODULE_DEVICE_TABLE(of, i2c_atmel_of_match);
264#endif
265
266static SIMPLE_DEV_PM_OPS(i2c_atmel_pm_ops, tpm_pm_suspend, tpm_pm_resume);
267
268static struct i2c_driver i2c_atmel_driver = {
269 .id_table = i2c_atmel_id,
270 .probe = i2c_atmel_probe,
271 .remove = i2c_atmel_remove,
272 .driver = {
273 .name = I2C_DRIVER_NAME,
274 .owner = THIS_MODULE,
275 .pm = &i2c_atmel_pm_ops,
276 .of_match_table = of_match_ptr(i2c_atmel_of_match),
277 },
278};
279
280module_i2c_driver(i2c_atmel_driver);
281
282MODULE_AUTHOR("Jason Gunthorpe <jgunthorpe@obsidianresearch.com>");
283MODULE_DESCRIPTION("Atmel TPM I2C Driver");
284MODULE_LICENSE("GPL");