2 * I2C Driver for Atmel ATSHA204 over I2C
4 * Copyright (C) 2014 Josh Datko, Cryptotronix, jbd@cryptotronix.com
5 * 2016 Tomas Hlavacek, CZ.NIC, tmshlvck@gmail.com
6 * 2017 Marek BehĂșn, CZ.NIC, kabel@kernel.org
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.
17 #include <atsha204a-i2c.h>
19 #include <asm/global_data.h>
20 #include <linux/delay.h>
21 #include <linux/bitrev.h>
22 #include <u-boot/crc.h>
24 #define ATSHA204A_TWHI_US 2500
25 #define ATSHA204A_TRANSACTION_TIMEOUT 100000
26 #define ATSHA204A_TRANSACTION_RETRY 5
27 #define ATSHA204A_EXECTIME 5000
29 DECLARE_GLOBAL_DATA_PTR
;
31 static inline u16
atsha204a_crc16(const u8
*buffer
, size_t len
)
33 return bitrev16(crc16(0, buffer
, len
));
36 static int atsha204a_ping_bus(struct udevice
*dev
)
38 struct udevice
*bus
= dev_get_parent(dev
);
44 speed
= dm_i2c_get_bus_speed(bus
);
45 if (speed
!= I2C_SPEED_STANDARD_RATE
) {
48 rv
= dm_i2c_set_bus_speed(bus
, I2C_SPEED_STANDARD_RATE
);
50 debug("Couldn't change the I2C bus speed\n");
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.
61 msg
.flags
= I2C_M_STOP
;
62 msg
.len
= sizeof(val
);
65 res
= dm_i2c_xfer(dev
, &msg
, 1);
67 if (speed
!= I2C_SPEED_STANDARD_RATE
) {
70 rv
= dm_i2c_set_bus_speed(bus
, speed
);
72 debug("Couldn't restore the I2C bus speed\n");
78 static int atsha204a_send(struct udevice
*dev
, const u8
*buf
, u8 len
)
80 fdt_addr_t
*priv
= dev_get_priv(dev
);
84 msg
.flags
= I2C_M_STOP
;
88 return dm_i2c_xfer(dev
, &msg
, 1);
91 static int atsha204a_recv(struct udevice
*dev
, u8
*buf
, u8 len
)
93 fdt_addr_t
*priv
= dev_get_priv(dev
);
97 msg
.flags
= I2C_M_RD
| I2C_M_STOP
;
101 return dm_i2c_xfer(dev
, &msg
, 1);
104 static int atsha204a_recv_resp(struct udevice
*dev
,
105 struct atsha204a_resp
*resp
)
108 u16 resp_crc
, computed_crc
;
111 res
= atsha204a_recv(dev
, p
, 4);
115 if (resp
->length
> 4) {
116 if (resp
->length
> sizeof(*resp
))
119 res
= atsha204a_recv(dev
, p
+ 4, resp
->length
- 4);
124 resp_crc
= (u16
) p
[resp
->length
- 2]
125 | (((u16
) p
[resp
->length
- 1]) << 8);
126 computed_crc
= atsha204a_crc16(p
, resp
->length
- 2);
128 if (resp_crc
!= computed_crc
) {
129 debug("Invalid checksum in ATSHA204A response\n");
136 int atsha204a_wakeup(struct udevice
*dev
)
138 struct atsha204a_resp resp
;
141 debug("Waking up ATSHA204A\n");
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.
148 atsha204a_ping_bus(dev
);
150 udelay(ATSHA204A_TWHI_US
);
152 res
= atsha204a_recv_resp(dev
, &resp
);
154 debug("failed on receiving response, ending\n");
158 if (resp
.code
!= ATSHA204A_STATUS_AFTER_WAKE
) {
159 debug("failed (response code = %02x), ending\n", resp
.code
);
167 int atsha204a_idle(struct udevice
*dev
)
170 u8 req
= ATSHA204A_FUNC_IDLE
;
172 res
= atsha204a_send(dev
, &req
, 1);
174 debug("Failed putting ATSHA204A idle\n");
178 int atsha204a_sleep(struct udevice
*dev
)
181 u8 req
= ATSHA204A_FUNC_SLEEP
;
183 res
= atsha204a_send(dev
, &req
, 1);
185 debug("Failed putting ATSHA204A to sleep\n");
189 static int atsha204a_transaction(struct udevice
*dev
, struct atsha204a_req
*req
,
190 struct atsha204a_resp
*resp
)
192 int res
, timeout
= ATSHA204A_TRANSACTION_TIMEOUT
;
194 res
= atsha204a_send(dev
, (u8
*) req
, req
->length
+ 1);
196 debug("ATSHA204A transaction send failed\n");
201 udelay(ATSHA204A_EXECTIME
);
202 res
= atsha204a_recv_resp(dev
, resp
);
203 if (!res
|| res
== -EMSGSIZE
|| res
== -EBADMSG
)
206 debug("ATSHA204A transaction polling for response "
207 "(timeout = %d)\n", timeout
);
209 timeout
-= ATSHA204A_EXECTIME
;
210 } while (timeout
> 0);
213 debug("ATSHA204A transaction timed out\n");
220 static void atsha204a_req_crc32(struct atsha204a_req
*req
)
224 u16
*crc_ptr
= (u16
*) &p
[req
->length
- 1];
226 /* The buffer to crc16 starts at byte 1, not 0 */
227 computed_crc
= atsha204a_crc16(p
+ 1, req
->length
- 2);
229 *crc_ptr
= cpu_to_le16(computed_crc
);
232 int atsha204a_read(struct udevice
*dev
, enum atsha204a_zone zone
, bool read32
,
233 u16 addr
, u8
*buffer
)
235 int res
, retry
= ATSHA204A_TRANSACTION_RETRY
;
236 struct atsha204a_req req
;
237 struct atsha204a_resp resp
;
239 req
.function
= ATSHA204A_FUNC_COMMAND
;
241 req
.command
= ATSHA204A_CMD_READ
;
243 req
.param1
= (u8
) zone
;
247 req
.param2
= cpu_to_le16(addr
);
249 atsha204a_req_crc32(&req
);
252 res
= atsha204a_transaction(dev
, &req
, &resp
);
256 debug("ATSHA204A read retry (%d)\n", retry
);
258 atsha204a_wakeup(dev
);
259 } while (retry
>= 0);
262 debug("ATSHA204A read failed\n");
266 if (resp
.length
!= (read32
? 32 : 4) + 3) {
267 debug("ATSHA204A read bad response length (%d)\n",
272 memcpy(buffer
, ((u8
*) &resp
) + 1, read32
? 32 : 4);
277 int atsha204a_get_random(struct udevice
*dev
, u8
*buffer
, size_t max
)
280 struct atsha204a_req req
;
281 struct atsha204a_resp resp
;
283 req
.function
= ATSHA204A_FUNC_COMMAND
;
285 req
.command
= ATSHA204A_CMD_RANDOM
;
290 /* We do not have to compute the checksum dynamically */
294 res
= atsha204a_transaction(dev
, &req
, &resp
);
296 debug("ATSHA204A random transaction failed\n");
300 memcpy(buffer
, ((u8
*) &resp
) + 1, max
>= 32 ? 32 : max
);
304 static int atsha204a_of_to_plat(struct udevice
*dev
)
306 fdt_addr_t
*priv
= dev_get_priv(dev
);
309 addr
= dev_read_addr(dev
);
310 if (addr
== FDT_ADDR_T_NONE
) {
311 debug("Can't get ATSHA204A I2C base address\n");
319 static const struct udevice_id atsha204a_ids
[] = {
320 { .compatible
= "atmel,atsha204" },
321 { .compatible
= "atmel,atsha204a" },
325 U_BOOT_DRIVER(atsha204
) = {
328 .of_match
= atsha204a_ids
,
329 .of_to_plat
= atsha204a_of_to_plat
,
330 .priv_auto
= sizeof(fdt_addr_t
),