18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24 48c2ecf20Sopenharmony_ci * Copyright (C) 2009 - 2016 STMicroelectronics 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/i2c.h> 98c2ecf20Sopenharmony_ci#include <linux/gpio.h> 108c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 118c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 128c2ecf20Sopenharmony_ci#include <linux/of_gpio.h> 138c2ecf20Sopenharmony_ci#include <linux/acpi.h> 148c2ecf20Sopenharmony_ci#include <linux/tpm.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_data/st33zp24.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "../tpm.h" 188c2ecf20Sopenharmony_ci#include "st33zp24.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define TPM_DUMMY_BYTE 0xAA 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistruct st33zp24_i2c_phy { 238c2ecf20Sopenharmony_ci struct i2c_client *client; 248c2ecf20Sopenharmony_ci u8 buf[ST33ZP24_BUFSIZE + 1]; 258c2ecf20Sopenharmony_ci int io_lpcpd; 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * write8_reg 308c2ecf20Sopenharmony_ci * Send byte to the TIS register according to the ST33ZP24 I2C protocol. 318c2ecf20Sopenharmony_ci * @param: tpm_register, the tpm tis register where the data should be written 328c2ecf20Sopenharmony_ci * @param: tpm_data, the tpm_data to write inside the tpm_register 338c2ecf20Sopenharmony_ci * @param: tpm_size, The length of the data 348c2ecf20Sopenharmony_ci * @return: Returns negative errno, or else the number of bytes written. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_cistatic int write8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci struct st33zp24_i2c_phy *phy = phy_id; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci phy->buf[0] = tpm_register; 418c2ecf20Sopenharmony_ci memcpy(phy->buf + 1, tpm_data, tpm_size); 428c2ecf20Sopenharmony_ci return i2c_master_send(phy->client, phy->buf, tpm_size + 1); 438c2ecf20Sopenharmony_ci} /* write8_reg() */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* 468c2ecf20Sopenharmony_ci * read8_reg 478c2ecf20Sopenharmony_ci * Recv byte from the TIS register according to the ST33ZP24 I2C protocol. 488c2ecf20Sopenharmony_ci * @param: tpm_register, the tpm tis register where the data should be read 498c2ecf20Sopenharmony_ci * @param: tpm_data, the TPM response 508c2ecf20Sopenharmony_ci * @param: tpm_size, tpm TPM response size to read. 518c2ecf20Sopenharmony_ci * @return: number of byte read successfully: should be one if success. 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_cistatic int read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct st33zp24_i2c_phy *phy = phy_id; 568c2ecf20Sopenharmony_ci u8 status = 0; 578c2ecf20Sopenharmony_ci u8 data; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci data = TPM_DUMMY_BYTE; 608c2ecf20Sopenharmony_ci status = write8_reg(phy, tpm_register, &data, 1); 618c2ecf20Sopenharmony_ci if (status == 2) 628c2ecf20Sopenharmony_ci status = i2c_master_recv(phy->client, tpm_data, tpm_size); 638c2ecf20Sopenharmony_ci return status; 648c2ecf20Sopenharmony_ci} /* read8_reg() */ 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* 678c2ecf20Sopenharmony_ci * st33zp24_i2c_send 688c2ecf20Sopenharmony_ci * Send byte to the TIS register according to the ST33ZP24 I2C protocol. 698c2ecf20Sopenharmony_ci * @param: phy_id, the phy description 708c2ecf20Sopenharmony_ci * @param: tpm_register, the tpm tis register where the data should be written 718c2ecf20Sopenharmony_ci * @param: tpm_data, the tpm_data to write inside the tpm_register 728c2ecf20Sopenharmony_ci * @param: tpm_size, the length of the data 738c2ecf20Sopenharmony_ci * @return: number of byte written successfully: should be one if success. 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_cistatic int st33zp24_i2c_send(void *phy_id, u8 tpm_register, u8 *tpm_data, 768c2ecf20Sopenharmony_ci int tpm_size) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci return write8_reg(phy_id, tpm_register | TPM_WRITE_DIRECTION, tpm_data, 798c2ecf20Sopenharmony_ci tpm_size); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* 838c2ecf20Sopenharmony_ci * st33zp24_i2c_recv 848c2ecf20Sopenharmony_ci * Recv byte from the TIS register according to the ST33ZP24 I2C protocol. 858c2ecf20Sopenharmony_ci * @param: phy_id, the phy description 868c2ecf20Sopenharmony_ci * @param: tpm_register, the tpm tis register where the data should be read 878c2ecf20Sopenharmony_ci * @param: tpm_data, the TPM response 888c2ecf20Sopenharmony_ci * @param: tpm_size, tpm TPM response size to read. 898c2ecf20Sopenharmony_ci * @return: number of byte read successfully: should be one if success. 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_cistatic int st33zp24_i2c_recv(void *phy_id, u8 tpm_register, u8 *tpm_data, 928c2ecf20Sopenharmony_ci int tpm_size) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci return read8_reg(phy_id, tpm_register, tpm_data, tpm_size); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic const struct st33zp24_phy_ops i2c_phy_ops = { 988c2ecf20Sopenharmony_ci .send = st33zp24_i2c_send, 998c2ecf20Sopenharmony_ci .recv = st33zp24_i2c_recv, 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic const struct acpi_gpio_params lpcpd_gpios = { 1, 0, false }; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic const struct acpi_gpio_mapping acpi_st33zp24_gpios[] = { 1058c2ecf20Sopenharmony_ci { "lpcpd-gpios", &lpcpd_gpios, 1 }, 1068c2ecf20Sopenharmony_ci {}, 1078c2ecf20Sopenharmony_ci}; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic int st33zp24_i2c_acpi_request_resources(struct i2c_client *client) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct tpm_chip *chip = i2c_get_clientdata(client); 1128c2ecf20Sopenharmony_ci struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); 1138c2ecf20Sopenharmony_ci struct st33zp24_i2c_phy *phy = tpm_dev->phy_id; 1148c2ecf20Sopenharmony_ci struct gpio_desc *gpiod_lpcpd; 1158c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 1168c2ecf20Sopenharmony_ci int ret; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci ret = devm_acpi_dev_add_driver_gpios(dev, acpi_st33zp24_gpios); 1198c2ecf20Sopenharmony_ci if (ret) 1208c2ecf20Sopenharmony_ci return ret; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* Get LPCPD GPIO from ACPI */ 1238c2ecf20Sopenharmony_ci gpiod_lpcpd = devm_gpiod_get(dev, "lpcpd", GPIOD_OUT_HIGH); 1248c2ecf20Sopenharmony_ci if (IS_ERR(gpiod_lpcpd)) { 1258c2ecf20Sopenharmony_ci dev_err(&client->dev, 1268c2ecf20Sopenharmony_ci "Failed to retrieve lpcpd-gpios from acpi.\n"); 1278c2ecf20Sopenharmony_ci phy->io_lpcpd = -1; 1288c2ecf20Sopenharmony_ci /* 1298c2ecf20Sopenharmony_ci * lpcpd pin is not specified. This is not an issue as 1308c2ecf20Sopenharmony_ci * power management can be also managed by TPM specific 1318c2ecf20Sopenharmony_ci * commands. So leave with a success status code. 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ci return 0; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci phy->io_lpcpd = desc_to_gpio(gpiod_lpcpd); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int st33zp24_i2c_of_request_resources(struct i2c_client *client) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct tpm_chip *chip = i2c_get_clientdata(client); 1448c2ecf20Sopenharmony_ci struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); 1458c2ecf20Sopenharmony_ci struct st33zp24_i2c_phy *phy = tpm_dev->phy_id; 1468c2ecf20Sopenharmony_ci struct device_node *pp; 1478c2ecf20Sopenharmony_ci int gpio; 1488c2ecf20Sopenharmony_ci int ret; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci pp = client->dev.of_node; 1518c2ecf20Sopenharmony_ci if (!pp) { 1528c2ecf20Sopenharmony_ci dev_err(&client->dev, "No platform data\n"); 1538c2ecf20Sopenharmony_ci return -ENODEV; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* Get GPIO from device tree */ 1578c2ecf20Sopenharmony_ci gpio = of_get_named_gpio(pp, "lpcpd-gpios", 0); 1588c2ecf20Sopenharmony_ci if (gpio < 0) { 1598c2ecf20Sopenharmony_ci dev_err(&client->dev, 1608c2ecf20Sopenharmony_ci "Failed to retrieve lpcpd-gpios from dts.\n"); 1618c2ecf20Sopenharmony_ci phy->io_lpcpd = -1; 1628c2ecf20Sopenharmony_ci /* 1638c2ecf20Sopenharmony_ci * lpcpd pin is not specified. This is not an issue as 1648c2ecf20Sopenharmony_ci * power management can be also managed by TPM specific 1658c2ecf20Sopenharmony_ci * commands. So leave with a success status code. 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_ci return 0; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci /* GPIO request and configuration */ 1708c2ecf20Sopenharmony_ci ret = devm_gpio_request_one(&client->dev, gpio, 1718c2ecf20Sopenharmony_ci GPIOF_OUT_INIT_HIGH, "TPM IO LPCPD"); 1728c2ecf20Sopenharmony_ci if (ret) { 1738c2ecf20Sopenharmony_ci dev_err(&client->dev, "Failed to request lpcpd pin\n"); 1748c2ecf20Sopenharmony_ci return -ENODEV; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci phy->io_lpcpd = gpio; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic int st33zp24_i2c_request_resources(struct i2c_client *client) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct tpm_chip *chip = i2c_get_clientdata(client); 1848c2ecf20Sopenharmony_ci struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); 1858c2ecf20Sopenharmony_ci struct st33zp24_i2c_phy *phy = tpm_dev->phy_id; 1868c2ecf20Sopenharmony_ci struct st33zp24_platform_data *pdata; 1878c2ecf20Sopenharmony_ci int ret; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci pdata = client->dev.platform_data; 1908c2ecf20Sopenharmony_ci if (!pdata) { 1918c2ecf20Sopenharmony_ci dev_err(&client->dev, "No platform data\n"); 1928c2ecf20Sopenharmony_ci return -ENODEV; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* store for late use */ 1968c2ecf20Sopenharmony_ci phy->io_lpcpd = pdata->io_lpcpd; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (gpio_is_valid(pdata->io_lpcpd)) { 1998c2ecf20Sopenharmony_ci ret = devm_gpio_request_one(&client->dev, 2008c2ecf20Sopenharmony_ci pdata->io_lpcpd, GPIOF_OUT_INIT_HIGH, 2018c2ecf20Sopenharmony_ci "TPM IO_LPCPD"); 2028c2ecf20Sopenharmony_ci if (ret) { 2038c2ecf20Sopenharmony_ci dev_err(&client->dev, "Failed to request lpcpd pin\n"); 2048c2ecf20Sopenharmony_ci return ret; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci return 0; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* 2128c2ecf20Sopenharmony_ci * st33zp24_i2c_probe initialize the TPM device 2138c2ecf20Sopenharmony_ci * @param: client, the i2c_client description (TPM I2C description). 2148c2ecf20Sopenharmony_ci * @param: id, the i2c_device_id struct. 2158c2ecf20Sopenharmony_ci * @return: 0 in case of success. 2168c2ecf20Sopenharmony_ci * -1 in other case. 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_cistatic int st33zp24_i2c_probe(struct i2c_client *client, 2198c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci int ret; 2228c2ecf20Sopenharmony_ci struct st33zp24_platform_data *pdata; 2238c2ecf20Sopenharmony_ci struct st33zp24_i2c_phy *phy; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (!client) { 2268c2ecf20Sopenharmony_ci pr_info("%s: i2c client is NULL. Device not accessible.\n", 2278c2ecf20Sopenharmony_ci __func__); 2288c2ecf20Sopenharmony_ci return -ENODEV; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { 2328c2ecf20Sopenharmony_ci dev_info(&client->dev, "client not i2c capable\n"); 2338c2ecf20Sopenharmony_ci return -ENODEV; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci phy = devm_kzalloc(&client->dev, sizeof(struct st33zp24_i2c_phy), 2378c2ecf20Sopenharmony_ci GFP_KERNEL); 2388c2ecf20Sopenharmony_ci if (!phy) 2398c2ecf20Sopenharmony_ci return -ENOMEM; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci phy->client = client; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci pdata = client->dev.platform_data; 2448c2ecf20Sopenharmony_ci if (!pdata && client->dev.of_node) { 2458c2ecf20Sopenharmony_ci ret = st33zp24_i2c_of_request_resources(client); 2468c2ecf20Sopenharmony_ci if (ret) 2478c2ecf20Sopenharmony_ci return ret; 2488c2ecf20Sopenharmony_ci } else if (pdata) { 2498c2ecf20Sopenharmony_ci ret = st33zp24_i2c_request_resources(client); 2508c2ecf20Sopenharmony_ci if (ret) 2518c2ecf20Sopenharmony_ci return ret; 2528c2ecf20Sopenharmony_ci } else if (ACPI_HANDLE(&client->dev)) { 2538c2ecf20Sopenharmony_ci ret = st33zp24_i2c_acpi_request_resources(client); 2548c2ecf20Sopenharmony_ci if (ret) 2558c2ecf20Sopenharmony_ci return ret; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci return st33zp24_probe(phy, &i2c_phy_ops, &client->dev, client->irq, 2598c2ecf20Sopenharmony_ci phy->io_lpcpd); 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci/* 2638c2ecf20Sopenharmony_ci * st33zp24_i2c_remove remove the TPM device 2648c2ecf20Sopenharmony_ci * @param: client, the i2c_client description (TPM I2C description). 2658c2ecf20Sopenharmony_ci * @return: 0 in case of success. 2668c2ecf20Sopenharmony_ci */ 2678c2ecf20Sopenharmony_cistatic int st33zp24_i2c_remove(struct i2c_client *client) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci struct tpm_chip *chip = i2c_get_clientdata(client); 2708c2ecf20Sopenharmony_ci int ret; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci ret = st33zp24_remove(chip); 2738c2ecf20Sopenharmony_ci if (ret) 2748c2ecf20Sopenharmony_ci return ret; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci return 0; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic const struct i2c_device_id st33zp24_i2c_id[] = { 2808c2ecf20Sopenharmony_ci {TPM_ST33_I2C, 0}, 2818c2ecf20Sopenharmony_ci {} 2828c2ecf20Sopenharmony_ci}; 2838c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, st33zp24_i2c_id); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic const struct of_device_id of_st33zp24_i2c_match[] = { 2868c2ecf20Sopenharmony_ci { .compatible = "st,st33zp24-i2c", }, 2878c2ecf20Sopenharmony_ci {} 2888c2ecf20Sopenharmony_ci}; 2898c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_st33zp24_i2c_match); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic const struct acpi_device_id st33zp24_i2c_acpi_match[] = { 2928c2ecf20Sopenharmony_ci {"SMO3324"}, 2938c2ecf20Sopenharmony_ci {} 2948c2ecf20Sopenharmony_ci}; 2958c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, st33zp24_i2c_acpi_match); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(st33zp24_i2c_ops, st33zp24_pm_suspend, 2988c2ecf20Sopenharmony_ci st33zp24_pm_resume); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic struct i2c_driver st33zp24_i2c_driver = { 3018c2ecf20Sopenharmony_ci .driver = { 3028c2ecf20Sopenharmony_ci .name = TPM_ST33_I2C, 3038c2ecf20Sopenharmony_ci .pm = &st33zp24_i2c_ops, 3048c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(of_st33zp24_i2c_match), 3058c2ecf20Sopenharmony_ci .acpi_match_table = ACPI_PTR(st33zp24_i2c_acpi_match), 3068c2ecf20Sopenharmony_ci }, 3078c2ecf20Sopenharmony_ci .probe = st33zp24_i2c_probe, 3088c2ecf20Sopenharmony_ci .remove = st33zp24_i2c_remove, 3098c2ecf20Sopenharmony_ci .id_table = st33zp24_i2c_id 3108c2ecf20Sopenharmony_ci}; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cimodule_i2c_driver(st33zp24_i2c_driver); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ciMODULE_AUTHOR("TPM support (TPMsupport@list.st.com)"); 3158c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("STM TPM 1.2 I2C ST33 Driver"); 3168c2ecf20Sopenharmony_ciMODULE_VERSION("1.3.0"); 3178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 318