| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Copyright (C) 2023 Loongson Technology Corporation Limited
- */
- #include <linux/delay.h>
- #include <drm/drm_file.h>
- #include <drm/drm_managed.h>
- #include <drm/drm_print.h>
- #include "lsdc_drv.h"
- /*
- * GFX PLL is the PLL used by DC, GMC and GPU, the structure of the GFX PLL
- * may suffer from change across chip variants.
- *
- *
- * +-------------+ sel_out_dc
- * +----| / div_out_0 | _____/ _____ DC
- * | +-------------+
- * refclk +---------+ +-------+ | +-------------+ sel_out_gmc
- * ---+---> | div_ref | ---> | loopc | --+--> | / div_out_1 | _____/ _____ GMC
- * | +---------+ +-------+ | +-------------+
- * | / * | +-------------+ sel_out_gpu
- * | +----| / div_out_2 | _____/ _____ GPU
- * | +-------------+
- * | ^
- * | |
- * +--------------------------- bypass ----------------------+
- */
- struct loongson_gfxpll_bitmap {
- /* Byte 0 ~ Byte 3 */
- unsigned div_out_dc : 7; /* 6 : 0 DC output clock divider */
- unsigned div_out_gmc : 7; /* 13 : 7 GMC output clock divider */
- unsigned div_out_gpu : 7; /* 20 : 14 GPU output clock divider */
- unsigned loopc : 9; /* 29 : 21 clock multiplier */
- unsigned _reserved_1_ : 2; /* 31 : 30 */
- /* Byte 4 ~ Byte 7 */
- unsigned div_ref : 7; /* 38 : 32 Input clock divider */
- unsigned locked : 1; /* 39 PLL locked indicator */
- unsigned sel_out_dc : 1; /* 40 dc output clk enable */
- unsigned sel_out_gmc : 1; /* 41 gmc output clk enable */
- unsigned sel_out_gpu : 1; /* 42 gpu output clk enable */
- unsigned set_param : 1; /* 43 Trigger the update */
- unsigned bypass : 1; /* 44 */
- unsigned powerdown : 1; /* 45 */
- unsigned _reserved_2_ : 18; /* 46 : 63 no use */
- };
- union loongson_gfxpll_reg_bitmap {
- struct loongson_gfxpll_bitmap bitmap;
- u32 w[2];
- u64 d;
- };
- static void __gfxpll_rreg(struct loongson_gfxpll *this,
- union loongson_gfxpll_reg_bitmap *reg)
- {
- #if defined(CONFIG_64BIT)
- reg->d = readq(this->mmio);
- #else
- reg->w[0] = readl(this->mmio);
- reg->w[1] = readl(this->mmio + 4);
- #endif
- }
- /* Update new parameters to the hardware */
- static int loongson_gfxpll_update(struct loongson_gfxpll * const this,
- struct loongson_gfxpll_parms const *pin)
- {
- /* None, TODO */
- return 0;
- }
- static void loongson_gfxpll_get_rates(struct loongson_gfxpll * const this,
- unsigned int *dc,
- unsigned int *gmc,
- unsigned int *gpu)
- {
- struct loongson_gfxpll_parms *pparms = &this->parms;
- union loongson_gfxpll_reg_bitmap gfxpll_reg;
- unsigned int pre_output;
- unsigned int dc_mhz;
- unsigned int gmc_mhz;
- unsigned int gpu_mhz;
- __gfxpll_rreg(this, &gfxpll_reg);
- pparms->div_ref = gfxpll_reg.bitmap.div_ref;
- pparms->loopc = gfxpll_reg.bitmap.loopc;
- pparms->div_out_dc = gfxpll_reg.bitmap.div_out_dc;
- pparms->div_out_gmc = gfxpll_reg.bitmap.div_out_gmc;
- pparms->div_out_gpu = gfxpll_reg.bitmap.div_out_gpu;
- pre_output = pparms->ref_clock / pparms->div_ref * pparms->loopc;
- dc_mhz = pre_output / pparms->div_out_dc / 1000;
- gmc_mhz = pre_output / pparms->div_out_gmc / 1000;
- gpu_mhz = pre_output / pparms->div_out_gpu / 1000;
- if (dc)
- *dc = dc_mhz;
- if (gmc)
- *gmc = gmc_mhz;
- if (gpu)
- *gpu = gpu_mhz;
- }
- static void loongson_gfxpll_print(struct loongson_gfxpll * const this,
- struct drm_printer *p,
- bool verbose)
- {
- struct loongson_gfxpll_parms *parms = &this->parms;
- unsigned int dc, gmc, gpu;
- if (verbose) {
- drm_printf(p, "reference clock: %u\n", parms->ref_clock);
- drm_printf(p, "div_ref = %u\n", parms->div_ref);
- drm_printf(p, "loopc = %u\n", parms->loopc);
- drm_printf(p, "div_out_dc = %u\n", parms->div_out_dc);
- drm_printf(p, "div_out_gmc = %u\n", parms->div_out_gmc);
- drm_printf(p, "div_out_gpu = %u\n", parms->div_out_gpu);
- }
- this->funcs->get_rates(this, &dc, &gmc, &gpu);
- drm_printf(p, "dc: %uMHz, gmc: %uMHz, gpu: %uMHz\n", dc, gmc, gpu);
- }
- /* GFX (DC, GPU, GMC) PLL initialization and destroy function */
- static void loongson_gfxpll_fini(struct drm_device *ddev, void *data)
- {
- struct loongson_gfxpll *this = (struct loongson_gfxpll *)data;
- iounmap(this->mmio);
- kfree(this);
- }
- static int loongson_gfxpll_init(struct loongson_gfxpll * const this)
- {
- struct loongson_gfxpll_parms *pparms = &this->parms;
- struct drm_printer printer = drm_info_printer(this->ddev->dev);
- pparms->ref_clock = LSDC_PLL_REF_CLK_KHZ;
- this->mmio = ioremap(this->reg_base, this->reg_size);
- if (IS_ERR_OR_NULL(this->mmio))
- return -ENOMEM;
- this->funcs->print(this, &printer, false);
- return 0;
- }
- static const struct loongson_gfxpll_funcs lsdc_gmc_gpu_funcs = {
- .init = loongson_gfxpll_init,
- .update = loongson_gfxpll_update,
- .get_rates = loongson_gfxpll_get_rates,
- .print = loongson_gfxpll_print,
- };
- int loongson_gfxpll_create(struct drm_device *ddev,
- struct loongson_gfxpll **ppout)
- {
- struct lsdc_device *ldev = to_lsdc(ddev);
- const struct loongson_gfx_desc *gfx = to_loongson_gfx(ldev->descp);
- struct loongson_gfxpll *this;
- int ret;
- this = kzalloc_obj(*this);
- if (IS_ERR_OR_NULL(this))
- return -ENOMEM;
- this->ddev = ddev;
- this->reg_size = gfx->gfxpll.reg_size;
- this->reg_base = gfx->conf_reg_base + gfx->gfxpll.reg_offset;
- this->funcs = &lsdc_gmc_gpu_funcs;
- ret = this->funcs->init(this);
- if (unlikely(ret)) {
- kfree(this);
- return ret;
- }
- *ppout = this;
- return drmm_add_action_or_reset(ddev, loongson_gfxpll_fini, this);
- }
|