amd_fam14h_idle.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. /*
  2. * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc.
  3. *
  4. * Licensed under the terms of the GNU GPL License version 2.
  5. *
  6. * PCI initialization based on example code from:
  7. * Andreas Herrmann <andreas.herrmann3@amd.com>
  8. */
  9. #if defined(__i386__) || defined(__x86_64__)
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <stdint.h>
  13. #include <time.h>
  14. #include <string.h>
  15. #include <pci/pci.h>
  16. #include "idle_monitor/cpupower-monitor.h"
  17. #include "helpers/helpers.h"
  18. #define PCI_NON_PC0_OFFSET 0xb0
  19. #define PCI_PC1_OFFSET 0xb4
  20. #define PCI_PC6_OFFSET 0xb8
  21. #define PCI_MONITOR_ENABLE_REG 0xe0
  22. #define PCI_NON_PC0_ENABLE_BIT 0
  23. #define PCI_PC1_ENABLE_BIT 1
  24. #define PCI_PC6_ENABLE_BIT 2
  25. #define PCI_NBP1_STAT_OFFSET 0x98
  26. #define PCI_NBP1_ACTIVE_BIT 2
  27. #define PCI_NBP1_ENTERED_BIT 1
  28. #define PCI_NBP1_CAP_OFFSET 0x90
  29. #define PCI_NBP1_CAPABLE_BIT 31
  30. #define OVERFLOW_MS 343597 /* 32 bit register filled at 12500 HZ
  31. (1 tick per 80ns) */
  32. enum amd_fam14h_states {NON_PC0 = 0, PC1, PC6, NBP1,
  33. AMD_FAM14H_STATE_NUM};
  34. static int fam14h_get_count_percent(unsigned int self_id, double *percent,
  35. unsigned int cpu);
  36. static int fam14h_nbp1_count(unsigned int id, unsigned long long *count,
  37. unsigned int cpu);
  38. static cstate_t amd_fam14h_cstates[AMD_FAM14H_STATE_NUM] = {
  39. {
  40. .name = "!PC0",
  41. .desc = N_("Package in sleep state (PC1 or deeper)"),
  42. .id = NON_PC0,
  43. .range = RANGE_PACKAGE,
  44. .get_count_percent = fam14h_get_count_percent,
  45. },
  46. {
  47. .name = "PC1",
  48. .desc = N_("Processor Package C1"),
  49. .id = PC1,
  50. .range = RANGE_PACKAGE,
  51. .get_count_percent = fam14h_get_count_percent,
  52. },
  53. {
  54. .name = "PC6",
  55. .desc = N_("Processor Package C6"),
  56. .id = PC6,
  57. .range = RANGE_PACKAGE,
  58. .get_count_percent = fam14h_get_count_percent,
  59. },
  60. {
  61. .name = "NBP1",
  62. .desc = N_("North Bridge P1 boolean counter (returns 0 or 1)"),
  63. .id = NBP1,
  64. .range = RANGE_PACKAGE,
  65. .get_count = fam14h_nbp1_count,
  66. },
  67. };
  68. static struct pci_access *pci_acc;
  69. static struct pci_dev *amd_fam14h_pci_dev;
  70. static int nbp1_entered;
  71. static struct timespec start_time;
  72. static unsigned long long timediff;
  73. #ifdef DEBUG
  74. struct timespec dbg_time;
  75. long dbg_timediff;
  76. #endif
  77. static unsigned long long *previous_count[AMD_FAM14H_STATE_NUM];
  78. static unsigned long long *current_count[AMD_FAM14H_STATE_NUM];
  79. static int amd_fam14h_get_pci_info(struct cstate *state,
  80. unsigned int *pci_offset,
  81. unsigned int *enable_bit,
  82. unsigned int cpu)
  83. {
  84. switch (state->id) {
  85. case NON_PC0:
  86. *enable_bit = PCI_NON_PC0_ENABLE_BIT;
  87. *pci_offset = PCI_NON_PC0_OFFSET;
  88. break;
  89. case PC1:
  90. *enable_bit = PCI_PC1_ENABLE_BIT;
  91. *pci_offset = PCI_PC1_OFFSET;
  92. break;
  93. case PC6:
  94. *enable_bit = PCI_PC6_ENABLE_BIT;
  95. *pci_offset = PCI_PC6_OFFSET;
  96. break;
  97. case NBP1:
  98. *enable_bit = PCI_NBP1_ENTERED_BIT;
  99. *pci_offset = PCI_NBP1_STAT_OFFSET;
  100. break;
  101. default:
  102. return -1;
  103. };
  104. return 0;
  105. }
  106. static int amd_fam14h_init(cstate_t *state, unsigned int cpu)
  107. {
  108. int enable_bit, pci_offset, ret;
  109. uint32_t val;
  110. ret = amd_fam14h_get_pci_info(state, &pci_offset, &enable_bit, cpu);
  111. if (ret)
  112. return ret;
  113. /* NBP1 needs extra treating -> write 1 to D18F6x98 bit 1 for init */
  114. if (state->id == NBP1) {
  115. val = pci_read_long(amd_fam14h_pci_dev, pci_offset);
  116. val |= 1 << enable_bit;
  117. val = pci_write_long(amd_fam14h_pci_dev, pci_offset, val);
  118. return ret;
  119. }
  120. /* Enable monitor */
  121. val = pci_read_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG);
  122. dprint("Init %s: read at offset: 0x%x val: %u\n", state->name,
  123. PCI_MONITOR_ENABLE_REG, (unsigned int) val);
  124. val |= 1 << enable_bit;
  125. pci_write_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG, val);
  126. dprint("Init %s: offset: 0x%x enable_bit: %d - val: %u (%u)\n",
  127. state->name, PCI_MONITOR_ENABLE_REG, enable_bit,
  128. (unsigned int) val, cpu);
  129. /* Set counter to zero */
  130. pci_write_long(amd_fam14h_pci_dev, pci_offset, 0);
  131. previous_count[state->id][cpu] = 0;
  132. return 0;
  133. }
  134. static int amd_fam14h_disable(cstate_t *state, unsigned int cpu)
  135. {
  136. int enable_bit, pci_offset, ret;
  137. uint32_t val;
  138. ret = amd_fam14h_get_pci_info(state, &pci_offset, &enable_bit, cpu);
  139. if (ret)
  140. return ret;
  141. val = pci_read_long(amd_fam14h_pci_dev, pci_offset);
  142. dprint("%s: offset: 0x%x %u\n", state->name, pci_offset, val);
  143. if (state->id == NBP1) {
  144. /* was the bit whether NBP1 got entered set? */
  145. nbp1_entered = (val & (1 << PCI_NBP1_ACTIVE_BIT)) |
  146. (val & (1 << PCI_NBP1_ENTERED_BIT));
  147. dprint("NBP1 was %sentered - 0x%x - enable_bit: "
  148. "%d - pci_offset: 0x%x\n",
  149. nbp1_entered ? "" : "not ",
  150. val, enable_bit, pci_offset);
  151. return ret;
  152. }
  153. current_count[state->id][cpu] = val;
  154. dprint("%s: Current - %llu (%u)\n", state->name,
  155. current_count[state->id][cpu], cpu);
  156. dprint("%s: Previous - %llu (%u)\n", state->name,
  157. previous_count[state->id][cpu], cpu);
  158. val = pci_read_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG);
  159. val &= ~(1 << enable_bit);
  160. pci_write_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG, val);
  161. return 0;
  162. }
  163. static int fam14h_nbp1_count(unsigned int id, unsigned long long *count,
  164. unsigned int cpu)
  165. {
  166. if (id == NBP1) {
  167. if (nbp1_entered)
  168. *count = 1;
  169. else
  170. *count = 0;
  171. return 0;
  172. }
  173. return -1;
  174. }
  175. static int fam14h_get_count_percent(unsigned int id, double *percent,
  176. unsigned int cpu)
  177. {
  178. unsigned long diff;
  179. if (id >= AMD_FAM14H_STATE_NUM)
  180. return -1;
  181. /* residency count in 80ns -> divide through 12.5 to get us residency */
  182. diff = current_count[id][cpu] - previous_count[id][cpu];
  183. if (timediff == 0)
  184. *percent = 0.0;
  185. else
  186. *percent = 100.0 * diff / timediff / 12.5;
  187. dprint("Timediff: %llu - res~: %lu us - percent: %.2f %%\n",
  188. timediff, diff * 10 / 125, *percent);
  189. return 0;
  190. }
  191. static int amd_fam14h_start(void)
  192. {
  193. int num, cpu;
  194. clock_gettime(CLOCK_REALTIME, &start_time);
  195. for (num = 0; num < AMD_FAM14H_STATE_NUM; num++) {
  196. for (cpu = 0; cpu < cpu_count; cpu++)
  197. amd_fam14h_init(&amd_fam14h_cstates[num], cpu);
  198. }
  199. #ifdef DEBUG
  200. clock_gettime(CLOCK_REALTIME, &dbg_time);
  201. dbg_timediff = timespec_diff_us(start_time, dbg_time);
  202. dprint("Enabling counters took: %lu us\n",
  203. dbg_timediff);
  204. #endif
  205. return 0;
  206. }
  207. static int amd_fam14h_stop(void)
  208. {
  209. int num, cpu;
  210. struct timespec end_time;
  211. clock_gettime(CLOCK_REALTIME, &end_time);
  212. for (num = 0; num < AMD_FAM14H_STATE_NUM; num++) {
  213. for (cpu = 0; cpu < cpu_count; cpu++)
  214. amd_fam14h_disable(&amd_fam14h_cstates[num], cpu);
  215. }
  216. #ifdef DEBUG
  217. clock_gettime(CLOCK_REALTIME, &dbg_time);
  218. dbg_timediff = timespec_diff_us(end_time, dbg_time);
  219. dprint("Disabling counters took: %lu ns\n", dbg_timediff);
  220. #endif
  221. timediff = timespec_diff_us(start_time, end_time);
  222. if (timediff / 1000 > OVERFLOW_MS)
  223. print_overflow_err((unsigned int)timediff / 1000000,
  224. OVERFLOW_MS / 1000);
  225. return 0;
  226. }
  227. static int is_nbp1_capable(void)
  228. {
  229. uint32_t val;
  230. val = pci_read_long(amd_fam14h_pci_dev, PCI_NBP1_CAP_OFFSET);
  231. return val & (1 << 31);
  232. }
  233. struct cpuidle_monitor *amd_fam14h_register(void)
  234. {
  235. int num;
  236. if (cpupower_cpu_info.vendor != X86_VENDOR_AMD)
  237. return NULL;
  238. if (cpupower_cpu_info.family == 0x14)
  239. strncpy(amd_fam14h_monitor.name, "Fam_14h",
  240. MONITOR_NAME_LEN - 1);
  241. else if (cpupower_cpu_info.family == 0x12)
  242. strncpy(amd_fam14h_monitor.name, "Fam_12h",
  243. MONITOR_NAME_LEN - 1);
  244. else
  245. return NULL;
  246. /* We do not alloc for nbp1 machine wide counter */
  247. for (num = 0; num < AMD_FAM14H_STATE_NUM - 1; num++) {
  248. previous_count[num] = calloc(cpu_count,
  249. sizeof(unsigned long long));
  250. current_count[num] = calloc(cpu_count,
  251. sizeof(unsigned long long));
  252. }
  253. /* We need PCI device: Slot 18, Func 6, compare with BKDG
  254. for fam 12h/14h */
  255. amd_fam14h_pci_dev = pci_slot_func_init(&pci_acc, 0x18, 6);
  256. if (amd_fam14h_pci_dev == NULL || pci_acc == NULL)
  257. return NULL;
  258. if (!is_nbp1_capable())
  259. amd_fam14h_monitor.hw_states_num = AMD_FAM14H_STATE_NUM - 1;
  260. amd_fam14h_monitor.name_len = strlen(amd_fam14h_monitor.name);
  261. return &amd_fam14h_monitor;
  262. }
  263. static void amd_fam14h_unregister(void)
  264. {
  265. int num;
  266. for (num = 0; num < AMD_FAM14H_STATE_NUM - 1; num++) {
  267. free(previous_count[num]);
  268. free(current_count[num]);
  269. }
  270. pci_cleanup(pci_acc);
  271. }
  272. struct cpuidle_monitor amd_fam14h_monitor = {
  273. .name = "",
  274. .hw_states = amd_fam14h_cstates,
  275. .hw_states_num = AMD_FAM14H_STATE_NUM,
  276. .start = amd_fam14h_start,
  277. .stop = amd_fam14h_stop,
  278. .do_register = amd_fam14h_register,
  279. .unregister = amd_fam14h_unregister,
  280. .needs_root = 1,
  281. .overflow_s = OVERFLOW_MS / 1000,
  282. };
  283. #endif /* #if defined(__i386__) || defined(__x86_64__) */