lsdc_gfxpll.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright (C) 2023 Loongson Technology Corporation Limited
  4. */
  5. #include <linux/delay.h>
  6. #include <drm/drm_file.h>
  7. #include <drm/drm_managed.h>
  8. #include <drm/drm_print.h>
  9. #include "lsdc_drv.h"
  10. /*
  11. * GFX PLL is the PLL used by DC, GMC and GPU, the structure of the GFX PLL
  12. * may suffer from change across chip variants.
  13. *
  14. *
  15. * +-------------+ sel_out_dc
  16. * +----| / div_out_0 | _____/ _____ DC
  17. * | +-------------+
  18. * refclk +---------+ +-------+ | +-------------+ sel_out_gmc
  19. * ---+---> | div_ref | ---> | loopc | --+--> | / div_out_1 | _____/ _____ GMC
  20. * | +---------+ +-------+ | +-------------+
  21. * | / * | +-------------+ sel_out_gpu
  22. * | +----| / div_out_2 | _____/ _____ GPU
  23. * | +-------------+
  24. * | ^
  25. * | |
  26. * +--------------------------- bypass ----------------------+
  27. */
  28. struct loongson_gfxpll_bitmap {
  29. /* Byte 0 ~ Byte 3 */
  30. unsigned div_out_dc : 7; /* 6 : 0 DC output clock divider */
  31. unsigned div_out_gmc : 7; /* 13 : 7 GMC output clock divider */
  32. unsigned div_out_gpu : 7; /* 20 : 14 GPU output clock divider */
  33. unsigned loopc : 9; /* 29 : 21 clock multiplier */
  34. unsigned _reserved_1_ : 2; /* 31 : 30 */
  35. /* Byte 4 ~ Byte 7 */
  36. unsigned div_ref : 7; /* 38 : 32 Input clock divider */
  37. unsigned locked : 1; /* 39 PLL locked indicator */
  38. unsigned sel_out_dc : 1; /* 40 dc output clk enable */
  39. unsigned sel_out_gmc : 1; /* 41 gmc output clk enable */
  40. unsigned sel_out_gpu : 1; /* 42 gpu output clk enable */
  41. unsigned set_param : 1; /* 43 Trigger the update */
  42. unsigned bypass : 1; /* 44 */
  43. unsigned powerdown : 1; /* 45 */
  44. unsigned _reserved_2_ : 18; /* 46 : 63 no use */
  45. };
  46. union loongson_gfxpll_reg_bitmap {
  47. struct loongson_gfxpll_bitmap bitmap;
  48. u32 w[2];
  49. u64 d;
  50. };
  51. static void __gfxpll_rreg(struct loongson_gfxpll *this,
  52. union loongson_gfxpll_reg_bitmap *reg)
  53. {
  54. #if defined(CONFIG_64BIT)
  55. reg->d = readq(this->mmio);
  56. #else
  57. reg->w[0] = readl(this->mmio);
  58. reg->w[1] = readl(this->mmio + 4);
  59. #endif
  60. }
  61. /* Update new parameters to the hardware */
  62. static int loongson_gfxpll_update(struct loongson_gfxpll * const this,
  63. struct loongson_gfxpll_parms const *pin)
  64. {
  65. /* None, TODO */
  66. return 0;
  67. }
  68. static void loongson_gfxpll_get_rates(struct loongson_gfxpll * const this,
  69. unsigned int *dc,
  70. unsigned int *gmc,
  71. unsigned int *gpu)
  72. {
  73. struct loongson_gfxpll_parms *pparms = &this->parms;
  74. union loongson_gfxpll_reg_bitmap gfxpll_reg;
  75. unsigned int pre_output;
  76. unsigned int dc_mhz;
  77. unsigned int gmc_mhz;
  78. unsigned int gpu_mhz;
  79. __gfxpll_rreg(this, &gfxpll_reg);
  80. pparms->div_ref = gfxpll_reg.bitmap.div_ref;
  81. pparms->loopc = gfxpll_reg.bitmap.loopc;
  82. pparms->div_out_dc = gfxpll_reg.bitmap.div_out_dc;
  83. pparms->div_out_gmc = gfxpll_reg.bitmap.div_out_gmc;
  84. pparms->div_out_gpu = gfxpll_reg.bitmap.div_out_gpu;
  85. pre_output = pparms->ref_clock / pparms->div_ref * pparms->loopc;
  86. dc_mhz = pre_output / pparms->div_out_dc / 1000;
  87. gmc_mhz = pre_output / pparms->div_out_gmc / 1000;
  88. gpu_mhz = pre_output / pparms->div_out_gpu / 1000;
  89. if (dc)
  90. *dc = dc_mhz;
  91. if (gmc)
  92. *gmc = gmc_mhz;
  93. if (gpu)
  94. *gpu = gpu_mhz;
  95. }
  96. static void loongson_gfxpll_print(struct loongson_gfxpll * const this,
  97. struct drm_printer *p,
  98. bool verbose)
  99. {
  100. struct loongson_gfxpll_parms *parms = &this->parms;
  101. unsigned int dc, gmc, gpu;
  102. if (verbose) {
  103. drm_printf(p, "reference clock: %u\n", parms->ref_clock);
  104. drm_printf(p, "div_ref = %u\n", parms->div_ref);
  105. drm_printf(p, "loopc = %u\n", parms->loopc);
  106. drm_printf(p, "div_out_dc = %u\n", parms->div_out_dc);
  107. drm_printf(p, "div_out_gmc = %u\n", parms->div_out_gmc);
  108. drm_printf(p, "div_out_gpu = %u\n", parms->div_out_gpu);
  109. }
  110. this->funcs->get_rates(this, &dc, &gmc, &gpu);
  111. drm_printf(p, "dc: %uMHz, gmc: %uMHz, gpu: %uMHz\n", dc, gmc, gpu);
  112. }
  113. /* GFX (DC, GPU, GMC) PLL initialization and destroy function */
  114. static void loongson_gfxpll_fini(struct drm_device *ddev, void *data)
  115. {
  116. struct loongson_gfxpll *this = (struct loongson_gfxpll *)data;
  117. iounmap(this->mmio);
  118. kfree(this);
  119. }
  120. static int loongson_gfxpll_init(struct loongson_gfxpll * const this)
  121. {
  122. struct loongson_gfxpll_parms *pparms = &this->parms;
  123. struct drm_printer printer = drm_info_printer(this->ddev->dev);
  124. pparms->ref_clock = LSDC_PLL_REF_CLK_KHZ;
  125. this->mmio = ioremap(this->reg_base, this->reg_size);
  126. if (IS_ERR_OR_NULL(this->mmio))
  127. return -ENOMEM;
  128. this->funcs->print(this, &printer, false);
  129. return 0;
  130. }
  131. static const struct loongson_gfxpll_funcs lsdc_gmc_gpu_funcs = {
  132. .init = loongson_gfxpll_init,
  133. .update = loongson_gfxpll_update,
  134. .get_rates = loongson_gfxpll_get_rates,
  135. .print = loongson_gfxpll_print,
  136. };
  137. int loongson_gfxpll_create(struct drm_device *ddev,
  138. struct loongson_gfxpll **ppout)
  139. {
  140. struct lsdc_device *ldev = to_lsdc(ddev);
  141. const struct loongson_gfx_desc *gfx = to_loongson_gfx(ldev->descp);
  142. struct loongson_gfxpll *this;
  143. int ret;
  144. this = kzalloc_obj(*this);
  145. if (IS_ERR_OR_NULL(this))
  146. return -ENOMEM;
  147. this->ddev = ddev;
  148. this->reg_size = gfx->gfxpll.reg_size;
  149. this->reg_base = gfx->conf_reg_base + gfx->gfxpll.reg_offset;
  150. this->funcs = &lsdc_gmc_gpu_funcs;
  151. ret = this->funcs->init(this);
  152. if (unlikely(ret)) {
  153. kfree(this);
  154. return ret;
  155. }
  156. *ppout = this;
  157. return drmm_add_action_or_reset(ddev, loongson_gfxpll_fini, this);
  158. }