351 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2019, Jeffrey Hugo4 */5 6#include <linux/kernel.h>7#include <linux/bitops.h>8#include <linux/err.h>9#include <linux/platform_device.h>10#include <linux/module.h>11#include <linux/of.h>12#include <linux/clk-provider.h>13#include <linux/regmap.h>14#include <linux/reset-controller.h>15 16#include <dt-bindings/clock/qcom,gpucc-msm8998.h>17 18#include "common.h"19#include "clk-regmap.h"20#include "clk-regmap-divider.h"21#include "clk-alpha-pll.h"22#include "clk-rcg.h"23#include "clk-branch.h"24#include "reset.h"25#include "gdsc.h"26 27enum {28 P_XO,29 P_GPLL0,30 P_GPUPLL0_OUT_EVEN,31};32 33/* Instead of going directly to the block, XO is routed through this branch */34static struct clk_branch gpucc_cxo_clk = {35 .halt_reg = 0x1020,36 .clkr = {37 .enable_reg = 0x1020,38 .enable_mask = BIT(0),39 .hw.init = &(struct clk_init_data){40 .name = "gpucc_cxo_clk",41 .parent_data = &(const struct clk_parent_data){42 .fw_name = "xo"43 },44 .num_parents = 1,45 .ops = &clk_branch2_ops,46 .flags = CLK_IS_CRITICAL,47 },48 },49};50 51static const struct pll_vco fabia_vco[] = {52 { 249600000, 2000000000, 0 },53 { 125000000, 1000000000, 1 },54};55 56static const struct clk_div_table post_div_table_fabia_even[] = {57 { 0x0, 1 },58 { 0x1, 2 },59 { 0x3, 4 },60 { 0x7, 8 },61 { }62};63 64static struct clk_alpha_pll gpupll0 = {65 .offset = 0x0,66 .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA],67 .vco_table = fabia_vco,68 .num_vco = ARRAY_SIZE(fabia_vco),69 .clkr.hw.init = &(struct clk_init_data){70 .name = "gpupll0",71 .parent_hws = (const struct clk_hw *[]){ &gpucc_cxo_clk.clkr.hw },72 .num_parents = 1,73 .ops = &clk_alpha_pll_fabia_ops,74 },75};76 77static struct clk_alpha_pll_postdiv gpupll0_out_even = {78 .offset = 0x0,79 .post_div_shift = 8,80 .post_div_table = post_div_table_fabia_even,81 .num_post_div = ARRAY_SIZE(post_div_table_fabia_even),82 .width = 4,83 .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA],84 .clkr.hw.init = &(struct clk_init_data){85 .name = "gpupll0_out_even",86 .parent_hws = (const struct clk_hw *[]){ &gpupll0.clkr.hw },87 .num_parents = 1,88 .flags = CLK_SET_RATE_PARENT,89 .ops = &clk_alpha_pll_postdiv_fabia_ops,90 },91};92 93static const struct parent_map gpu_xo_gpll0_map[] = {94 { P_XO, 0 },95 { P_GPLL0, 5 },96};97 98static const struct clk_parent_data gpu_xo_gpll0[] = {99 { .hw = &gpucc_cxo_clk.clkr.hw },100 { .fw_name = "gpll0", .name = "gcc_gpu_gpll0_clk" },101};102 103static const struct parent_map gpu_xo_gpupll0_map[] = {104 { P_XO, 0 },105 { P_GPUPLL0_OUT_EVEN, 1 },106};107 108static const struct clk_hw *gpu_xo_gpupll0[] = {109 &gpucc_cxo_clk.clkr.hw,110 &gpupll0_out_even.clkr.hw,111};112 113static const struct freq_tbl ftbl_rbcpr_clk_src[] = {114 F(19200000, P_XO, 1, 0, 0),115 F(50000000, P_GPLL0, 12, 0, 0),116 { }117};118 119static struct clk_rcg2 rbcpr_clk_src = {120 .cmd_rcgr = 0x1030,121 .hid_width = 5,122 .parent_map = gpu_xo_gpll0_map,123 .freq_tbl = ftbl_rbcpr_clk_src,124 .clkr.hw.init = &(struct clk_init_data){125 .name = "rbcpr_clk_src",126 .parent_data = gpu_xo_gpll0,127 .num_parents = ARRAY_SIZE(gpu_xo_gpll0),128 .ops = &clk_rcg2_ops,129 },130};131 132static const struct freq_tbl ftbl_gfx3d_clk_src[] = {133 { .src = P_GPUPLL0_OUT_EVEN, .pre_div = 3 },134 { }135};136 137static struct clk_rcg2 gfx3d_clk_src = {138 .cmd_rcgr = 0x1070,139 .hid_width = 5,140 .parent_map = gpu_xo_gpupll0_map,141 .freq_tbl = ftbl_gfx3d_clk_src,142 .clkr.hw.init = &(struct clk_init_data){143 .name = "gfx3d_clk_src",144 .parent_hws = gpu_xo_gpupll0,145 .num_parents = ARRAY_SIZE(gpu_xo_gpupll0),146 .ops = &clk_rcg2_ops,147 .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,148 },149};150 151static const struct freq_tbl ftbl_rbbmtimer_clk_src[] = {152 F(19200000, P_XO, 1, 0, 0),153 { }154};155 156static struct clk_rcg2 rbbmtimer_clk_src = {157 .cmd_rcgr = 0x10b0,158 .hid_width = 5,159 .parent_map = gpu_xo_gpll0_map,160 .freq_tbl = ftbl_rbbmtimer_clk_src,161 .clkr.hw.init = &(struct clk_init_data){162 .name = "rbbmtimer_clk_src",163 .parent_data = gpu_xo_gpll0,164 .num_parents = ARRAY_SIZE(gpu_xo_gpll0),165 .ops = &clk_rcg2_ops,166 },167};168 169static const struct freq_tbl ftbl_gfx3d_isense_clk_src[] = {170 F(19200000, P_XO, 1, 0, 0),171 F(40000000, P_GPLL0, 15, 0, 0),172 F(200000000, P_GPLL0, 3, 0, 0),173 F(300000000, P_GPLL0, 2, 0, 0),174 { }175};176 177static struct clk_rcg2 gfx3d_isense_clk_src = {178 .cmd_rcgr = 0x1100,179 .hid_width = 5,180 .parent_map = gpu_xo_gpll0_map,181 .freq_tbl = ftbl_gfx3d_isense_clk_src,182 .clkr.hw.init = &(struct clk_init_data){183 .name = "gfx3d_isense_clk_src",184 .parent_data = gpu_xo_gpll0,185 .num_parents = ARRAY_SIZE(gpu_xo_gpll0),186 .ops = &clk_rcg2_ops,187 },188};189 190static struct clk_branch rbcpr_clk = {191 .halt_reg = 0x1054,192 .clkr = {193 .enable_reg = 0x1054,194 .enable_mask = BIT(0),195 .hw.init = &(struct clk_init_data){196 .name = "rbcpr_clk",197 .parent_hws = (const struct clk_hw *[]){ &rbcpr_clk_src.clkr.hw },198 .num_parents = 1,199 .ops = &clk_branch2_ops,200 .flags = CLK_SET_RATE_PARENT,201 },202 },203};204 205static struct clk_branch gfx3d_clk = {206 .halt_reg = 0x1098,207 .clkr = {208 .enable_reg = 0x1098,209 .enable_mask = BIT(0),210 .hw.init = &(struct clk_init_data){211 .name = "gfx3d_clk",212 .parent_hws = (const struct clk_hw *[]){ &gfx3d_clk_src.clkr.hw },213 .num_parents = 1,214 .ops = &clk_branch2_ops,215 .flags = CLK_SET_RATE_PARENT,216 },217 },218};219 220static struct clk_branch rbbmtimer_clk = {221 .halt_reg = 0x10d0,222 .clkr = {223 .enable_reg = 0x10d0,224 .enable_mask = BIT(0),225 .hw.init = &(struct clk_init_data){226 .name = "rbbmtimer_clk",227 .parent_hws = (const struct clk_hw *[]){ &rbbmtimer_clk_src.clkr.hw },228 .num_parents = 1,229 .ops = &clk_branch2_ops,230 .flags = CLK_SET_RATE_PARENT,231 },232 },233};234 235static struct clk_branch gfx3d_isense_clk = {236 .halt_reg = 0x1124,237 .clkr = {238 .enable_reg = 0x1124,239 .enable_mask = BIT(0),240 .hw.init = &(struct clk_init_data){241 .name = "gfx3d_isense_clk",242 .parent_hws = (const struct clk_hw *[]){ &gfx3d_isense_clk_src.clkr.hw },243 .num_parents = 1,244 .ops = &clk_branch2_ops,245 },246 },247};248 249static struct gdsc gpu_cx_gdsc = {250 .gdscr = 0x1004,251 .gds_hw_ctrl = 0x1008,252 .pd = {253 .name = "gpu_cx",254 },255 .pwrsts = PWRSTS_OFF_ON,256 .flags = VOTABLE,257};258 259static struct gdsc gpu_gx_gdsc = {260 .gdscr = 0x1094,261 .clamp_io_ctrl = 0x130,262 .resets = (unsigned int []){ GPU_GX_BCR },263 .reset_count = 1,264 .cxcs = (unsigned int []){ 0x1098 },265 .cxc_count = 1,266 .pd = {267 .name = "gpu_gx",268 },269 .parent = &gpu_cx_gdsc.pd,270 .pwrsts = PWRSTS_OFF_ON | PWRSTS_RET,271 .flags = CLAMP_IO | SW_RESET | AON_RESET | NO_RET_PERIPH,272};273 274static struct clk_regmap *gpucc_msm8998_clocks[] = {275 [GPUPLL0] = &gpupll0.clkr,276 [GPUPLL0_OUT_EVEN] = &gpupll0_out_even.clkr,277 [RBCPR_CLK_SRC] = &rbcpr_clk_src.clkr,278 [GFX3D_CLK_SRC] = &gfx3d_clk_src.clkr,279 [RBBMTIMER_CLK_SRC] = &rbbmtimer_clk_src.clkr,280 [GFX3D_ISENSE_CLK_SRC] = &gfx3d_isense_clk_src.clkr,281 [RBCPR_CLK] = &rbcpr_clk.clkr,282 [GFX3D_CLK] = &gfx3d_clk.clkr,283 [RBBMTIMER_CLK] = &rbbmtimer_clk.clkr,284 [GFX3D_ISENSE_CLK] = &gfx3d_isense_clk.clkr,285 [GPUCC_CXO_CLK] = &gpucc_cxo_clk.clkr,286};287 288static struct gdsc *gpucc_msm8998_gdscs[] = {289 [GPU_CX_GDSC] = &gpu_cx_gdsc,290 [GPU_GX_GDSC] = &gpu_gx_gdsc,291};292 293static const struct qcom_reset_map gpucc_msm8998_resets[] = {294 [GPU_CX_BCR] = { 0x1000 },295 [RBCPR_BCR] = { 0x1050 },296 [GPU_GX_BCR] = { 0x1090 },297 [GPU_ISENSE_BCR] = { 0x1120 },298};299 300static const struct regmap_config gpucc_msm8998_regmap_config = {301 .reg_bits = 32,302 .reg_stride = 4,303 .val_bits = 32,304 .max_register = 0x9000,305 .fast_io = true,306};307 308static const struct qcom_cc_desc gpucc_msm8998_desc = {309 .config = &gpucc_msm8998_regmap_config,310 .clks = gpucc_msm8998_clocks,311 .num_clks = ARRAY_SIZE(gpucc_msm8998_clocks),312 .resets = gpucc_msm8998_resets,313 .num_resets = ARRAY_SIZE(gpucc_msm8998_resets),314 .gdscs = gpucc_msm8998_gdscs,315 .num_gdscs = ARRAY_SIZE(gpucc_msm8998_gdscs),316};317 318static const struct of_device_id gpucc_msm8998_match_table[] = {319 { .compatible = "qcom,msm8998-gpucc" },320 { }321};322MODULE_DEVICE_TABLE(of, gpucc_msm8998_match_table);323 324static int gpucc_msm8998_probe(struct platform_device *pdev)325{326 struct regmap *regmap;327 328 regmap = qcom_cc_map(pdev, &gpucc_msm8998_desc);329 if (IS_ERR(regmap))330 return PTR_ERR(regmap);331 332 /* force periph logic on to avoid perf counter corruption */333 regmap_write_bits(regmap, gfx3d_clk.clkr.enable_reg, BIT(13), BIT(13));334 /* tweak droop detector (GPUCC_GPU_DD_WRAP_CTRL) to reduce leakage */335 regmap_write_bits(regmap, gfx3d_clk.clkr.enable_reg, BIT(0), BIT(0));336 337 return qcom_cc_really_probe(&pdev->dev, &gpucc_msm8998_desc, regmap);338}339 340static struct platform_driver gpucc_msm8998_driver = {341 .probe = gpucc_msm8998_probe,342 .driver = {343 .name = "gpucc-msm8998",344 .of_match_table = gpucc_msm8998_match_table,345 },346};347module_platform_driver(gpucc_msm8998_driver);348 349MODULE_DESCRIPTION("QCOM GPUCC MSM8998 Driver");350MODULE_LICENSE("GPL v2");351