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