driver-cairnsmore.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. /*
  2. * Copyright 2012 Luke Dashjr
  3. *
  4. * This program is free software; you can redistribute it and/or modify it
  5. * under the terms of the GNU General Public License as published by the Free
  6. * Software Foundation; either version 3 of the License, or (at your option)
  7. * any later version. See COPYING for more details.
  8. */
  9. #include "compat.h"
  10. #include "dynclock.h"
  11. #include "fpgautils.h"
  12. #include "icarus-common.h"
  13. #include "miner.h"
  14. #define CAIRNSMORE1_IO_SPEED 115200
  15. // This is a general ballpark
  16. #define CAIRNSMORE1_HASH_TIME 0.0000000024484
  17. #define CAIRNSMORE1_MINIMUM_CLOCK 50
  18. #define CAIRNSMORE1_DEFAULT_CLOCK 200
  19. #define CAIRNSMORE1_MAXIMUM_CLOCK 210
  20. struct device_api cairnsmore_api;
  21. static void cairnsmore_api_init();
  22. static bool cairnsmore_detect_one(const char *devpath)
  23. {
  24. struct ICARUS_INFO *info = calloc(1, sizeof(struct ICARUS_INFO));
  25. if (unlikely(!info))
  26. quit(1, "Failed to malloc ICARUS_INFO");
  27. info->baud = CAIRNSMORE1_IO_SPEED;
  28. info->work_division = 2;
  29. info->fpga_count = 2;
  30. info->quirk_reopen = false;
  31. info->Hs = CAIRNSMORE1_HASH_TIME;
  32. info->timing_mode = MODE_LONG;
  33. info->do_icarus_timing = true;
  34. if (!icarus_detect_custom(devpath, &cairnsmore_api, info)) {
  35. free(info);
  36. return false;
  37. }
  38. return true;
  39. }
  40. static int cairnsmore_detect_auto(void)
  41. {
  42. return
  43. serial_autodetect_udev (cairnsmore_detect_one, "*Cairnsmore1*") ?:
  44. serial_autodetect_devserial(cairnsmore_detect_one, "Cairnsmore1") ?:
  45. serial_autodetect_ftdi (cairnsmore_detect_one, "Cairnsmore1", NULL) ?:
  46. 0;
  47. }
  48. static void cairnsmore_detect()
  49. {
  50. cairnsmore_api_init();
  51. // Actual serial detection is handled by Icarus driver
  52. serial_detect_auto_byname(&cairnsmore_api, cairnsmore_detect_one, cairnsmore_detect_auto);
  53. }
  54. static bool cairnsmore_send_cmd(int fd, uint8_t cmd, uint8_t data, bool probe)
  55. {
  56. unsigned char pkt[64] =
  57. "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
  58. "vdi\xb7"
  59. "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
  60. "bfg0" "\xff\xff\xff\xff" "\xb5\0\0\0";
  61. if (unlikely(probe))
  62. pkt[61] = '\x01';
  63. pkt[32] = 0xda ^ cmd ^ data;
  64. pkt[33] = data;
  65. pkt[34] = cmd;
  66. return write(fd, pkt, sizeof(pkt)) == sizeof(pkt);
  67. }
  68. bool cairnsmore_supports_dynclock(int fd)
  69. {
  70. if (!cairnsmore_send_cmd(fd, 0, 1, true))
  71. return false;
  72. struct timeval tv_start, elapsed;
  73. gettimeofday(&tv_start, NULL);
  74. if (!cairnsmore_send_cmd(fd, 0, 1, true))
  75. return false;
  76. uint32_t nonce = 0;
  77. {
  78. struct timeval tv_finish;
  79. struct thr_info dummy = {
  80. .work_restart = false,
  81. .work_restart_fd = -1,
  82. };
  83. icarus_gets((unsigned char*)&nonce, fd, &tv_finish, &dummy, 1);
  84. timersub(&tv_finish, &tv_start, &elapsed);
  85. }
  86. applog(LOG_DEBUG, "Cairnsmore dynclock detection... Got %08x in %d.%06ds", nonce, elapsed.tv_sec, elapsed.tv_usec);
  87. switch (nonce) {
  88. case 0x00949a6f: // big endian
  89. case 0x6f9a9400: // little endian
  90. // Hashed the command, so it's not supported
  91. return false;
  92. default:
  93. applog(LOG_WARNING, "Unexpected nonce from dynclock probe: %08x", be32toh(nonce));
  94. return false;
  95. case 0:
  96. return true;
  97. }
  98. }
  99. #define cairnsmore_send_cmd(fd, cmd, data) cairnsmore_send_cmd(fd, cmd, data, false)
  100. static bool cairnsmore_change_clock_func(struct thr_info *thr, int bestM)
  101. {
  102. struct cgpu_info *cm1 = thr->cgpu;
  103. struct ICARUS_INFO *info = cm1->cgpu_data;
  104. if (unlikely(!cairnsmore_send_cmd(cm1->device_fd, 0, bestM)))
  105. return false;
  106. // Adjust Hs expectations for frequency change
  107. info->Hs = info->Hs * (double)bestM / (double)info->dclk.freqM;
  108. char repr[0x10];
  109. sprintf(repr, "%s %u", cm1->api->name, cm1->device_id);
  110. dclk_msg_freqchange(repr, 2.5 * (double)info->dclk.freqM, 2.5 * (double)bestM, NULL);
  111. info->dclk.freqM = bestM;
  112. return true;
  113. }
  114. static bool cairnsmore_init(struct thr_info *thr)
  115. {
  116. struct cgpu_info *cm1 = thr->cgpu;
  117. struct ICARUS_INFO *info = cm1->cgpu_data;
  118. struct icarus_state *state = thr->cgpu_data;
  119. if (cairnsmore_supports_dynclock(cm1->device_fd)) {
  120. info->dclk_change_clock_func = cairnsmore_change_clock_func;
  121. dclk_prepare(&info->dclk);
  122. info->dclk.freqMaxM = CAIRNSMORE1_MAXIMUM_CLOCK / 2.5;
  123. info->dclk.freqM =
  124. info->dclk.freqMDefault = CAIRNSMORE1_DEFAULT_CLOCK / 2.5;
  125. cairnsmore_send_cmd(cm1->device_fd, 0, info->dclk.freqM);
  126. applog(LOG_WARNING, "%s %u: Frequency set to %u MHz (range: %u-%u)",
  127. cm1->api->name, cm1->device_id,
  128. CAIRNSMORE1_DEFAULT_CLOCK, CAIRNSMORE1_MINIMUM_CLOCK, CAIRNSMORE1_MAXIMUM_CLOCK
  129. );
  130. // The dynamic-clocking firmware connects each FPGA as its own device
  131. if (!(info->user_set & 1)) {
  132. info->work_division = 1;
  133. if (!(info->user_set & 2))
  134. info->fpga_count = 1;
  135. }
  136. } else {
  137. applog(LOG_WARNING, "%s %u: Frequency scaling not supported",
  138. cm1->api->name, cm1->device_id
  139. );
  140. }
  141. // Commands corrupt the hash state, so next scanhash is a firstrun
  142. state->firstrun = true;
  143. return true;
  144. }
  145. void convert_icarus_to_cairnsmore(struct cgpu_info *cm1)
  146. {
  147. struct ICARUS_INFO *info = cm1->cgpu_data;
  148. info->Hs = CAIRNSMORE1_HASH_TIME;
  149. info->fullnonce = info->Hs * (((double)0xffffffff) + 1);
  150. info->timing_mode = MODE_LONG;
  151. info->do_icarus_timing = true;
  152. cm1->api = &cairnsmore_api;
  153. renumber_cgpu(cm1);
  154. cairnsmore_init(cm1->thr[0]);
  155. }
  156. static struct api_data *cairnsmore_api_extra_device_status(struct cgpu_info *cm1)
  157. {
  158. struct ICARUS_INFO *info = cm1->cgpu_data;
  159. struct api_data*root = NULL;
  160. if (info->dclk.freqM) {
  161. double frequency = 2.5 * info->dclk.freqM;
  162. root = api_add_freq(root, "Frequency", &frequency, true);
  163. }
  164. return root;
  165. }
  166. static bool cairnsmore_identify(struct cgpu_info *cm1)
  167. {
  168. struct ICARUS_INFO *info = cm1->cgpu_data;
  169. if (!info->dclk.freqM)
  170. return false;
  171. cairnsmore_send_cmd(cm1->device_fd, 1, 1);
  172. sleep(5);
  173. cairnsmore_send_cmd(cm1->device_fd, 1, 0);
  174. cm1->flash_led = true;
  175. return true;
  176. }
  177. extern struct device_api icarus_api;
  178. static void cairnsmore_api_init()
  179. {
  180. cairnsmore_api = icarus_api;
  181. cairnsmore_api.dname = "cairnsmore";
  182. cairnsmore_api.name = "ECM";
  183. cairnsmore_api.api_detect = cairnsmore_detect;
  184. cairnsmore_api.thread_init = cairnsmore_init;
  185. cairnsmore_api.identify_device = cairnsmore_identify;
  186. cairnsmore_api.get_api_extra_device_status = cairnsmore_api_extra_device_status;
  187. }
  188. struct device_api cairnsmore_api = {
  189. // Needed to get to cairnsmore_api_init at all
  190. .api_detect = cairnsmore_detect,
  191. };