xref: /OK3568_Linux_fs/kernel/drivers/char/tpm/tpm_atmel.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (C) 2004 IBM Corporation
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Authors:
6*4882a593Smuzhiyun  * Leendert van Doorn <leendert@watson.ibm.com>
7*4882a593Smuzhiyun  * Dave Safford <safford@watson.ibm.com>
8*4882a593Smuzhiyun  * Reiner Sailer <sailer@watson.ibm.com>
9*4882a593Smuzhiyun  * Kylene Hall <kjhall@us.ibm.com>
10*4882a593Smuzhiyun  *
11*4882a593Smuzhiyun  * Maintained by: <tpmdd-devel@lists.sourceforge.net>
12*4882a593Smuzhiyun  *
13*4882a593Smuzhiyun  * Device driver for TCG/TCPA TPM (trusted platform module).
14*4882a593Smuzhiyun  * Specifications at www.trustedcomputinggroup.org
15*4882a593Smuzhiyun  */
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun #include "tpm.h"
18*4882a593Smuzhiyun #include "tpm_atmel.h"
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun /* write status bits */
21*4882a593Smuzhiyun enum tpm_atmel_write_status {
22*4882a593Smuzhiyun 	ATML_STATUS_ABORT = 0x01,
23*4882a593Smuzhiyun 	ATML_STATUS_LASTBYTE = 0x04
24*4882a593Smuzhiyun };
25*4882a593Smuzhiyun /* read status bits */
26*4882a593Smuzhiyun enum tpm_atmel_read_status {
27*4882a593Smuzhiyun 	ATML_STATUS_BUSY = 0x01,
28*4882a593Smuzhiyun 	ATML_STATUS_DATA_AVAIL = 0x02,
29*4882a593Smuzhiyun 	ATML_STATUS_REWRITE = 0x04,
30*4882a593Smuzhiyun 	ATML_STATUS_READY = 0x08
31*4882a593Smuzhiyun };
32*4882a593Smuzhiyun 
tpm_atml_recv(struct tpm_chip * chip,u8 * buf,size_t count)33*4882a593Smuzhiyun static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
34*4882a593Smuzhiyun {
35*4882a593Smuzhiyun 	struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
36*4882a593Smuzhiyun 	u8 status, *hdr = buf;
37*4882a593Smuzhiyun 	u32 size;
38*4882a593Smuzhiyun 	int i;
39*4882a593Smuzhiyun 	__be32 *native_size;
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	/* start reading header */
42*4882a593Smuzhiyun 	if (count < 6)
43*4882a593Smuzhiyun 		return -EIO;
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun 	for (i = 0; i < 6; i++) {
46*4882a593Smuzhiyun 		status = ioread8(priv->iobase + 1);
47*4882a593Smuzhiyun 		if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
48*4882a593Smuzhiyun 			dev_err(&chip->dev, "error reading header\n");
49*4882a593Smuzhiyun 			return -EIO;
50*4882a593Smuzhiyun 		}
51*4882a593Smuzhiyun 		*buf++ = ioread8(priv->iobase);
52*4882a593Smuzhiyun 	}
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun 	/* size of the data received */
55*4882a593Smuzhiyun 	native_size = (__force __be32 *) (hdr + 2);
56*4882a593Smuzhiyun 	size = be32_to_cpu(*native_size);
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun 	if (count < size) {
59*4882a593Smuzhiyun 		dev_err(&chip->dev,
60*4882a593Smuzhiyun 			"Recv size(%d) less than available space\n", size);
61*4882a593Smuzhiyun 		for (; i < size; i++) {	/* clear the waiting data anyway */
62*4882a593Smuzhiyun 			status = ioread8(priv->iobase + 1);
63*4882a593Smuzhiyun 			if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
64*4882a593Smuzhiyun 				dev_err(&chip->dev, "error reading data\n");
65*4882a593Smuzhiyun 				return -EIO;
66*4882a593Smuzhiyun 			}
67*4882a593Smuzhiyun 		}
68*4882a593Smuzhiyun 		return -EIO;
69*4882a593Smuzhiyun 	}
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 	/* read all the data available */
72*4882a593Smuzhiyun 	for (; i < size; i++) {
73*4882a593Smuzhiyun 		status = ioread8(priv->iobase + 1);
74*4882a593Smuzhiyun 		if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
75*4882a593Smuzhiyun 			dev_err(&chip->dev, "error reading data\n");
76*4882a593Smuzhiyun 			return -EIO;
77*4882a593Smuzhiyun 		}
78*4882a593Smuzhiyun 		*buf++ = ioread8(priv->iobase);
79*4882a593Smuzhiyun 	}
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 	/* make sure data available is gone */
82*4882a593Smuzhiyun 	status = ioread8(priv->iobase + 1);
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	if (status & ATML_STATUS_DATA_AVAIL) {
85*4882a593Smuzhiyun 		dev_err(&chip->dev, "data available is stuck\n");
86*4882a593Smuzhiyun 		return -EIO;
87*4882a593Smuzhiyun 	}
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	return size;
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun 
tpm_atml_send(struct tpm_chip * chip,u8 * buf,size_t count)92*4882a593Smuzhiyun static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count)
93*4882a593Smuzhiyun {
94*4882a593Smuzhiyun 	struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
95*4882a593Smuzhiyun 	int i;
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	dev_dbg(&chip->dev, "tpm_atml_send:\n");
98*4882a593Smuzhiyun 	for (i = 0; i < count; i++) {
99*4882a593Smuzhiyun 		dev_dbg(&chip->dev, "%d 0x%x(%d)\n",  i, buf[i], buf[i]);
100*4882a593Smuzhiyun 		iowrite8(buf[i], priv->iobase);
101*4882a593Smuzhiyun 	}
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun 	return 0;
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun 
tpm_atml_cancel(struct tpm_chip * chip)106*4882a593Smuzhiyun static void tpm_atml_cancel(struct tpm_chip *chip)
107*4882a593Smuzhiyun {
108*4882a593Smuzhiyun 	struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 	iowrite8(ATML_STATUS_ABORT, priv->iobase + 1);
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun 
tpm_atml_status(struct tpm_chip * chip)113*4882a593Smuzhiyun static u8 tpm_atml_status(struct tpm_chip *chip)
114*4882a593Smuzhiyun {
115*4882a593Smuzhiyun 	struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	return ioread8(priv->iobase + 1);
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun 
tpm_atml_req_canceled(struct tpm_chip * chip,u8 status)120*4882a593Smuzhiyun static bool tpm_atml_req_canceled(struct tpm_chip *chip, u8 status)
121*4882a593Smuzhiyun {
122*4882a593Smuzhiyun 	return (status == ATML_STATUS_READY);
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun static const struct tpm_class_ops tpm_atmel = {
126*4882a593Smuzhiyun 	.recv = tpm_atml_recv,
127*4882a593Smuzhiyun 	.send = tpm_atml_send,
128*4882a593Smuzhiyun 	.cancel = tpm_atml_cancel,
129*4882a593Smuzhiyun 	.status = tpm_atml_status,
130*4882a593Smuzhiyun 	.req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL,
131*4882a593Smuzhiyun 	.req_complete_val = ATML_STATUS_DATA_AVAIL,
132*4882a593Smuzhiyun 	.req_canceled = tpm_atml_req_canceled,
133*4882a593Smuzhiyun };
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun static struct platform_device *pdev;
136*4882a593Smuzhiyun 
atml_plat_remove(void)137*4882a593Smuzhiyun static void atml_plat_remove(void)
138*4882a593Smuzhiyun {
139*4882a593Smuzhiyun 	struct tpm_chip *chip = dev_get_drvdata(&pdev->dev);
140*4882a593Smuzhiyun 	struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	tpm_chip_unregister(chip);
143*4882a593Smuzhiyun 	if (priv->have_region)
144*4882a593Smuzhiyun 		atmel_release_region(priv->base, priv->region_size);
145*4882a593Smuzhiyun 	atmel_put_base_addr(priv->iobase);
146*4882a593Smuzhiyun 	platform_device_unregister(pdev);
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(tpm_atml_pm, tpm_pm_suspend, tpm_pm_resume);
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun static struct platform_driver atml_drv = {
152*4882a593Smuzhiyun 	.driver = {
153*4882a593Smuzhiyun 		.name = "tpm_atmel",
154*4882a593Smuzhiyun 		.pm		= &tpm_atml_pm,
155*4882a593Smuzhiyun 	},
156*4882a593Smuzhiyun };
157*4882a593Smuzhiyun 
init_atmel(void)158*4882a593Smuzhiyun static int __init init_atmel(void)
159*4882a593Smuzhiyun {
160*4882a593Smuzhiyun 	int rc = 0;
161*4882a593Smuzhiyun 	void __iomem *iobase = NULL;
162*4882a593Smuzhiyun 	int have_region, region_size;
163*4882a593Smuzhiyun 	unsigned long base;
164*4882a593Smuzhiyun 	struct  tpm_chip *chip;
165*4882a593Smuzhiyun 	struct tpm_atmel_priv *priv;
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	rc = platform_driver_register(&atml_drv);
168*4882a593Smuzhiyun 	if (rc)
169*4882a593Smuzhiyun 		return rc;
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	if ((iobase = atmel_get_base_addr(&base, &region_size)) == NULL) {
172*4882a593Smuzhiyun 		rc = -ENODEV;
173*4882a593Smuzhiyun 		goto err_unreg_drv;
174*4882a593Smuzhiyun 	}
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 	have_region =
177*4882a593Smuzhiyun 	    (atmel_request_region
178*4882a593Smuzhiyun 	     (base, region_size, "tpm_atmel0") == NULL) ? 0 : 1;
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	pdev = platform_device_register_simple("tpm_atmel", -1, NULL, 0);
181*4882a593Smuzhiyun 	if (IS_ERR(pdev)) {
182*4882a593Smuzhiyun 		rc = PTR_ERR(pdev);
183*4882a593Smuzhiyun 		goto err_rel_reg;
184*4882a593Smuzhiyun 	}
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
187*4882a593Smuzhiyun 	if (!priv) {
188*4882a593Smuzhiyun 		rc = -ENOMEM;
189*4882a593Smuzhiyun 		goto err_unreg_dev;
190*4882a593Smuzhiyun 	}
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 	priv->iobase = iobase;
193*4882a593Smuzhiyun 	priv->base = base;
194*4882a593Smuzhiyun 	priv->have_region = have_region;
195*4882a593Smuzhiyun 	priv->region_size = region_size;
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 	chip = tpmm_chip_alloc(&pdev->dev, &tpm_atmel);
198*4882a593Smuzhiyun 	if (IS_ERR(chip)) {
199*4882a593Smuzhiyun 		rc = PTR_ERR(chip);
200*4882a593Smuzhiyun 		goto err_unreg_dev;
201*4882a593Smuzhiyun 	}
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	dev_set_drvdata(&chip->dev, priv);
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	rc = tpm_chip_register(chip);
206*4882a593Smuzhiyun 	if (rc)
207*4882a593Smuzhiyun 		goto err_unreg_dev;
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	return 0;
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun err_unreg_dev:
212*4882a593Smuzhiyun 	platform_device_unregister(pdev);
213*4882a593Smuzhiyun err_rel_reg:
214*4882a593Smuzhiyun 	atmel_put_base_addr(iobase);
215*4882a593Smuzhiyun 	if (have_region)
216*4882a593Smuzhiyun 		atmel_release_region(base,
217*4882a593Smuzhiyun 				     region_size);
218*4882a593Smuzhiyun err_unreg_drv:
219*4882a593Smuzhiyun 	platform_driver_unregister(&atml_drv);
220*4882a593Smuzhiyun 	return rc;
221*4882a593Smuzhiyun }
222*4882a593Smuzhiyun 
cleanup_atmel(void)223*4882a593Smuzhiyun static void __exit cleanup_atmel(void)
224*4882a593Smuzhiyun {
225*4882a593Smuzhiyun 	platform_driver_unregister(&atml_drv);
226*4882a593Smuzhiyun 	atml_plat_remove();
227*4882a593Smuzhiyun }
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun module_init(init_atmel);
230*4882a593Smuzhiyun module_exit(cleanup_atmel);
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
233*4882a593Smuzhiyun MODULE_DESCRIPTION("TPM Driver");
234*4882a593Smuzhiyun MODULE_VERSION("2.0");
235*4882a593Smuzhiyun MODULE_LICENSE("GPL");
236