// SPDX-License-Identifier: MPL-2.0
#include "cpu/tsc.h"
#include "cpu/pit.h"
#include "cpu/pic.h"
#include "irq/irq.h"
#include "kernel/log.h"
sh_tsc_TSC_VALUE kernel_start_tsc=0;
sh_uint64 internal_cpu_freq=0;
sh_bool is_cpu_freq_provided=SH_FALSE;
SH_STATUS sh_tsc_init_tsc() {
  kernel_start_tsc=sh_tsc_read_tsc();
  return SH_STATUS_SUCCESS;
}
sh_tsc_TSC_VALUE sh_tsc_get_kernel_init_tsc() {
  return kernel_start_tsc;
}
sh_tsc_TSC_VALUE sh_tsc_get_kernel_current_tsc() {
  return sh_tsc_read_tsc()-kernel_start_tsc;
}
sh_uint64 sh_tsc_estimate_cpu_freq() {
  sh_pic_remap();
  sh_pic_mask(0);
  sh_asm_sti();
  sh_pit_set_frequency(1000);
  sh_uint64 total_tsc=0;
  for (sh_uint64 i=0;i<16;i++) {
    sh_irq_start_tsc();
    sh_pic_unmask(0);
    while (sh_irq_get_tsc_delta()==0) {
      __asm__ volatile("hlt");
    }
    sh_pic_mask(0);
    total_tsc+=sh_irq_get_tsc_delta();
    sh_log_fdebug(SH_LOG_SOURCE_MAIN,"CPU Frequence #%8u: %8u hz`\n",i+1,sh_irq_get_tsc_delta()*1000);
  }
  return (total_tsc*1000)/16;
}
void sh_tsc_load_cpu_freq(sh_uint64 cpu_freq) {
  internal_cpu_freq=cpu_freq;
}
sh_bool sh_tsc_has_hypervisor() {
  sh_uint32 eax,ebx,ecx,edx;
  sh_asm_cpuid(1,0,&eax,&ebx,&ecx,&edx);
  return (sh_bool)((ecx>>31)&1);
}
sh_bool sh_tsc_is_tsc_constant() {
  sh_uint32 eax,ebx,ecx,edx;
  sh_asm_cpuid(0x80000000,0,&eax,&ebx,&ecx,&edx);
  if (eax<0x80000007) return SH_FALSE;
  sh_asm_cpuid(0x80000007,0,&eax,&ebx,&ecx,&edx);
  sh_uint32 constant;
  constant=(edx>>8)&1;
  return (sh_bool)constant;
}
SH_STATUS sh_tsc_get_cpu_freq_cpuid(sh_uint64 *freq) {
  sh_uint32 eax,ebx,ecx,edx;
  sh_asm_cpuid(0x15,0,&eax,&ebx,&ecx,&edx);
  if (eax==0 || ebx==0) return SH_STATUS_UNAVAILABLE;
  if (ecx!=0) {
    *freq=((sh_uint64)ecx*ebx)/eax;
  } else {
    return SH_STATUS_UNAVAILABLE;
  }
  is_cpu_freq_provided=SH_TRUE;
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_tsc_devs_query(char *sub_path,sh_devs_RESULT *result) {
  if (sub_path==SH_NULLPTR || result==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  sh_uint64 path_len=sh_string_len(sub_path);
  if (path_len==0 || path_len>256) return SH_STATUS_INVALID_PARAMETER;
  if (sh_string_compare(sub_path,"/kernel-init-tsc",16)) {
    if (path_len==16) {
      result->type=SH_DEVS_VALUE;
      result->value=kernel_start_tsc;
      return SH_STATUS_SUCCESS;
    } else {
      return SH_STATUS_NOT_FOUND;
    }
  } else if (sh_string_compare(sub_path,"/cpu-freq",9)) {
    if (path_len==9) {
      result->type=SH_DEVS_VALUE;
      result->value=internal_cpu_freq;
      return SH_STATUS_SUCCESS;
    } else {
      return SH_STATUS_NOT_FOUND;
    }
  } else if (sh_string_compare(sub_path,"/is-cpu-freq-provided",21)) {
    if (path_len==21) {
      result->type=SH_DEVS_BOOL;
      result->value=is_cpu_freq_provided;
      return SH_STATUS_SUCCESS;
    } else {
      return SH_STATUS_NOT_FOUND;
    }
  } else {
    return SH_STATUS_NOT_FOUND;
  }
}
