Commit | Line | Data |
---|---|---|
a2871c62 JG |
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 | ||
44 | struct 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 | ||
52 | static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len) | |
53 | { | |
54 | struct priv_data *priv = chip->vendor.priv; | |
71ed848f | 55 | struct i2c_client *client = to_i2c_client(chip->pdev); |
a2871c62 JG |
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 | ||
71ed848f | 65 | dev_dbg(chip->pdev, |
a2871c62 JG |
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 | ||
71 | static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count) | |
72 | { | |
73 | struct priv_data *priv = chip->vendor.priv; | |
71ed848f | 74 | struct i2c_client *client = to_i2c_client(chip->pdev); |
a2871c62 JG |
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) { | |
71ed848f | 91 | dev_dbg(chip->pdev, |
a2871c62 JG |
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); | |
71ed848f | 100 | dev_dbg(chip->pdev, |
a2871c62 JG |
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 | ||
107 | static void i2c_atmel_cancel(struct tpm_chip *chip) | |
108 | { | |
71ed848f | 109 | dev_err(chip->pdev, "TPM operation cancellation was requested, but is not supported"); |
a2871c62 JG |
110 | } |
111 | ||
112 | static u8 i2c_atmel_read_status(struct tpm_chip *chip) | |
113 | { | |
114 | struct priv_data *priv = chip->vendor.priv; | |
71ed848f | 115 | struct i2c_client *client = to_i2c_client(chip->pdev); |
a2871c62 JG |
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)); | |
71ed848f | 128 | dev_dbg(chip->pdev, |
a2871c62 JG |
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 | ||
a2871c62 JG |
138 | static bool i2c_atmel_req_canceled(struct tpm_chip *chip, u8 status) |
139 | { | |
ba6a09d7 | 140 | return false; |
a2871c62 JG |
141 | } |
142 | ||
01ad1fa7 | 143 | static const struct tpm_class_ops i2c_atmel = { |
a2871c62 JG |
144 | .status = i2c_atmel_read_status, |
145 | .recv = i2c_atmel_recv, | |
146 | .send = i2c_atmel_send, | |
147 | .cancel = i2c_atmel_cancel, | |
148 | .req_complete_mask = ATMEL_STS_OK, | |
149 | .req_complete_val = ATMEL_STS_OK, | |
150 | .req_canceled = i2c_atmel_req_canceled, | |
a2871c62 JG |
151 | }; |
152 | ||
153 | static int i2c_atmel_probe(struct i2c_client *client, | |
154 | const struct i2c_device_id *id) | |
155 | { | |
a2871c62 JG |
156 | struct tpm_chip *chip; |
157 | struct device *dev = &client->dev; | |
158 | ||
159 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) | |
160 | return -ENODEV; | |
161 | ||
afb5abc2 JS |
162 | chip = tpmm_chip_alloc(dev, &i2c_atmel); |
163 | if (IS_ERR(chip)) | |
164 | return PTR_ERR(chip); | |
a2871c62 JG |
165 | |
166 | chip->vendor.priv = devm_kzalloc(dev, sizeof(struct priv_data), | |
167 | GFP_KERNEL); | |
afb5abc2 JS |
168 | if (!chip->vendor.priv) |
169 | return -ENOMEM; | |
a2871c62 JG |
170 | |
171 | /* Default timeouts */ | |
172 | chip->vendor.timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); | |
173 | chip->vendor.timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT); | |
174 | chip->vendor.timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); | |
175 | chip->vendor.timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); | |
176 | chip->vendor.irq = 0; | |
177 | ||
178 | /* There is no known way to probe for this device, and all version | |
179 | * information seems to be read via TPM commands. Thus we rely on the | |
180 | * TPM startup process in the common code to detect the device. */ | |
afb5abc2 JS |
181 | if (tpm_get_timeouts(chip)) |
182 | return -ENODEV; | |
a2871c62 | 183 | |
afb5abc2 JS |
184 | if (tpm_do_selftest(chip)) |
185 | return -ENODEV; | |
a2871c62 | 186 | |
afb5abc2 | 187 | return tpm_chip_register(chip); |
a2871c62 JG |
188 | } |
189 | ||
190 | static int i2c_atmel_remove(struct i2c_client *client) | |
191 | { | |
192 | struct device *dev = &(client->dev); | |
193 | struct tpm_chip *chip = dev_get_drvdata(dev); | |
afb5abc2 | 194 | tpm_chip_unregister(chip); |
a2871c62 JG |
195 | return 0; |
196 | } | |
197 | ||
198 | static const struct i2c_device_id i2c_atmel_id[] = { | |
199 | {I2C_DRIVER_NAME, 0}, | |
200 | {} | |
201 | }; | |
202 | MODULE_DEVICE_TABLE(i2c, i2c_atmel_id); | |
203 | ||
204 | #ifdef CONFIG_OF | |
205 | static const struct of_device_id i2c_atmel_of_match[] = { | |
206 | {.compatible = "atmel,at97sc3204t"}, | |
207 | {}, | |
208 | }; | |
209 | MODULE_DEVICE_TABLE(of, i2c_atmel_of_match); | |
210 | #endif | |
211 | ||
212 | static SIMPLE_DEV_PM_OPS(i2c_atmel_pm_ops, tpm_pm_suspend, tpm_pm_resume); | |
213 | ||
214 | static struct i2c_driver i2c_atmel_driver = { | |
215 | .id_table = i2c_atmel_id, | |
216 | .probe = i2c_atmel_probe, | |
217 | .remove = i2c_atmel_remove, | |
218 | .driver = { | |
219 | .name = I2C_DRIVER_NAME, | |
220 | .owner = THIS_MODULE, | |
221 | .pm = &i2c_atmel_pm_ops, | |
222 | .of_match_table = of_match_ptr(i2c_atmel_of_match), | |
223 | }, | |
224 | }; | |
225 | ||
226 | module_i2c_driver(i2c_atmel_driver); | |
227 | ||
228 | MODULE_AUTHOR("Jason Gunthorpe <jgunthorpe@obsidianresearch.com>"); | |
229 | MODULE_DESCRIPTION("Atmel TPM I2C Driver"); | |
230 | MODULE_LICENSE("GPL"); |