Commit | Line | Data |
---|---|---|
bf38b871 CR |
1 | /* |
2 | * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24 | |
8bb273f2 | 3 | * Copyright (C) 2009 - 2016 STMicroelectronics |
bf38b871 CR |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
19 | #include <linux/module.h> | |
20 | #include <linux/i2c.h> | |
21 | #include <linux/gpio.h> | |
22eb90db | 22 | #include <linux/gpio/consumer.h> |
bf38b871 CR |
23 | #include <linux/of_irq.h> |
24 | #include <linux/of_gpio.h> | |
22eb90db | 25 | #include <linux/acpi.h> |
bf38b871 CR |
26 | #include <linux/tpm.h> |
27 | #include <linux/platform_data/st33zp24.h> | |
28 | ||
29 | #include "st33zp24.h" | |
30 | ||
31 | #define TPM_DUMMY_BYTE 0xAA | |
bf38b871 CR |
32 | |
33 | struct st33zp24_i2c_phy { | |
34 | struct i2c_client *client; | |
35 | u8 buf[TPM_BUFSIZE + 1]; | |
36 | int io_lpcpd; | |
37 | }; | |
38 | ||
39 | /* | |
40 | * write8_reg | |
41 | * Send byte to the TIS register according to the ST33ZP24 I2C protocol. | |
42 | * @param: tpm_register, the tpm tis register where the data should be written | |
43 | * @param: tpm_data, the tpm_data to write inside the tpm_register | |
44 | * @param: tpm_size, The length of the data | |
45 | * @return: Returns negative errno, or else the number of bytes written. | |
46 | */ | |
47 | static int write8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size) | |
48 | { | |
49 | struct st33zp24_i2c_phy *phy = phy_id; | |
50 | ||
51 | phy->buf[0] = tpm_register; | |
52 | memcpy(phy->buf + 1, tpm_data, tpm_size); | |
53 | return i2c_master_send(phy->client, phy->buf, tpm_size + 1); | |
54 | } /* write8_reg() */ | |
55 | ||
56 | /* | |
57 | * read8_reg | |
58 | * Recv byte from the TIS register according to the ST33ZP24 I2C protocol. | |
59 | * @param: tpm_register, the tpm tis register where the data should be read | |
60 | * @param: tpm_data, the TPM response | |
61 | * @param: tpm_size, tpm TPM response size to read. | |
62 | * @return: number of byte read successfully: should be one if success. | |
63 | */ | |
64 | static int read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size) | |
65 | { | |
66 | struct st33zp24_i2c_phy *phy = phy_id; | |
67 | u8 status = 0; | |
68 | u8 data; | |
69 | ||
70 | data = TPM_DUMMY_BYTE; | |
71 | status = write8_reg(phy, tpm_register, &data, 1); | |
72 | if (status == 2) | |
73 | status = i2c_master_recv(phy->client, tpm_data, tpm_size); | |
74 | return status; | |
75 | } /* read8_reg() */ | |
76 | ||
77 | /* | |
78 | * st33zp24_i2c_send | |
79 | * Send byte to the TIS register according to the ST33ZP24 I2C protocol. | |
80 | * @param: phy_id, the phy description | |
81 | * @param: tpm_register, the tpm tis register where the data should be written | |
82 | * @param: tpm_data, the tpm_data to write inside the tpm_register | |
83 | * @param: tpm_size, the length of the data | |
84 | * @return: number of byte written successfully: should be one if success. | |
85 | */ | |
86 | static int st33zp24_i2c_send(void *phy_id, u8 tpm_register, u8 *tpm_data, | |
87 | int tpm_size) | |
88 | { | |
89 | return write8_reg(phy_id, tpm_register | TPM_WRITE_DIRECTION, tpm_data, | |
90 | tpm_size); | |
91 | } | |
92 | ||
93 | /* | |
94 | * st33zp24_i2c_recv | |
95 | * Recv byte from the TIS register according to the ST33ZP24 I2C protocol. | |
96 | * @param: phy_id, the phy description | |
97 | * @param: tpm_register, the tpm tis register where the data should be read | |
98 | * @param: tpm_data, the TPM response | |
99 | * @param: tpm_size, tpm TPM response size to read. | |
100 | * @return: number of byte read successfully: should be one if success. | |
101 | */ | |
102 | static int st33zp24_i2c_recv(void *phy_id, u8 tpm_register, u8 *tpm_data, | |
103 | int tpm_size) | |
104 | { | |
105 | return read8_reg(phy_id, tpm_register, tpm_data, tpm_size); | |
106 | } | |
107 | ||
108 | static const struct st33zp24_phy_ops i2c_phy_ops = { | |
109 | .send = st33zp24_i2c_send, | |
110 | .recv = st33zp24_i2c_recv, | |
111 | }; | |
112 | ||
22eb90db CR |
113 | static int st33zp24_i2c_acpi_request_resources(struct st33zp24_i2c_phy *phy) |
114 | { | |
115 | struct i2c_client *client = phy->client; | |
116 | const struct acpi_device_id *id; | |
117 | struct gpio_desc *gpiod_lpcpd; | |
118 | struct device *dev; | |
119 | ||
120 | if (!client) | |
121 | return -EINVAL; | |
122 | ||
123 | dev = &client->dev; | |
124 | ||
125 | /* Match the struct device against a given list of ACPI IDs */ | |
126 | id = acpi_match_device(dev->driver->acpi_match_table, dev); | |
127 | if (!id) | |
128 | return -ENODEV; | |
129 | ||
130 | /* Get LPCPD GPIO from ACPI */ | |
131 | gpiod_lpcpd = devm_gpiod_get_index(dev, "TPM IO LPCPD", 1, | |
132 | GPIOD_OUT_HIGH); | |
133 | if (IS_ERR(gpiod_lpcpd)) { | |
134 | dev_err(&client->dev, | |
135 | "Failed to retrieve lpcpd-gpios from acpi.\n"); | |
136 | phy->io_lpcpd = -1; | |
137 | /* | |
138 | * lpcpd pin is not specified. This is not an issue as | |
139 | * power management can be also managed by TPM specific | |
140 | * commands. So leave with a success status code. | |
141 | */ | |
142 | return 0; | |
143 | } | |
144 | ||
145 | phy->io_lpcpd = desc_to_gpio(gpiod_lpcpd); | |
146 | ||
147 | return 0; | |
148 | } | |
149 | ||
300796cd | 150 | static int st33zp24_i2c_of_request_resources(struct st33zp24_i2c_phy *phy) |
fec60f29 | 151 | { |
bf38b871 | 152 | struct device_node *pp; |
300796cd | 153 | struct i2c_client *client = phy->client; |
bf38b871 CR |
154 | int gpio; |
155 | int ret; | |
156 | ||
157 | pp = client->dev.of_node; | |
158 | if (!pp) { | |
159 | dev_err(&client->dev, "No platform data\n"); | |
160 | return -ENODEV; | |
161 | } | |
162 | ||
163 | /* Get GPIO from device tree */ | |
164 | gpio = of_get_named_gpio(pp, "lpcpd-gpios", 0); | |
165 | if (gpio < 0) { | |
166 | dev_err(&client->dev, | |
167 | "Failed to retrieve lpcpd-gpios from dts.\n"); | |
168 | phy->io_lpcpd = -1; | |
169 | /* | |
170 | * lpcpd pin is not specified. This is not an issue as | |
171 | * power management can be also managed by TPM specific | |
172 | * commands. So leave with a success status code. | |
173 | */ | |
174 | return 0; | |
175 | } | |
176 | /* GPIO request and configuration */ | |
177 | ret = devm_gpio_request_one(&client->dev, gpio, | |
178 | GPIOF_OUT_INIT_HIGH, "TPM IO LPCPD"); | |
179 | if (ret) { | |
180 | dev_err(&client->dev, "Failed to request lpcpd pin\n"); | |
181 | return -ENODEV; | |
182 | } | |
183 | phy->io_lpcpd = gpio; | |
184 | ||
185 | return 0; | |
186 | } | |
bf38b871 | 187 | |
300796cd CR |
188 | static int st33zp24_i2c_request_resources(struct i2c_client *client, |
189 | struct st33zp24_i2c_phy *phy) | |
bf38b871 CR |
190 | { |
191 | struct st33zp24_platform_data *pdata; | |
192 | int ret; | |
193 | ||
194 | pdata = client->dev.platform_data; | |
195 | if (!pdata) { | |
196 | dev_err(&client->dev, "No platform data\n"); | |
197 | return -ENODEV; | |
198 | } | |
199 | ||
200 | /* store for late use */ | |
201 | phy->io_lpcpd = pdata->io_lpcpd; | |
202 | ||
203 | if (gpio_is_valid(pdata->io_lpcpd)) { | |
204 | ret = devm_gpio_request_one(&client->dev, | |
205 | pdata->io_lpcpd, GPIOF_OUT_INIT_HIGH, | |
206 | "TPM IO_LPCPD"); | |
207 | if (ret) { | |
208 | dev_err(&client->dev, "Failed to request lpcpd pin\n"); | |
209 | return ret; | |
210 | } | |
211 | } | |
212 | ||
213 | return 0; | |
214 | } | |
215 | ||
216 | /* | |
217 | * st33zp24_i2c_probe initialize the TPM device | |
218 | * @param: client, the i2c_client drescription (TPM I2C description). | |
219 | * @param: id, the i2c_device_id struct. | |
220 | * @return: 0 in case of success. | |
221 | * -1 in other case. | |
222 | */ | |
223 | static int st33zp24_i2c_probe(struct i2c_client *client, | |
224 | const struct i2c_device_id *id) | |
225 | { | |
226 | int ret; | |
227 | struct st33zp24_platform_data *pdata; | |
228 | struct st33zp24_i2c_phy *phy; | |
229 | ||
230 | if (!client) { | |
231 | pr_info("%s: i2c client is NULL. Device not accessible.\n", | |
232 | __func__); | |
233 | return -ENODEV; | |
234 | } | |
235 | ||
236 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | |
237 | dev_info(&client->dev, "client not i2c capable\n"); | |
238 | return -ENODEV; | |
239 | } | |
240 | ||
241 | phy = devm_kzalloc(&client->dev, sizeof(struct st33zp24_i2c_phy), | |
242 | GFP_KERNEL); | |
243 | if (!phy) | |
244 | return -ENOMEM; | |
245 | ||
246 | phy->client = client; | |
247 | pdata = client->dev.platform_data; | |
248 | if (!pdata && client->dev.of_node) { | |
300796cd | 249 | ret = st33zp24_i2c_of_request_resources(phy); |
bf38b871 CR |
250 | if (ret) |
251 | return ret; | |
252 | } else if (pdata) { | |
300796cd | 253 | ret = st33zp24_i2c_request_resources(client, phy); |
fec60f29 CR |
254 | if (ret) |
255 | return ret; | |
22eb90db CR |
256 | } else if (ACPI_HANDLE(&client->dev)) { |
257 | ret = st33zp24_i2c_acpi_request_resources(phy); | |
258 | if (ret) | |
259 | return ret; | |
bf38b871 CR |
260 | } |
261 | ||
262 | return st33zp24_probe(phy, &i2c_phy_ops, &client->dev, client->irq, | |
263 | phy->io_lpcpd); | |
264 | } | |
265 | ||
266 | /* | |
267 | * st33zp24_i2c_remove remove the TPM device | |
268 | * @param: client, the i2c_client description (TPM I2C description). | |
269 | * @return: 0 in case of success. | |
270 | */ | |
271 | static int st33zp24_i2c_remove(struct i2c_client *client) | |
272 | { | |
273 | struct tpm_chip *chip = i2c_get_clientdata(client); | |
274 | ||
275 | return st33zp24_remove(chip); | |
276 | } | |
277 | ||
278 | static const struct i2c_device_id st33zp24_i2c_id[] = { | |
279 | {TPM_ST33_I2C, 0}, | |
280 | {} | |
281 | }; | |
282 | MODULE_DEVICE_TABLE(i2c, st33zp24_i2c_id); | |
283 | ||
bf38b871 CR |
284 | static const struct of_device_id of_st33zp24_i2c_match[] = { |
285 | { .compatible = "st,st33zp24-i2c", }, | |
286 | {} | |
287 | }; | |
288 | MODULE_DEVICE_TABLE(of, of_st33zp24_i2c_match); | |
bf38b871 | 289 | |
22eb90db CR |
290 | static const struct acpi_device_id st33zp24_i2c_acpi_match[] = { |
291 | {"SMO3324"}, | |
292 | {} | |
293 | }; | |
294 | MODULE_DEVICE_TABLE(acpi, st33zp24_i2c_acpi_match); | |
295 | ||
bf38b871 CR |
296 | static SIMPLE_DEV_PM_OPS(st33zp24_i2c_ops, st33zp24_pm_suspend, |
297 | st33zp24_pm_resume); | |
298 | ||
299 | static struct i2c_driver st33zp24_i2c_driver = { | |
300 | .driver = { | |
bf38b871 CR |
301 | .name = TPM_ST33_I2C, |
302 | .pm = &st33zp24_i2c_ops, | |
303 | .of_match_table = of_match_ptr(of_st33zp24_i2c_match), | |
22eb90db | 304 | .acpi_match_table = ACPI_PTR(st33zp24_i2c_acpi_match), |
bf38b871 CR |
305 | }, |
306 | .probe = st33zp24_i2c_probe, | |
307 | .remove = st33zp24_i2c_remove, | |
308 | .id_table = st33zp24_i2c_id | |
309 | }; | |
310 | ||
311 | module_i2c_driver(st33zp24_i2c_driver); | |
312 | ||
313 | MODULE_AUTHOR("TPM support (TPMsupport@list.st.com)"); | |
314 | MODULE_DESCRIPTION("STM TPM 1.2 I2C ST33 Driver"); | |
315 | MODULE_VERSION("1.3.0"); | |
316 | MODULE_LICENSE("GPL"); |