driver-cairnsmore.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. /*
  2. * Copyright 2012-2013 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 "config.h"
  10. #include <stdbool.h>
  11. #include <stdint.h>
  12. #include "compat.h"
  13. #include "dynclock.h"
  14. #include "icarus-common.h"
  15. #include "lowlevel.h"
  16. #include "lowl-vcom.h"
  17. #include "miner.h"
  18. #define CAIRNSMORE1_IO_SPEED 115200
  19. // This is a general ballpark
  20. #define CAIRNSMORE1_HASH_TIME 0.0000000024484
  21. #define CAIRNSMORE1_MINIMUM_CLOCK 50
  22. #define CAIRNSMORE1_DEFAULT_CLOCK 200
  23. #define CAIRNSMORE1_MAXIMUM_CLOCK 210
  24. BFG_REGISTER_DRIVER(cairnsmore_drv)
  25. static
  26. bool cairnsmore_lowl_match(const struct lowlevel_device_info * const info)
  27. {
  28. return lowlevel_match_product(info, "Cairnsmore1");
  29. }
  30. static bool cairnsmore_detect_one(const char *devpath)
  31. {
  32. struct ICARUS_INFO *info = calloc(1, sizeof(struct ICARUS_INFO));
  33. if (unlikely(!info))
  34. quit(1, "Failed to malloc ICARUS_INFO");
  35. info->baud = CAIRNSMORE1_IO_SPEED;
  36. info->work_division = 2;
  37. info->fpga_count = 2;
  38. info->reopen_mode = IRM_NEVER;
  39. info->Hs = CAIRNSMORE1_HASH_TIME;
  40. info->timing_mode = MODE_LONG;
  41. info->do_icarus_timing = true;
  42. if (!icarus_detect_custom(devpath, &cairnsmore_drv, info)) {
  43. free(info);
  44. return false;
  45. }
  46. return true;
  47. }
  48. static
  49. bool cairnsmore_lowl_probe(const struct lowlevel_device_info * const info)
  50. {
  51. return vcom_lowl_probe_wrapper(info, cairnsmore_detect_one);
  52. }
  53. static bool cairnsmore_send_cmd(int fd, uint8_t cmd, uint8_t data, bool probe)
  54. {
  55. unsigned char pkt[64] =
  56. "\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"
  57. "vdi\xb7"
  58. "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
  59. "bfg0" "\xff\xff\xff\xff" "\xb5\0\0\0";
  60. if (unlikely(probe))
  61. pkt[61] = '\x01';
  62. pkt[32] = 0xda ^ cmd ^ data;
  63. pkt[33] = data;
  64. pkt[34] = cmd;
  65. return write(fd, pkt, sizeof(pkt)) == sizeof(pkt);
  66. }
  67. bool cairnsmore_supports_dynclock(int fd)
  68. {
  69. if (!cairnsmore_send_cmd(fd, 0, 1, true))
  70. return false;
  71. if (!cairnsmore_send_cmd(fd, 0, 1, true))
  72. return false;
  73. uint32_t nonce = 0;
  74. {
  75. struct timeval tv_finish;
  76. struct thr_info dummy = {
  77. .work_restart = false,
  78. .work_restart_notifier = {-1, -1},
  79. };
  80. icarus_gets((unsigned char*)&nonce, fd, &tv_finish, &dummy, 1, ICARUS_DEFAULT_READ_SIZE);
  81. }
  82. applog(LOG_DEBUG, "Cairnsmore dynclock detection... Got %08x", nonce);
  83. switch (nonce) {
  84. case 0x00949a6f: // big endian
  85. case 0x6f9a9400: // little endian
  86. // Hashed the command, so it's not supported
  87. return false;
  88. default:
  89. applog(LOG_WARNING, "Unexpected nonce from dynclock probe: %08x", (uint32_t)be32toh(nonce));
  90. return false;
  91. case 0:
  92. return true;
  93. }
  94. }
  95. #define cairnsmore_send_cmd(fd, cmd, data) cairnsmore_send_cmd(fd, cmd, data, false)
  96. static bool cairnsmore_change_clock_func(struct thr_info *thr, int bestM)
  97. {
  98. struct cgpu_info *cm1 = thr->cgpu;
  99. struct ICARUS_INFO *info = cm1->device_data;
  100. if (unlikely(!cairnsmore_send_cmd(cm1->device_fd, 0, bestM)))
  101. return false;
  102. // Adjust Hs expectations for frequency change
  103. info->Hs = info->Hs * (double)bestM / (double)info->dclk.freqM;
  104. dclk_msg_freqchange(cm1->proc_repr, 2.5 * (double)info->dclk.freqM, 2.5 * (double)bestM, NULL);
  105. info->dclk.freqM = bestM;
  106. return true;
  107. }
  108. static bool cairnsmore_init(struct thr_info *thr)
  109. {
  110. struct cgpu_info *cm1 = thr->cgpu;
  111. struct ICARUS_INFO *info = cm1->device_data;
  112. struct icarus_state *state = thr->cgpu_data;
  113. if (cairnsmore_supports_dynclock(cm1->device_fd)) {
  114. info->dclk_change_clock_func = cairnsmore_change_clock_func;
  115. dclk_prepare(&info->dclk);
  116. info->dclk.freqMinM = CAIRNSMORE1_MINIMUM_CLOCK / 2.5;
  117. info->dclk.freqMaxM = CAIRNSMORE1_MAXIMUM_CLOCK / 2.5;
  118. info->dclk.freqM =
  119. info->dclk.freqMDefault = CAIRNSMORE1_DEFAULT_CLOCK / 2.5;
  120. cairnsmore_send_cmd(cm1->device_fd, 0, info->dclk.freqM);
  121. applog(LOG_WARNING, "%"PRIpreprv": Frequency set to %u MHz (range: %u-%u)",
  122. cm1->proc_repr,
  123. CAIRNSMORE1_DEFAULT_CLOCK, CAIRNSMORE1_MINIMUM_CLOCK, CAIRNSMORE1_MAXIMUM_CLOCK
  124. );
  125. // The dynamic-clocking firmware connects each FPGA as its own device
  126. if (!(info->user_set & IUS_WORK_DIVISION)) {
  127. info->work_division = 1;
  128. if (!(info->user_set & IUS_FPGA_COUNT))
  129. info->fpga_count = 1;
  130. }
  131. } else {
  132. applog(LOG_WARNING, "%"PRIpreprv": Frequency scaling not supported",
  133. cm1->proc_repr
  134. );
  135. }
  136. // Commands corrupt the hash state, so next scanhash is a firstrun
  137. state->firstrun = true;
  138. return true;
  139. }
  140. void convert_icarus_to_cairnsmore(struct cgpu_info *cm1)
  141. {
  142. struct ICARUS_INFO *info = cm1->device_data;
  143. info->Hs = CAIRNSMORE1_HASH_TIME;
  144. info->fullnonce = info->Hs * (((double)0xffffffff) + 1);
  145. info->timing_mode = MODE_LONG;
  146. info->do_icarus_timing = true;
  147. cm1->drv = &cairnsmore_drv;
  148. renumber_cgpu(cm1);
  149. cairnsmore_init(cm1->thr[0]);
  150. }
  151. static struct api_data *cairnsmore_drv_extra_device_status(struct cgpu_info *cm1)
  152. {
  153. struct ICARUS_INFO *info = cm1->device_data;
  154. struct api_data*root = NULL;
  155. if (info->dclk.freqM) {
  156. double frequency = 2.5 * info->dclk.freqM;
  157. root = api_add_freq(root, "Frequency", &frequency, true);
  158. }
  159. return root;
  160. }
  161. static bool cairnsmore_identify(struct cgpu_info *cm1)
  162. {
  163. struct ICARUS_INFO *info = cm1->device_data;
  164. if (!info->dclk.freqM)
  165. return false;
  166. cairnsmore_send_cmd(cm1->device_fd, 1, 1);
  167. cgsleep_ms(5000);
  168. cairnsmore_send_cmd(cm1->device_fd, 1, 0);
  169. cm1->flash_led = true;
  170. return true;
  171. }
  172. static void cairnsmore_drv_init()
  173. {
  174. cairnsmore_drv = icarus_drv;
  175. cairnsmore_drv.dname = "cairnsmore";
  176. cairnsmore_drv.name = "ECM";
  177. cairnsmore_drv.lowl_match = cairnsmore_lowl_match;
  178. cairnsmore_drv.lowl_probe = cairnsmore_lowl_probe;
  179. cairnsmore_drv.thread_init = cairnsmore_init;
  180. cairnsmore_drv.identify_device = cairnsmore_identify;
  181. cairnsmore_drv.get_api_extra_device_status = cairnsmore_drv_extra_device_status;
  182. ++cairnsmore_drv.probe_priority;
  183. }
  184. struct device_drv cairnsmore_drv = {
  185. .drv_init = cairnsmore_drv_init,
  186. };