162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2023 Loongson Technology Corporation Limited 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/delay.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <drm/drm_file.h> 962306a36Sopenharmony_ci#include <drm/drm_managed.h> 1062306a36Sopenharmony_ci#include <drm/drm_print.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "lsdc_drv.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* 1562306a36Sopenharmony_ci * GFX PLL is the PLL used by DC, GMC and GPU, the structure of the GFX PLL 1662306a36Sopenharmony_ci * may suffer from change across chip variants. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * +-------------+ sel_out_dc 2062306a36Sopenharmony_ci * +----| / div_out_0 | _____/ _____ DC 2162306a36Sopenharmony_ci * | +-------------+ 2262306a36Sopenharmony_ci * refclk +---------+ +-------+ | +-------------+ sel_out_gmc 2362306a36Sopenharmony_ci * ---+---> | div_ref | ---> | loopc | --+--> | / div_out_1 | _____/ _____ GMC 2462306a36Sopenharmony_ci * | +---------+ +-------+ | +-------------+ 2562306a36Sopenharmony_ci * | / * | +-------------+ sel_out_gpu 2662306a36Sopenharmony_ci * | +----| / div_out_2 | _____/ _____ GPU 2762306a36Sopenharmony_ci * | +-------------+ 2862306a36Sopenharmony_ci * | ^ 2962306a36Sopenharmony_ci * | | 3062306a36Sopenharmony_ci * +--------------------------- bypass ----------------------+ 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct loongson_gfxpll_bitmap { 3462306a36Sopenharmony_ci /* Byte 0 ~ Byte 3 */ 3562306a36Sopenharmony_ci unsigned div_out_dc : 7; /* 6 : 0 DC output clock divider */ 3662306a36Sopenharmony_ci unsigned div_out_gmc : 7; /* 13 : 7 GMC output clock divider */ 3762306a36Sopenharmony_ci unsigned div_out_gpu : 7; /* 20 : 14 GPU output clock divider */ 3862306a36Sopenharmony_ci unsigned loopc : 9; /* 29 : 21 clock multiplier */ 3962306a36Sopenharmony_ci unsigned _reserved_1_ : 2; /* 31 : 30 */ 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci /* Byte 4 ~ Byte 7 */ 4262306a36Sopenharmony_ci unsigned div_ref : 7; /* 38 : 32 Input clock divider */ 4362306a36Sopenharmony_ci unsigned locked : 1; /* 39 PLL locked indicator */ 4462306a36Sopenharmony_ci unsigned sel_out_dc : 1; /* 40 dc output clk enable */ 4562306a36Sopenharmony_ci unsigned sel_out_gmc : 1; /* 41 gmc output clk enable */ 4662306a36Sopenharmony_ci unsigned sel_out_gpu : 1; /* 42 gpu output clk enable */ 4762306a36Sopenharmony_ci unsigned set_param : 1; /* 43 Trigger the update */ 4862306a36Sopenharmony_ci unsigned bypass : 1; /* 44 */ 4962306a36Sopenharmony_ci unsigned powerdown : 1; /* 45 */ 5062306a36Sopenharmony_ci unsigned _reserved_2_ : 18; /* 46 : 63 no use */ 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ciunion loongson_gfxpll_reg_bitmap { 5462306a36Sopenharmony_ci struct loongson_gfxpll_bitmap bitmap; 5562306a36Sopenharmony_ci u32 w[2]; 5662306a36Sopenharmony_ci u64 d; 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void __gfxpll_rreg(struct loongson_gfxpll *this, 6062306a36Sopenharmony_ci union loongson_gfxpll_reg_bitmap *reg) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci#if defined(CONFIG_64BIT) 6362306a36Sopenharmony_ci reg->d = readq(this->mmio); 6462306a36Sopenharmony_ci#else 6562306a36Sopenharmony_ci reg->w[0] = readl(this->mmio); 6662306a36Sopenharmony_ci reg->w[1] = readl(this->mmio + 4); 6762306a36Sopenharmony_ci#endif 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* Update new parameters to the hardware */ 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int loongson_gfxpll_update(struct loongson_gfxpll * const this, 7362306a36Sopenharmony_ci struct loongson_gfxpll_parms const *pin) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci /* None, TODO */ 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci return 0; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic void loongson_gfxpll_get_rates(struct loongson_gfxpll * const this, 8162306a36Sopenharmony_ci unsigned int *dc, 8262306a36Sopenharmony_ci unsigned int *gmc, 8362306a36Sopenharmony_ci unsigned int *gpu) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct loongson_gfxpll_parms *pparms = &this->parms; 8662306a36Sopenharmony_ci union loongson_gfxpll_reg_bitmap gfxpll_reg; 8762306a36Sopenharmony_ci unsigned int pre_output; 8862306a36Sopenharmony_ci unsigned int dc_mhz; 8962306a36Sopenharmony_ci unsigned int gmc_mhz; 9062306a36Sopenharmony_ci unsigned int gpu_mhz; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci __gfxpll_rreg(this, &gfxpll_reg); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci pparms->div_ref = gfxpll_reg.bitmap.div_ref; 9562306a36Sopenharmony_ci pparms->loopc = gfxpll_reg.bitmap.loopc; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci pparms->div_out_dc = gfxpll_reg.bitmap.div_out_dc; 9862306a36Sopenharmony_ci pparms->div_out_gmc = gfxpll_reg.bitmap.div_out_gmc; 9962306a36Sopenharmony_ci pparms->div_out_gpu = gfxpll_reg.bitmap.div_out_gpu; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci pre_output = pparms->ref_clock / pparms->div_ref * pparms->loopc; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci dc_mhz = pre_output / pparms->div_out_dc / 1000; 10462306a36Sopenharmony_ci gmc_mhz = pre_output / pparms->div_out_gmc / 1000; 10562306a36Sopenharmony_ci gpu_mhz = pre_output / pparms->div_out_gpu / 1000; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (dc) 10862306a36Sopenharmony_ci *dc = dc_mhz; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (gmc) 11162306a36Sopenharmony_ci *gmc = gmc_mhz; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (gpu) 11462306a36Sopenharmony_ci *gpu = gpu_mhz; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void loongson_gfxpll_print(struct loongson_gfxpll * const this, 11862306a36Sopenharmony_ci struct drm_printer *p, 11962306a36Sopenharmony_ci bool verbose) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct loongson_gfxpll_parms *parms = &this->parms; 12262306a36Sopenharmony_ci unsigned int dc, gmc, gpu; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (verbose) { 12562306a36Sopenharmony_ci drm_printf(p, "reference clock: %u\n", parms->ref_clock); 12662306a36Sopenharmony_ci drm_printf(p, "div_ref = %u\n", parms->div_ref); 12762306a36Sopenharmony_ci drm_printf(p, "loopc = %u\n", parms->loopc); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci drm_printf(p, "div_out_dc = %u\n", parms->div_out_dc); 13062306a36Sopenharmony_ci drm_printf(p, "div_out_gmc = %u\n", parms->div_out_gmc); 13162306a36Sopenharmony_ci drm_printf(p, "div_out_gpu = %u\n", parms->div_out_gpu); 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci this->funcs->get_rates(this, &dc, &gmc, &gpu); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci drm_printf(p, "dc: %uMHz, gmc: %uMHz, gpu: %uMHz\n", dc, gmc, gpu); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/* GFX (DC, GPU, GMC) PLL initialization and destroy function */ 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic void loongson_gfxpll_fini(struct drm_device *ddev, void *data) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct loongson_gfxpll *this = (struct loongson_gfxpll *)data; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci iounmap(this->mmio); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci kfree(this); 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic int loongson_gfxpll_init(struct loongson_gfxpll * const this) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct loongson_gfxpll_parms *pparms = &this->parms; 15362306a36Sopenharmony_ci struct drm_printer printer = drm_info_printer(this->ddev->dev); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci pparms->ref_clock = LSDC_PLL_REF_CLK_KHZ; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci this->mmio = ioremap(this->reg_base, this->reg_size); 15862306a36Sopenharmony_ci if (IS_ERR_OR_NULL(this->mmio)) 15962306a36Sopenharmony_ci return -ENOMEM; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci this->funcs->print(this, &printer, false); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic const struct loongson_gfxpll_funcs lsdc_gmc_gpu_funcs = { 16762306a36Sopenharmony_ci .init = loongson_gfxpll_init, 16862306a36Sopenharmony_ci .update = loongson_gfxpll_update, 16962306a36Sopenharmony_ci .get_rates = loongson_gfxpll_get_rates, 17062306a36Sopenharmony_ci .print = loongson_gfxpll_print, 17162306a36Sopenharmony_ci}; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ciint loongson_gfxpll_create(struct drm_device *ddev, 17462306a36Sopenharmony_ci struct loongson_gfxpll **ppout) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct lsdc_device *ldev = to_lsdc(ddev); 17762306a36Sopenharmony_ci const struct loongson_gfx_desc *gfx = to_loongson_gfx(ldev->descp); 17862306a36Sopenharmony_ci struct loongson_gfxpll *this; 17962306a36Sopenharmony_ci int ret; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci this = kzalloc(sizeof(*this), GFP_KERNEL); 18262306a36Sopenharmony_ci if (IS_ERR_OR_NULL(this)) 18362306a36Sopenharmony_ci return -ENOMEM; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci this->ddev = ddev; 18662306a36Sopenharmony_ci this->reg_size = gfx->gfxpll.reg_size; 18762306a36Sopenharmony_ci this->reg_base = gfx->conf_reg_base + gfx->gfxpll.reg_offset; 18862306a36Sopenharmony_ci this->funcs = &lsdc_gmc_gpu_funcs; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci ret = this->funcs->init(this); 19162306a36Sopenharmony_ci if (unlikely(ret)) { 19262306a36Sopenharmony_ci kfree(this); 19362306a36Sopenharmony_ci return ret; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci *ppout = this; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return drmm_add_action_or_reset(ddev, loongson_gfxpll_fini, this); 19962306a36Sopenharmony_ci} 200