]>
Commit | Line | Data |
---|---|---|
1ccea77e | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
a2871c62 JG |
2 | /* |
3 | * ATMEL I2C TPM AT97SC3204T | |
4 | * | |
5 | * Copyright (C) 2012 V Lab Technologies | |
6 | * Teddy Reed <teddy@prosauce.org> | |
7 | * Copyright (C) 2013, Obsidian Research Corp. | |
8 | * Jason Gunthorpe <jgunthorpe@obsidianresearch.com> | |
9 | * Device driver for ATMEL I2C TPMs. | |
10 | * | |
11 | * Teddy Reed determined the basic I2C command flow, unlike other I2C TPM | |
12 | * devices the raw TCG formatted TPM command data is written via I2C and then | |
13 | * raw TCG formatted TPM command data is returned via I2C. | |
14 | * | |
15 | * TGC status/locality/etc functions seen in the LPC implementation do not | |
16 | * seem to be present. | |
a2871c62 JG |
17 | */ |
18 | #include <linux/init.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/moduleparam.h> | |
21 | #include <linux/slab.h> | |
22 | #include <linux/i2c.h> | |
23 | #include "tpm.h" | |
24 | ||
25 | #define I2C_DRIVER_NAME "tpm_i2c_atmel" | |
26 | ||
27 | #define TPM_I2C_SHORT_TIMEOUT 750 /* ms */ | |
28 | #define TPM_I2C_LONG_TIMEOUT 2000 /* 2 sec */ | |
29 | ||
30 | #define ATMEL_STS_OK 1 | |
31 | ||
32 | struct priv_data { | |
33 | size_t len; | |
34 | /* This is the amount we read on the first try. 25 was chosen to fit a | |
35 | * fair number of read responses in the buffer so a 2nd retry can be | |
36 | * avoided in small message cases. */ | |
b34b77a9 | 37 | u8 buffer[sizeof(struct tpm_header) + 25]; |
a2871c62 JG |
38 | }; |
39 | ||
40 | static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len) | |
41 | { | |
9e0d39d8 | 42 | struct priv_data *priv = dev_get_drvdata(&chip->dev); |
8cfffc9d | 43 | struct i2c_client *client = to_i2c_client(chip->dev.parent); |
a2871c62 JG |
44 | s32 status; |
45 | ||
46 | priv->len = 0; | |
47 | ||
48 | if (len <= 2) | |
49 | return -EIO; | |
50 | ||
51 | status = i2c_master_send(client, buf, len); | |
52 | ||
8cfffc9d | 53 | dev_dbg(&chip->dev, |
a2871c62 JG |
54 | "%s(buf=%*ph len=%0zx) -> sts=%d\n", __func__, |
55 | (int)min_t(size_t, 64, len), buf, len, status); | |
f5595f5b JS |
56 | |
57 | if (status < 0) | |
58 | return status; | |
59 | ||
442601e8 JS |
60 | /* The upper layer does not support incomplete sends. */ |
61 | if (status != len) | |
62 | return -E2BIG; | |
63 | ||
f5595f5b | 64 | return 0; |
a2871c62 JG |
65 | } |
66 | ||
67 | static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count) | |
68 | { | |
9e0d39d8 | 69 | struct priv_data *priv = dev_get_drvdata(&chip->dev); |
8cfffc9d | 70 | struct i2c_client *client = to_i2c_client(chip->dev.parent); |
b34b77a9 | 71 | struct tpm_header *hdr = (struct tpm_header *)priv->buffer; |
a2871c62 JG |
72 | u32 expected_len; |
73 | int rc; | |
74 | ||
75 | if (priv->len == 0) | |
76 | return -EIO; | |
77 | ||
78 | /* Get the message size from the message header, if we didn't get the | |
79 | * whole message in read_status then we need to re-read the | |
80 | * message. */ | |
81 | expected_len = be32_to_cpu(hdr->length); | |
82 | if (expected_len > count) | |
83 | return -ENOMEM; | |
84 | ||
85 | if (priv->len >= expected_len) { | |
8cfffc9d | 86 | dev_dbg(&chip->dev, |
a2871c62 JG |
87 | "%s early(buf=%*ph count=%0zx) -> ret=%d\n", __func__, |
88 | (int)min_t(size_t, 64, expected_len), buf, count, | |
89 | expected_len); | |
90 | memcpy(buf, priv->buffer, expected_len); | |
91 | return expected_len; | |
92 | } | |
93 | ||
94 | rc = i2c_master_recv(client, buf, expected_len); | |
8cfffc9d | 95 | dev_dbg(&chip->dev, |
a2871c62 JG |
96 | "%s reread(buf=%*ph count=%0zx) -> ret=%d\n", __func__, |
97 | (int)min_t(size_t, 64, expected_len), buf, count, | |
98 | expected_len); | |
99 | return rc; | |
100 | } | |
101 | ||
102 | static void i2c_atmel_cancel(struct tpm_chip *chip) | |
103 | { | |
8cfffc9d | 104 | dev_err(&chip->dev, "TPM operation cancellation was requested, but is not supported"); |
a2871c62 JG |
105 | } |
106 | ||
107 | static u8 i2c_atmel_read_status(struct tpm_chip *chip) | |
108 | { | |
9e0d39d8 | 109 | struct priv_data *priv = dev_get_drvdata(&chip->dev); |
8cfffc9d | 110 | struct i2c_client *client = to_i2c_client(chip->dev.parent); |
a2871c62 JG |
111 | int rc; |
112 | ||
113 | /* The TPM fails the I2C read until it is ready, so we do the entire | |
114 | * transfer here and buffer it locally. This way the common code can | |
115 | * properly handle the timeouts. */ | |
116 | priv->len = 0; | |
117 | memset(priv->buffer, 0, sizeof(priv->buffer)); | |
118 | ||
119 | ||
120 | /* Once the TPM has completed the command the command remains readable | |
121 | * until another command is issued. */ | |
122 | rc = i2c_master_recv(client, priv->buffer, sizeof(priv->buffer)); | |
8cfffc9d | 123 | dev_dbg(&chip->dev, |
a2871c62 JG |
124 | "%s: sts=%d", __func__, rc); |
125 | if (rc <= 0) | |
126 | return 0; | |
127 | ||
128 | priv->len = rc; | |
129 | ||
130 | return ATMEL_STS_OK; | |
131 | } | |
132 | ||
a2871c62 JG |
133 | static bool i2c_atmel_req_canceled(struct tpm_chip *chip, u8 status) |
134 | { | |
ba6a09d7 | 135 | return false; |
a2871c62 JG |
136 | } |
137 | ||
01ad1fa7 | 138 | static const struct tpm_class_ops i2c_atmel = { |
cae8b441 | 139 | .flags = TPM_OPS_AUTO_STARTUP, |
a2871c62 JG |
140 | .status = i2c_atmel_read_status, |
141 | .recv = i2c_atmel_recv, | |
142 | .send = i2c_atmel_send, | |
143 | .cancel = i2c_atmel_cancel, | |
144 | .req_complete_mask = ATMEL_STS_OK, | |
145 | .req_complete_val = ATMEL_STS_OK, | |
146 | .req_canceled = i2c_atmel_req_canceled, | |
a2871c62 JG |
147 | }; |
148 | ||
149 | static int i2c_atmel_probe(struct i2c_client *client, | |
150 | const struct i2c_device_id *id) | |
151 | { | |
a2871c62 JG |
152 | struct tpm_chip *chip; |
153 | struct device *dev = &client->dev; | |
9e0d39d8 | 154 | struct priv_data *priv; |
a2871c62 JG |
155 | |
156 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) | |
157 | return -ENODEV; | |
158 | ||
afb5abc2 JS |
159 | chip = tpmm_chip_alloc(dev, &i2c_atmel); |
160 | if (IS_ERR(chip)) | |
161 | return PTR_ERR(chip); | |
a2871c62 | 162 | |
9e0d39d8 CR |
163 | priv = devm_kzalloc(dev, sizeof(struct priv_data), GFP_KERNEL); |
164 | if (!priv) | |
afb5abc2 | 165 | return -ENOMEM; |
a2871c62 JG |
166 | |
167 | /* Default timeouts */ | |
af782f33 CR |
168 | chip->timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); |
169 | chip->timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT); | |
170 | chip->timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); | |
171 | chip->timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); | |
a2871c62 | 172 | |
9e0d39d8 CR |
173 | dev_set_drvdata(&chip->dev, priv); |
174 | ||
a2871c62 JG |
175 | /* There is no known way to probe for this device, and all version |
176 | * information seems to be read via TPM commands. Thus we rely on the | |
177 | * TPM startup process in the common code to detect the device. */ | |
a2871c62 | 178 | |
afb5abc2 | 179 | return tpm_chip_register(chip); |
a2871c62 JG |
180 | } |
181 | ||
182 | static int i2c_atmel_remove(struct i2c_client *client) | |
183 | { | |
184 | struct device *dev = &(client->dev); | |
185 | struct tpm_chip *chip = dev_get_drvdata(dev); | |
afb5abc2 | 186 | tpm_chip_unregister(chip); |
a2871c62 JG |
187 | return 0; |
188 | } | |
189 | ||
190 | static const struct i2c_device_id i2c_atmel_id[] = { | |
191 | {I2C_DRIVER_NAME, 0}, | |
192 | {} | |
193 | }; | |
194 | MODULE_DEVICE_TABLE(i2c, i2c_atmel_id); | |
195 | ||
196 | #ifdef CONFIG_OF | |
197 | static const struct of_device_id i2c_atmel_of_match[] = { | |
198 | {.compatible = "atmel,at97sc3204t"}, | |
199 | {}, | |
200 | }; | |
201 | MODULE_DEVICE_TABLE(of, i2c_atmel_of_match); | |
202 | #endif | |
203 | ||
204 | static SIMPLE_DEV_PM_OPS(i2c_atmel_pm_ops, tpm_pm_suspend, tpm_pm_resume); | |
205 | ||
206 | static struct i2c_driver i2c_atmel_driver = { | |
207 | .id_table = i2c_atmel_id, | |
208 | .probe = i2c_atmel_probe, | |
209 | .remove = i2c_atmel_remove, | |
210 | .driver = { | |
211 | .name = I2C_DRIVER_NAME, | |
a2871c62 JG |
212 | .pm = &i2c_atmel_pm_ops, |
213 | .of_match_table = of_match_ptr(i2c_atmel_of_match), | |
214 | }, | |
215 | }; | |
216 | ||
217 | module_i2c_driver(i2c_atmel_driver); | |
218 | ||
219 | MODULE_AUTHOR("Jason Gunthorpe <jgunthorpe@obsidianresearch.com>"); | |
220 | MODULE_DESCRIPTION("Atmel TPM I2C Driver"); | |
221 | MODULE_LICENSE("GPL"); |