cppc.c 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Implement CPPC FFH helper routines for RISC-V.
  4. *
  5. * Copyright (C) 2024 Ventana Micro Systems Inc.
  6. */
  7. #include <acpi/cppc_acpi.h>
  8. #include <asm/csr.h>
  9. #include <asm/sbi.h>
  10. #define SBI_EXT_CPPC 0x43505043
  11. /* CPPC interfaces defined in SBI spec */
  12. #define SBI_CPPC_PROBE 0x0
  13. #define SBI_CPPC_READ 0x1
  14. #define SBI_CPPC_READ_HI 0x2
  15. #define SBI_CPPC_WRITE 0x3
  16. /* RISC-V FFH definitions from RISC-V FFH spec */
  17. #define FFH_CPPC_TYPE(r) (((r) & GENMASK_ULL(63, 60)) >> 60)
  18. #define FFH_CPPC_SBI_REG(r) ((r) & GENMASK(31, 0))
  19. #define FFH_CPPC_CSR_NUM(r) ((r) & GENMASK(11, 0))
  20. #define FFH_CPPC_SBI 0x1
  21. #define FFH_CPPC_CSR 0x2
  22. struct sbi_cppc_data {
  23. u64 val;
  24. u32 reg;
  25. struct sbiret ret;
  26. };
  27. static bool cppc_ext_present;
  28. static int __init sbi_cppc_init(void)
  29. {
  30. if (sbi_spec_version >= sbi_mk_version(2, 0) &&
  31. sbi_probe_extension(SBI_EXT_CPPC) > 0) {
  32. pr_info("SBI CPPC extension detected\n");
  33. cppc_ext_present = true;
  34. } else {
  35. pr_info("SBI CPPC extension NOT detected!!\n");
  36. cppc_ext_present = false;
  37. }
  38. return 0;
  39. }
  40. device_initcall(sbi_cppc_init);
  41. static void sbi_cppc_read(void *read_data)
  42. {
  43. struct sbi_cppc_data *data = (struct sbi_cppc_data *)read_data;
  44. data->ret = sbi_ecall(SBI_EXT_CPPC, SBI_CPPC_READ,
  45. data->reg, 0, 0, 0, 0, 0);
  46. }
  47. static void sbi_cppc_write(void *write_data)
  48. {
  49. struct sbi_cppc_data *data = (struct sbi_cppc_data *)write_data;
  50. data->ret = sbi_ecall(SBI_EXT_CPPC, SBI_CPPC_WRITE,
  51. data->reg, data->val, 0, 0, 0, 0);
  52. }
  53. static void cppc_ffh_csr_read(void *read_data)
  54. {
  55. struct sbi_cppc_data *data = (struct sbi_cppc_data *)read_data;
  56. switch (data->reg) {
  57. /* Support only TIME CSR for now */
  58. case CSR_TIME:
  59. data->ret.value = csr_read(CSR_TIME);
  60. data->ret.error = 0;
  61. break;
  62. default:
  63. data->ret.error = -EINVAL;
  64. break;
  65. }
  66. }
  67. static void cppc_ffh_csr_write(void *write_data)
  68. {
  69. struct sbi_cppc_data *data = (struct sbi_cppc_data *)write_data;
  70. data->ret.error = -EINVAL;
  71. }
  72. /*
  73. * Refer to drivers/acpi/cppc_acpi.c for the description of the functions
  74. * below.
  75. */
  76. bool cpc_ffh_supported(void)
  77. {
  78. return true;
  79. }
  80. int cpc_read_ffh(int cpu, struct cpc_reg *reg, u64 *val)
  81. {
  82. struct sbi_cppc_data data;
  83. if (WARN_ON_ONCE(irqs_disabled()))
  84. return -EPERM;
  85. if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_SBI) {
  86. if (!cppc_ext_present)
  87. return -EINVAL;
  88. data.reg = FFH_CPPC_SBI_REG(reg->address);
  89. smp_call_function_single(cpu, sbi_cppc_read, &data, 1);
  90. *val = data.ret.value;
  91. return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;
  92. } else if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_CSR) {
  93. data.reg = FFH_CPPC_CSR_NUM(reg->address);
  94. smp_call_function_single(cpu, cppc_ffh_csr_read, &data, 1);
  95. *val = data.ret.value;
  96. return data.ret.error;
  97. }
  98. return -EINVAL;
  99. }
  100. int cpc_write_ffh(int cpu, struct cpc_reg *reg, u64 val)
  101. {
  102. struct sbi_cppc_data data;
  103. if (WARN_ON_ONCE(irqs_disabled()))
  104. return -EPERM;
  105. if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_SBI) {
  106. if (!cppc_ext_present)
  107. return -EINVAL;
  108. data.reg = FFH_CPPC_SBI_REG(reg->address);
  109. data.val = val;
  110. smp_call_function_single(cpu, sbi_cppc_write, &data, 1);
  111. return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;
  112. } else if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_CSR) {
  113. data.reg = FFH_CPPC_CSR_NUM(reg->address);
  114. data.val = val;
  115. smp_call_function_single(cpu, cppc_ffh_csr_write, &data, 1);
  116. return data.ret.error;
  117. }
  118. return -EINVAL;
  119. }