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, ®ion_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