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