atsha204a-i2c.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. /*
  2. * I2C Driver for Atmel ATSHA204 over I2C
  3. *
  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
  7. *
  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. #include <common.h>
  13. #include <dm.h>
  14. #include <i2c.h>
  15. #include <errno.h>
  16. #include <atsha204a-i2c.h>
  17. #include <log.h>
  18. #include <asm/global_data.h>
  19. #include <linux/delay.h>
  20. #include <linux/bitrev.h>
  21. #include <u-boot/crc.h>
  22. #define ATSHA204A_TWLO_US 60
  23. #define ATSHA204A_TWHI_US 2500
  24. #define ATSHA204A_TRANSACTION_TIMEOUT 100000
  25. #define ATSHA204A_TRANSACTION_RETRY 5
  26. #define ATSHA204A_EXECTIME 5000
  27. DECLARE_GLOBAL_DATA_PTR;
  28. static inline u16 atsha204a_crc16(const u8 *buffer, size_t len)
  29. {
  30. return bitrev16(crc16(0, buffer, len));
  31. }
  32. static int atsha204a_send(struct udevice *dev, const u8 *buf, u8 len)
  33. {
  34. fdt_addr_t *priv = dev_get_priv(dev);
  35. struct i2c_msg msg;
  36. msg.addr = *priv;
  37. msg.flags = I2C_M_STOP;
  38. msg.len = len;
  39. msg.buf = (u8 *) buf;
  40. return dm_i2c_xfer(dev, &msg, 1);
  41. }
  42. static int atsha204a_recv(struct udevice *dev, u8 *buf, u8 len)
  43. {
  44. fdt_addr_t *priv = dev_get_priv(dev);
  45. struct i2c_msg msg;
  46. msg.addr = *priv;
  47. msg.flags = I2C_M_RD | I2C_M_STOP;
  48. msg.len = len;
  49. msg.buf = (u8 *) buf;
  50. return dm_i2c_xfer(dev, &msg, 1);
  51. }
  52. static int atsha204a_recv_resp(struct udevice *dev,
  53. struct atsha204a_resp *resp)
  54. {
  55. int res;
  56. u16 resp_crc, computed_crc;
  57. u8 *p = (u8 *) resp;
  58. res = atsha204a_recv(dev, p, 4);
  59. if (res)
  60. return res;
  61. if (resp->length > 4) {
  62. if (resp->length > sizeof(*resp))
  63. return -EMSGSIZE;
  64. res = atsha204a_recv(dev, p + 4, resp->length - 4);
  65. if (res)
  66. return res;
  67. }
  68. resp_crc = (u16) p[resp->length - 2]
  69. | (((u16) p[resp->length - 1]) << 8);
  70. computed_crc = atsha204a_crc16(p, resp->length - 2);
  71. if (resp_crc != computed_crc) {
  72. debug("Invalid checksum in ATSHA204A response\n");
  73. return -EBADMSG;
  74. }
  75. return 0;
  76. }
  77. int atsha204a_wakeup(struct udevice *dev)
  78. {
  79. u8 req[4];
  80. struct atsha204a_resp resp;
  81. int try, res;
  82. debug("Waking up ATSHA204A\n");
  83. for (try = 1; try <= 10; ++try) {
  84. debug("Try %i... ", try);
  85. /*
  86. * The device ignores any levels or transitions on the SCL pin
  87. * when the device is idle, asleep or during waking up.
  88. * Don't check for error when waking up the device.
  89. */
  90. memset(req, 0, 4);
  91. atsha204a_send(dev, req, 4);
  92. udelay(ATSHA204A_TWLO_US + ATSHA204A_TWHI_US);
  93. res = atsha204a_recv_resp(dev, &resp);
  94. if (res) {
  95. debug("failed on receiving response, ending\n");
  96. return res;
  97. }
  98. if (resp.code != ATSHA204A_STATUS_AFTER_WAKE) {
  99. debug ("failed (responce code = %02x), ending\n",
  100. resp.code);
  101. return -EBADMSG;
  102. }
  103. debug("success\n");
  104. return 0;
  105. }
  106. return -ETIMEDOUT;
  107. }
  108. int atsha204a_idle(struct udevice *dev)
  109. {
  110. int res;
  111. u8 req = ATSHA204A_FUNC_IDLE;
  112. res = atsha204a_send(dev, &req, 1);
  113. if (res)
  114. debug("Failed putting ATSHA204A idle\n");
  115. return res;
  116. }
  117. int atsha204a_sleep(struct udevice *dev)
  118. {
  119. int res;
  120. u8 req = ATSHA204A_FUNC_IDLE;
  121. res = atsha204a_send(dev, &req, 1);
  122. if (res)
  123. debug("Failed putting ATSHA204A to sleep\n");
  124. return res;
  125. }
  126. static int atsha204a_transaction(struct udevice *dev, struct atsha204a_req *req,
  127. struct atsha204a_resp *resp)
  128. {
  129. int res, timeout = ATSHA204A_TRANSACTION_TIMEOUT;
  130. res = atsha204a_send(dev, (u8 *) req, req->length + 1);
  131. if (res) {
  132. debug("ATSHA204A transaction send failed\n");
  133. return -EBUSY;
  134. }
  135. do {
  136. udelay(ATSHA204A_EXECTIME);
  137. res = atsha204a_recv_resp(dev, resp);
  138. if (!res || res == -EMSGSIZE || res == -EBADMSG)
  139. break;
  140. debug("ATSHA204A transaction polling for response "
  141. "(timeout = %d)\n", timeout);
  142. timeout -= ATSHA204A_EXECTIME;
  143. } while (timeout > 0);
  144. if (timeout <= 0) {
  145. debug("ATSHA204A transaction timed out\n");
  146. return -ETIMEDOUT;
  147. }
  148. return res;
  149. }
  150. static void atsha204a_req_crc32(struct atsha204a_req *req)
  151. {
  152. u8 *p = (u8 *) req;
  153. u16 computed_crc;
  154. u16 *crc_ptr = (u16 *) &p[req->length - 1];
  155. /* The buffer to crc16 starts at byte 1, not 0 */
  156. computed_crc = atsha204a_crc16(p + 1, req->length - 2);
  157. *crc_ptr = cpu_to_le16(computed_crc);
  158. }
  159. int atsha204a_read(struct udevice *dev, enum atsha204a_zone zone, bool read32,
  160. u16 addr, u8 *buffer)
  161. {
  162. int res, retry = ATSHA204A_TRANSACTION_RETRY;
  163. struct atsha204a_req req;
  164. struct atsha204a_resp resp;
  165. req.function = ATSHA204A_FUNC_COMMAND;
  166. req.length = 7;
  167. req.command = ATSHA204A_CMD_READ;
  168. req.param1 = (u8) zone;
  169. if (read32)
  170. req.param1 |= 0x80;
  171. req.param2 = cpu_to_le16(addr);
  172. atsha204a_req_crc32(&req);
  173. do {
  174. res = atsha204a_transaction(dev, &req, &resp);
  175. if (!res)
  176. break;
  177. debug("ATSHA204A read retry (%d)\n", retry);
  178. retry--;
  179. atsha204a_wakeup(dev);
  180. } while (retry >= 0);
  181. if (res) {
  182. debug("ATSHA204A read failed\n");
  183. return res;
  184. }
  185. if (resp.length != (read32 ? 32 : 4) + 3) {
  186. debug("ATSHA204A read bad response length (%d)\n",
  187. resp.length);
  188. return -EBADMSG;
  189. }
  190. memcpy(buffer, ((u8 *) &resp) + 1, read32 ? 32 : 4);
  191. return 0;
  192. }
  193. int atsha204a_get_random(struct udevice *dev, u8 *buffer, size_t max)
  194. {
  195. int res;
  196. struct atsha204a_req req;
  197. struct atsha204a_resp resp;
  198. req.function = ATSHA204A_FUNC_COMMAND;
  199. req.length = 7;
  200. req.command = ATSHA204A_CMD_RANDOM;
  201. req.param1 = 1;
  202. req.param2 = 0;
  203. /* We do not have to compute the checksum dynamically */
  204. req.data[0] = 0x27;
  205. req.data[1] = 0x47;
  206. res = atsha204a_transaction(dev, &req, &resp);
  207. if (res) {
  208. debug("ATSHA204A random transaction failed\n");
  209. return res;
  210. }
  211. memcpy(buffer, ((u8 *) &resp) + 1, max >= 32 ? 32 : max);
  212. return 0;
  213. }
  214. static int atsha204a_of_to_plat(struct udevice *dev)
  215. {
  216. fdt_addr_t *priv = dev_get_priv(dev);
  217. fdt_addr_t addr;
  218. addr = dev_read_addr(dev);
  219. if (addr == FDT_ADDR_T_NONE) {
  220. debug("Can't get ATSHA204A I2C base address\n");
  221. return -ENXIO;
  222. }
  223. *priv = addr;
  224. return 0;
  225. }
  226. static const struct udevice_id atsha204a_ids[] = {
  227. { .compatible = "atmel,atsha204" },
  228. { .compatible = "atmel,atsha204a" },
  229. { }
  230. };
  231. U_BOOT_DRIVER(atsha204) = {
  232. .name = "atsha204",
  233. .id = UCLASS_MISC,
  234. .of_match = atsha204a_ids,
  235. .of_to_plat = atsha204a_of_to_plat,
  236. .priv_auto = sizeof(fdt_addr_t),
  237. };