blob: 707daa90bdba76551bc28848ecfbda881b0c901c [file] [log] [blame]
Marek Behúnaa5eb9a2017-06-09 19:28:44 +02001/*
2 * I2C Driver for Atmel ATSHA204 over I2C
3 *
4 * Copyright (C) 2014 Josh Datko, Cryptotronix, jbd@cryptotronix.com
Wolfgang Denk0cf207e2021-09-27 17:42:39 +02005 * 2016 Tomas Hlavacek, CZ.NIC, tmshlvck@gmail.com
Marek Behún61143f72022-06-01 17:17:06 +02006 * 2017 Marek Behún, CZ.NIC, kabel@kernel.org
Marek Behúnaa5eb9a2017-06-09 19:28:44 +02007 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <common.h>
14#include <dm.h>
15#include <i2c.h>
16#include <errno.h>
17#include <atsha204a-i2c.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -060018#include <log.h>
Simon Glass401d1c42020-10-30 21:38:53 -060019#include <asm/global_data.h>
Simon Glassc05ed002020-05-10 11:40:11 -060020#include <linux/delay.h>
Pali Rohár467f0c42022-04-12 11:20:44 +020021#include <linux/bitrev.h>
Simon Glass3db71102019-11-14 12:57:16 -070022#include <u-boot/crc.h>
Marek Behúnaa5eb9a2017-06-09 19:28:44 +020023
Paweł Anikiel73d88cf2022-06-17 12:47:21 +020024#define ATSHA204A_TWHI_US 2500
Marek Behúnaa5eb9a2017-06-09 19:28:44 +020025#define ATSHA204A_TRANSACTION_TIMEOUT 100000
26#define ATSHA204A_TRANSACTION_RETRY 5
27#define ATSHA204A_EXECTIME 5000
28
29DECLARE_GLOBAL_DATA_PTR;
30
Pali Rohár467f0c42022-04-12 11:20:44 +020031static inline u16 atsha204a_crc16(const u8 *buffer, size_t len)
Marek Behúnaa5eb9a2017-06-09 19:28:44 +020032{
Pali Rohár467f0c42022-04-12 11:20:44 +020033 return bitrev16(crc16(0, buffer, len));
Marek Behúnaa5eb9a2017-06-09 19:28:44 +020034}
35
Michał Barnaś2a31d712024-02-19 16:32:04 +000036static int atsha204a_ping_bus(struct udevice *dev)
37{
38 struct udevice *bus = dev_get_parent(dev);
39 struct i2c_msg msg;
40 int speed;
41 int res;
42 u8 val = 0;
43
44 speed = dm_i2c_get_bus_speed(bus);
45 if (speed != I2C_SPEED_STANDARD_RATE) {
46 int rv;
47
48 rv = dm_i2c_set_bus_speed(bus, I2C_SPEED_STANDARD_RATE);
49 if (rv)
50 debug("Couldn't change the I2C bus speed\n");
51 }
52
53 /*
54 * The I2C drivers don't support sending messages when NAK is received.
55 * This chip requires wake up low signal on SDA for >= 60us.
56 * To achieve this, we slow the bus to 100kHz and send an empty
57 * message to address 0. This will hold the SDA line low for the
58 * required time to wake up the chip.
59 */
60 msg.addr = 0;
61 msg.flags = I2C_M_STOP;
62 msg.len = sizeof(val);
63 msg.buf = &val;
64
65 res = dm_i2c_xfer(dev, &msg, 1);
66
67 if (speed != I2C_SPEED_STANDARD_RATE) {
68 int rv;
69
70 rv = dm_i2c_set_bus_speed(bus, speed);
71 if (rv)
72 debug("Couldn't restore the I2C bus speed\n");
73 }
74
75 return res;
76}
77
Marek Behúnaa5eb9a2017-06-09 19:28:44 +020078static int atsha204a_send(struct udevice *dev, const u8 *buf, u8 len)
79{
80 fdt_addr_t *priv = dev_get_priv(dev);
81 struct i2c_msg msg;
82
83 msg.addr = *priv;
84 msg.flags = I2C_M_STOP;
85 msg.len = len;
86 msg.buf = (u8 *) buf;
87
88 return dm_i2c_xfer(dev, &msg, 1);
89}
90
91static int atsha204a_recv(struct udevice *dev, u8 *buf, u8 len)
92{
93 fdt_addr_t *priv = dev_get_priv(dev);
94 struct i2c_msg msg;
95
96 msg.addr = *priv;
97 msg.flags = I2C_M_RD | I2C_M_STOP;
98 msg.len = len;
99 msg.buf = (u8 *) buf;
100
101 return dm_i2c_xfer(dev, &msg, 1);
102}
103
104static int atsha204a_recv_resp(struct udevice *dev,
105 struct atsha204a_resp *resp)
106{
107 int res;
108 u16 resp_crc, computed_crc;
109 u8 *p = (u8 *) resp;
110
111 res = atsha204a_recv(dev, p, 4);
112 if (res)
113 return res;
114
115 if (resp->length > 4) {
116 if (resp->length > sizeof(*resp))
117 return -EMSGSIZE;
118
119 res = atsha204a_recv(dev, p + 4, resp->length - 4);
120 if (res)
121 return res;
122 }
123
124 resp_crc = (u16) p[resp->length - 2]
125 | (((u16) p[resp->length - 1]) << 8);
126 computed_crc = atsha204a_crc16(p, resp->length - 2);
127
128 if (resp_crc != computed_crc) {
129 debug("Invalid checksum in ATSHA204A response\n");
130 return -EBADMSG;
131 }
132
133 return 0;
134}
135
136int atsha204a_wakeup(struct udevice *dev)
137{
Marek Behúnaa5eb9a2017-06-09 19:28:44 +0200138 struct atsha204a_resp resp;
Michał Barnaśc4841ae2024-02-19 16:32:02 +0000139 int res;
Marek Behúnaa5eb9a2017-06-09 19:28:44 +0200140
141 debug("Waking up ATSHA204A\n");
142
Michał Barnaśc4841ae2024-02-19 16:32:02 +0000143 /*
144 * The device ignores any levels or transitions on the SCL pin
145 * when the device is idle, asleep or during waking up.
146 * Don't check for error when waking up the device.
147 */
Michał Barnaś2a31d712024-02-19 16:32:04 +0000148 atsha204a_ping_bus(dev);
Marek Behúnaa5eb9a2017-06-09 19:28:44 +0200149
Michał Barnaś2a31d712024-02-19 16:32:04 +0000150 udelay(ATSHA204A_TWHI_US);
Marek Behúnaa5eb9a2017-06-09 19:28:44 +0200151
Michał Barnaśc4841ae2024-02-19 16:32:02 +0000152 res = atsha204a_recv_resp(dev, &resp);
153 if (res) {
154 debug("failed on receiving response, ending\n");
155 return res;
Marek Behúnaa5eb9a2017-06-09 19:28:44 +0200156 }
157
Michał Barnaśc4841ae2024-02-19 16:32:02 +0000158 if (resp.code != ATSHA204A_STATUS_AFTER_WAKE) {
159 debug("failed (response code = %02x), ending\n", resp.code);
160 return -EBADMSG;
161 }
162
163 debug("success\n");
164 return 0;
Marek Behúnaa5eb9a2017-06-09 19:28:44 +0200165}
166
167int atsha204a_idle(struct udevice *dev)
168{
169 int res;
170 u8 req = ATSHA204A_FUNC_IDLE;
171
172 res = atsha204a_send(dev, &req, 1);
173 if (res)
174 debug("Failed putting ATSHA204A idle\n");
175 return res;
176}
177
178int atsha204a_sleep(struct udevice *dev)
179{
180 int res;
Michał Barnaś6e0d4a72024-02-19 16:32:03 +0000181 u8 req = ATSHA204A_FUNC_SLEEP;
Marek Behúnaa5eb9a2017-06-09 19:28:44 +0200182
183 res = atsha204a_send(dev, &req, 1);
184 if (res)
185 debug("Failed putting ATSHA204A to sleep\n");
186 return res;
187}
188
189static int atsha204a_transaction(struct udevice *dev, struct atsha204a_req *req,
190 struct atsha204a_resp *resp)
191{
192 int res, timeout = ATSHA204A_TRANSACTION_TIMEOUT;
193
194 res = atsha204a_send(dev, (u8 *) req, req->length + 1);
195 if (res) {
196 debug("ATSHA204A transaction send failed\n");
197 return -EBUSY;
198 }
199
200 do {
Adrian Fiergolskie4662712022-01-11 19:05:30 +0100201 udelay(ATSHA204A_EXECTIME);
Marek Behúnaa5eb9a2017-06-09 19:28:44 +0200202 res = atsha204a_recv_resp(dev, resp);
203 if (!res || res == -EMSGSIZE || res == -EBADMSG)
204 break;
205
206 debug("ATSHA204A transaction polling for response "
207 "(timeout = %d)\n", timeout);
208
Marek Behúnaa5eb9a2017-06-09 19:28:44 +0200209 timeout -= ATSHA204A_EXECTIME;
210 } while (timeout > 0);
211
212 if (timeout <= 0) {
213 debug("ATSHA204A transaction timed out\n");
214 return -ETIMEDOUT;
215 }
216
217 return res;
218}
219
220static void atsha204a_req_crc32(struct atsha204a_req *req)
221{
222 u8 *p = (u8 *) req;
223 u16 computed_crc;
224 u16 *crc_ptr = (u16 *) &p[req->length - 1];
225
226 /* The buffer to crc16 starts at byte 1, not 0 */
227 computed_crc = atsha204a_crc16(p + 1, req->length - 2);
228
229 *crc_ptr = cpu_to_le16(computed_crc);
230}
231
232int atsha204a_read(struct udevice *dev, enum atsha204a_zone zone, bool read32,
233 u16 addr, u8 *buffer)
234{
235 int res, retry = ATSHA204A_TRANSACTION_RETRY;
236 struct atsha204a_req req;
237 struct atsha204a_resp resp;
238
239 req.function = ATSHA204A_FUNC_COMMAND;
240 req.length = 7;
241 req.command = ATSHA204A_CMD_READ;
242
243 req.param1 = (u8) zone;
244 if (read32)
245 req.param1 |= 0x80;
246
247 req.param2 = cpu_to_le16(addr);
248
249 atsha204a_req_crc32(&req);
250
251 do {
252 res = atsha204a_transaction(dev, &req, &resp);
253 if (!res)
254 break;
255
256 debug("ATSHA204A read retry (%d)\n", retry);
257 retry--;
258 atsha204a_wakeup(dev);
259 } while (retry >= 0);
Wolfgang Denk0a50b3c2021-09-27 17:42:38 +0200260
Marek Behúnaa5eb9a2017-06-09 19:28:44 +0200261 if (res) {
262 debug("ATSHA204A read failed\n");
263 return res;
264 }
265
266 if (resp.length != (read32 ? 32 : 4) + 3) {
267 debug("ATSHA204A read bad response length (%d)\n",
268 resp.length);
269 return -EBADMSG;
270 }
271
272 memcpy(buffer, ((u8 *) &resp) + 1, read32 ? 32 : 4);
273
274 return 0;
275}
276
277int atsha204a_get_random(struct udevice *dev, u8 *buffer, size_t max)
278{
279 int res;
280 struct atsha204a_req req;
281 struct atsha204a_resp resp;
282
283 req.function = ATSHA204A_FUNC_COMMAND;
284 req.length = 7;
285 req.command = ATSHA204A_CMD_RANDOM;
286
287 req.param1 = 1;
288 req.param2 = 0;
289
290 /* We do not have to compute the checksum dynamically */
291 req.data[0] = 0x27;
292 req.data[1] = 0x47;
293
294 res = atsha204a_transaction(dev, &req, &resp);
295 if (res) {
296 debug("ATSHA204A random transaction failed\n");
297 return res;
298 }
299
300 memcpy(buffer, ((u8 *) &resp) + 1, max >= 32 ? 32 : max);
301 return 0;
302}
303
Simon Glassd1998a92020-12-03 16:55:21 -0700304static int atsha204a_of_to_plat(struct udevice *dev)
Marek Behúnaa5eb9a2017-06-09 19:28:44 +0200305{
306 fdt_addr_t *priv = dev_get_priv(dev);
307 fdt_addr_t addr;
308
Adrian Fiergolski532a5b22022-01-11 19:05:31 +0100309 addr = dev_read_addr(dev);
Marek Behúnaa5eb9a2017-06-09 19:28:44 +0200310 if (addr == FDT_ADDR_T_NONE) {
311 debug("Can't get ATSHA204A I2C base address\n");
312 return -ENXIO;
313 }
314
315 *priv = addr;
316 return 0;
317}
318
319static const struct udevice_id atsha204a_ids[] = {
Pali Rohár89eabd22022-04-05 14:49:08 +0200320 { .compatible = "atmel,atsha204" },
Marek Behúnaa5eb9a2017-06-09 19:28:44 +0200321 { .compatible = "atmel,atsha204a" },
322 { }
323};
324
325U_BOOT_DRIVER(atsha204) = {
326 .name = "atsha204",
327 .id = UCLASS_MISC,
328 .of_match = atsha204a_ids,
Simon Glassd1998a92020-12-03 16:55:21 -0700329 .of_to_plat = atsha204a_of_to_plat,
Simon Glass41575d82020-12-03 16:55:17 -0700330 .priv_auto = sizeof(fdt_addr_t),
Marek Behúnaa5eb9a2017-06-09 19:28:44 +0200331};