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