summaryrefslogtreecommitdiff
path: root/drivers/tpm/tpm2_tis_i2c.c
blob: 99d1cf218dafc2fdc8e97b2841a3dbe6e7bf0510 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright 2022 IBM Corp.
 */

#include <common.h>
#include <dm.h>
#include <fdtdec.h>
#include <i2c.h>
#include <log.h>
#include <tpm-v2.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/unaligned/be_byteshift.h>
#include <asm-generic/gpio.h>

#include "tpm_tis.h"
#include "tpm_internal.h"

struct tpm_tis_chip_data {
	unsigned int pcr_count;
	unsigned int pcr_select_min;
};

static uint tpm_tis_i2c_address_to_register(u32 addr)
{
	addr &= 0xFFF;

	/*
	 * Adapt register addresses that have changed compared to older TIS
	 * version.
	 */
	switch (addr) {
	case TPM_ACCESS(0):
		return 0x04;
	case TPM_DID_VID(0):
		return 0x48;
	case TPM_RID(0):
		return 0x4C;
	default:
		return addr;
	}
}

static int tpm_tis_i2c_read(struct udevice *dev, u32 addr, u16 len, u8 *in)
{
	int rc;
	int count = 0;
	uint reg = tpm_tis_i2c_address_to_register(addr);

	do {
		rc = dm_i2c_read(dev, reg, in, len);
		udelay(SLEEP_DURATION_US);
	} while (rc && count++ < MAX_COUNT);

	return rc;
}

static int tpm_tis_i2c_write(struct udevice *dev, u32 addr, u16 len,
			     const u8 *out)
{
	int rc;
	int count = 0;
	uint reg = tpm_tis_i2c_address_to_register(addr);

	do {
		rc = dm_i2c_write(dev, reg, out, len);
		udelay(SLEEP_DURATION_US);
	} while (rc && count++ < MAX_COUNT);

	return rc;
}

static int tpm_tis_i2c_read32(struct udevice *dev, u32 addr, u32 *result)
{
	__le32 result_le;
	int rc;

	rc = tpm_tis_i2c_read(dev, addr, sizeof(u32), (u8 *)&result_le);
	if (!rc)
		*result = le32_to_cpu(result_le);

	return rc;
}

static int tpm_tis_i2c_write32(struct udevice *dev, u32 addr, u32 value)
{
	__le32 value_le = cpu_to_le32(value);

	return tpm_tis_i2c_write(dev, addr, sizeof(value), (u8 *)&value_le);
}

static struct tpm_tis_phy_ops phy_ops = {
	.read_bytes = tpm_tis_i2c_read,
	.write_bytes = tpm_tis_i2c_write,
	.read32 = tpm_tis_i2c_read32,
	.write32 = tpm_tis_i2c_write32,
};

static int tpm_tis_i2c_probe(struct udevice *udev)
{
	struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);
	struct tpm_chip_priv *priv = dev_get_uclass_priv(udev);
	int rc;
	u8 loc = 0;

	tpm_tis_ops_register(udev, &phy_ops);

	/*
	 * Force locality 0. The core driver doesn't actually write the
	 * locality register and instead just reads/writes various access
	 * bits of the selected locality.
	 */
	rc = dm_i2c_write(udev, 0, &loc, 1);
	if (rc)
		return rc;

	rc = tpm_tis_init(udev);
	if (rc)
		return rc;

	priv->pcr_count = drv_data->pcr_count;
	priv->pcr_select_min = drv_data->pcr_select_min;
	priv->version = TPM_V2;

	return 0;
}

static int tpm_tis_i2c_remove(struct udevice *udev)
{
	return tpm_tis_cleanup(udev);
}

static const struct tpm_ops tpm_tis_i2c_ops = {
	.open = tpm_tis_open,
	.close = tpm_tis_close,
	.get_desc = tpm_tis_get_desc,
	.send = tpm_tis_send,
	.recv = tpm_tis_recv,
	.cleanup = tpm_tis_cleanup,
};

static const struct tpm_tis_chip_data tpm_tis_std_chip_data = {
	.pcr_count = 24,
	.pcr_select_min = 3,
};

static const struct udevice_id tpm_tis_i2c_ids[] = {
	{
		.compatible = "nuvoton,npct75x",
		.data = (ulong)&tpm_tis_std_chip_data,
	},
	{
		.compatible = "tcg,tpm-tis-i2c",
		.data = (ulong)&tpm_tis_std_chip_data,
	},
	{ }
};

U_BOOT_DRIVER(tpm_tis_i2c) = {
	.name = "tpm_tis_i2c",
	.id = UCLASS_TPM,
	.of_match = tpm_tis_i2c_ids,
	.ops = &tpm_tis_i2c_ops,
	.probe = tpm_tis_i2c_probe,
	.remove = tpm_tis_i2c_remove,
	.priv_auto = sizeof(struct tpm_chip),
};