1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright 2022 IBM Corp.
4 */
5
6#include <common.h>
7#include <dm.h>
8#include <fdtdec.h>
9#include <i2c.h>
10#include <log.h>
11#include <tpm-v2.h>
12#include <linux/bitops.h>
13#include <linux/delay.h>
14#include <linux/errno.h>
15#include <linux/compiler.h>
16#include <linux/types.h>
17#include <linux/unaligned/be_byteshift.h>
18#include <asm-generic/gpio.h>
19
20#include "tpm_tis.h"
21#include "tpm_internal.h"
22
23struct tpm_tis_chip_data {
24	unsigned int pcr_count;
25	unsigned int pcr_select_min;
26};
27
28static uint tpm_tis_i2c_address_to_register(u32 addr)
29{
30	addr &= 0xFFF;
31
32	/*
33	 * Adapt register addresses that have changed compared to older TIS
34	 * version.
35	 */
36	switch (addr) {
37	case TPM_ACCESS(0):
38		return 0x04;
39	case TPM_DID_VID(0):
40		return 0x48;
41	case TPM_RID(0):
42		return 0x4C;
43	default:
44		return addr;
45	}
46}
47
48static int tpm_tis_i2c_read(struct udevice *dev, u32 addr, u16 len, u8 *in)
49{
50	int rc;
51	int count = 0;
52	uint reg = tpm_tis_i2c_address_to_register(addr);
53
54	do {
55		rc = dm_i2c_read(dev, reg, in, len);
56		udelay(SLEEP_DURATION_US);
57	} while (rc && count++ < MAX_COUNT);
58
59	return rc;
60}
61
62static int tpm_tis_i2c_write(struct udevice *dev, u32 addr, u16 len,
63			     const u8 *out)
64{
65	int rc;
66	int count = 0;
67	uint reg = tpm_tis_i2c_address_to_register(addr);
68
69	do {
70		rc = dm_i2c_write(dev, reg, out, len);
71		udelay(SLEEP_DURATION_US);
72	} while (rc && count++ < MAX_COUNT);
73
74	return rc;
75}
76
77static int tpm_tis_i2c_read32(struct udevice *dev, u32 addr, u32 *result)
78{
79	__le32 result_le;
80	int rc;
81
82	rc = tpm_tis_i2c_read(dev, addr, sizeof(u32), (u8 *)&result_le);
83	if (!rc)
84		*result = le32_to_cpu(result_le);
85
86	return rc;
87}
88
89static int tpm_tis_i2c_write32(struct udevice *dev, u32 addr, u32 value)
90{
91	__le32 value_le = cpu_to_le32(value);
92
93	return tpm_tis_i2c_write(dev, addr, sizeof(value), (u8 *)&value_le);
94}
95
96static struct tpm_tis_phy_ops phy_ops = {
97	.read_bytes = tpm_tis_i2c_read,
98	.write_bytes = tpm_tis_i2c_write,
99	.read32 = tpm_tis_i2c_read32,
100	.write32 = tpm_tis_i2c_write32,
101};
102
103static int tpm_tis_i2c_probe(struct udevice *udev)
104{
105	struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);
106	struct tpm_chip_priv *priv = dev_get_uclass_priv(udev);
107	int rc;
108	u8 loc = 0;
109
110	tpm_tis_ops_register(udev, &phy_ops);
111
112	/*
113	 * Force locality 0. The core driver doesn't actually write the
114	 * locality register and instead just reads/writes various access
115	 * bits of the selected locality.
116	 */
117	rc = dm_i2c_write(udev, 0, &loc, 1);
118	if (rc)
119		return rc;
120
121	rc = tpm_tis_init(udev);
122	if (rc)
123		return rc;
124
125	priv->pcr_count = drv_data->pcr_count;
126	priv->pcr_select_min = drv_data->pcr_select_min;
127	priv->version = TPM_V2;
128
129	return 0;
130}
131
132static int tpm_tis_i2c_remove(struct udevice *udev)
133{
134	return tpm_tis_cleanup(udev);
135}
136
137static const struct tpm_ops tpm_tis_i2c_ops = {
138	.open = tpm_tis_open,
139	.close = tpm_tis_close,
140	.get_desc = tpm_tis_get_desc,
141	.send = tpm_tis_send,
142	.recv = tpm_tis_recv,
143	.cleanup = tpm_tis_cleanup,
144};
145
146static const struct tpm_tis_chip_data tpm_tis_std_chip_data = {
147	.pcr_count = 24,
148	.pcr_select_min = 3,
149};
150
151static const struct udevice_id tpm_tis_i2c_ids[] = {
152	{
153		.compatible = "nuvoton,npct75x",
154		.data = (ulong)&tpm_tis_std_chip_data,
155	},
156	{
157		.compatible = "tcg,tpm-tis-i2c",
158		.data = (ulong)&tpm_tis_std_chip_data,
159	},
160	{ }
161};
162
163U_BOOT_DRIVER(tpm_tis_i2c) = {
164	.name = "tpm_tis_i2c",
165	.id = UCLASS_TPM,
166	.of_match = tpm_tis_i2c_ids,
167	.ops = &tpm_tis_i2c_ops,
168	.probe = tpm_tis_i2c_probe,
169	.remove = tpm_tis_i2c_remove,
170	.priv_auto = sizeof(struct tpm_chip),
171};
172