linux/drivers/clk/qcom/gpucc-sdm845.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
   4 */
   5
   6#include <linux/clk-provider.h>
   7#include <linux/module.h>
   8#include <linux/platform_device.h>
   9#include <linux/regmap.h>
  10
  11#include <dt-bindings/clock/qcom,gpucc-sdm845.h>
  12
  13#include "common.h"
  14#include "clk-alpha-pll.h"
  15#include "clk-branch.h"
  16#include "clk-pll.h"
  17#include "clk-rcg.h"
  18#include "clk-regmap.h"
  19#include "gdsc.h"
  20
  21#define CX_GMU_CBCR_SLEEP_MASK          0xf
  22#define CX_GMU_CBCR_SLEEP_SHIFT         4
  23#define CX_GMU_CBCR_WAKE_MASK           0xf
  24#define CX_GMU_CBCR_WAKE_SHIFT          8
  25#define CLK_DIS_WAIT_SHIFT              12
  26#define CLK_DIS_WAIT_MASK               (0xf << CLK_DIS_WAIT_SHIFT)
  27
  28enum {
  29        P_BI_TCXO,
  30        P_CORE_BI_PLL_TEST_SE,
  31        P_GPLL0_OUT_MAIN,
  32        P_GPLL0_OUT_MAIN_DIV,
  33        P_GPU_CC_PLL1_OUT_EVEN,
  34        P_GPU_CC_PLL1_OUT_MAIN,
  35        P_GPU_CC_PLL1_OUT_ODD,
  36};
  37
  38static const struct parent_map gpu_cc_parent_map_0[] = {
  39        { P_BI_TCXO, 0 },
  40        { P_GPU_CC_PLL1_OUT_MAIN, 3 },
  41        { P_GPLL0_OUT_MAIN, 5 },
  42        { P_GPLL0_OUT_MAIN_DIV, 6 },
  43        { P_CORE_BI_PLL_TEST_SE, 7 },
  44};
  45
  46static const char * const gpu_cc_parent_names_0[] = {
  47        "bi_tcxo",
  48        "gpu_cc_pll1",
  49        "gcc_gpu_gpll0_clk_src",
  50        "gcc_gpu_gpll0_div_clk_src",
  51        "core_bi_pll_test_se",
  52};
  53
  54static const struct alpha_pll_config gpu_cc_pll1_config = {
  55        .l = 0x1a,
  56        .alpha = 0xaab,
  57};
  58
  59static struct clk_alpha_pll gpu_cc_pll1 = {
  60        .offset = 0x100,
  61        .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA],
  62        .clkr = {
  63                .hw.init = &(struct clk_init_data){
  64                        .name = "gpu_cc_pll1",
  65                        .parent_names = (const char *[]){ "bi_tcxo" },
  66                        .num_parents = 1,
  67                        .ops = &clk_alpha_pll_fabia_ops,
  68                },
  69        },
  70};
  71
  72static const struct freq_tbl ftbl_gpu_cc_gmu_clk_src[] = {
  73        F(19200000, P_BI_TCXO, 1, 0, 0),
  74        F(200000000, P_GPLL0_OUT_MAIN_DIV, 1.5, 0, 0),
  75        F(500000000, P_GPU_CC_PLL1_OUT_MAIN, 1, 0, 0),
  76        { }
  77};
  78
  79static struct clk_rcg2 gpu_cc_gmu_clk_src = {
  80        .cmd_rcgr = 0x1120,
  81        .mnd_width = 0,
  82        .hid_width = 5,
  83        .parent_map = gpu_cc_parent_map_0,
  84        .freq_tbl = ftbl_gpu_cc_gmu_clk_src,
  85        .clkr.hw.init = &(struct clk_init_data){
  86                .name = "gpu_cc_gmu_clk_src",
  87                .parent_names = gpu_cc_parent_names_0,
  88                .num_parents = 5,
  89                .ops = &clk_rcg2_shared_ops,
  90        },
  91};
  92
  93static struct clk_branch gpu_cc_cx_gmu_clk = {
  94        .halt_reg = 0x1098,
  95        .halt_check = BRANCH_HALT,
  96        .clkr = {
  97                .enable_reg = 0x1098,
  98                .enable_mask = BIT(0),
  99                .hw.init = &(struct clk_init_data){
 100                        .name = "gpu_cc_cx_gmu_clk",
 101                        .parent_names = (const char *[]){
 102                                "gpu_cc_gmu_clk_src",
 103                        },
 104                        .num_parents = 1,
 105                        .flags = CLK_SET_RATE_PARENT,
 106                        .ops = &clk_branch2_ops,
 107                },
 108        },
 109};
 110
 111static struct clk_branch gpu_cc_cxo_clk = {
 112        .halt_reg = 0x109c,
 113        .halt_check = BRANCH_HALT,
 114        .clkr = {
 115                .enable_reg = 0x109c,
 116                .enable_mask = BIT(0),
 117                .hw.init = &(struct clk_init_data){
 118                        .name = "gpu_cc_cxo_clk",
 119                        .ops = &clk_branch2_ops,
 120                },
 121        },
 122};
 123
 124static struct gdsc gpu_cx_gdsc = {
 125        .gdscr = 0x106c,
 126        .gds_hw_ctrl = 0x1540,
 127        .pd = {
 128                .name = "gpu_cx_gdsc",
 129        },
 130        .pwrsts = PWRSTS_OFF_ON,
 131        .flags = VOTABLE,
 132};
 133
 134/*
 135 * On SDM845 the GPU GX domain is *almost* entirely controlled by the GMU
 136 * running in the CX domain so the CPU doesn't need to know anything about the
 137 * GX domain EXCEPT....
 138 *
 139 * Hardware constraints dictate that the GX be powered down before the CX. If
 140 * the GMU crashes it could leave the GX on. In order to successfully bring back
 141 * the device the CPU needs to disable the GX headswitch. There being no sane
 142 * way to reach in and touch that register from deep inside the GPU driver we
 143 * need to set up the infrastructure to be able to ensure that the GPU can
 144 * ensure that the GX is off during this super special case. We do this by
 145 * defining a GX gdsc with a dummy enable function and a "default" disable
 146 * function.
 147 *
 148 * This allows us to attach with genpd_dev_pm_attach_by_name() in the GPU
 149 * driver. During power up, nothing will happen from the CPU (and the GMU will
 150 * power up normally but during power down this will ensure that the GX domain
 151 * is *really* off - this gives us a semi standard way of doing what we need.
 152 */
 153static int gx_gdsc_enable(struct generic_pm_domain *domain)
 154{
 155        /* Do nothing but give genpd the impression that we were successful */
 156        return 0;
 157}
 158
 159static struct gdsc gpu_gx_gdsc = {
 160        .gdscr = 0x100c,
 161        .clamp_io_ctrl = 0x1508,
 162        .pd = {
 163                .name = "gpu_gx_gdsc",
 164                .power_on = gx_gdsc_enable,
 165        },
 166        .pwrsts = PWRSTS_OFF_ON,
 167        .flags = CLAMP_IO | AON_RESET | POLL_CFG_GDSCR,
 168};
 169
 170static struct clk_regmap *gpu_cc_sdm845_clocks[] = {
 171        [GPU_CC_CXO_CLK] = &gpu_cc_cxo_clk.clkr,
 172        [GPU_CC_CX_GMU_CLK] = &gpu_cc_cx_gmu_clk.clkr,
 173        [GPU_CC_GMU_CLK_SRC] = &gpu_cc_gmu_clk_src.clkr,
 174        [GPU_CC_PLL1] = &gpu_cc_pll1.clkr,
 175};
 176
 177static struct gdsc *gpu_cc_sdm845_gdscs[] = {
 178        [GPU_CX_GDSC] = &gpu_cx_gdsc,
 179        [GPU_GX_GDSC] = &gpu_gx_gdsc,
 180};
 181
 182static const struct regmap_config gpu_cc_sdm845_regmap_config = {
 183        .reg_bits       = 32,
 184        .reg_stride     = 4,
 185        .val_bits       = 32,
 186        .max_register   = 0x8008,
 187        .fast_io        = true,
 188};
 189
 190static const struct qcom_cc_desc gpu_cc_sdm845_desc = {
 191        .config = &gpu_cc_sdm845_regmap_config,
 192        .clks = gpu_cc_sdm845_clocks,
 193        .num_clks = ARRAY_SIZE(gpu_cc_sdm845_clocks),
 194        .gdscs = gpu_cc_sdm845_gdscs,
 195        .num_gdscs = ARRAY_SIZE(gpu_cc_sdm845_gdscs),
 196};
 197
 198static const struct of_device_id gpu_cc_sdm845_match_table[] = {
 199        { .compatible = "qcom,sdm845-gpucc" },
 200        { }
 201};
 202MODULE_DEVICE_TABLE(of, gpu_cc_sdm845_match_table);
 203
 204static int gpu_cc_sdm845_probe(struct platform_device *pdev)
 205{
 206        struct regmap *regmap;
 207        unsigned int value, mask;
 208
 209        regmap = qcom_cc_map(pdev, &gpu_cc_sdm845_desc);
 210        if (IS_ERR(regmap))
 211                return PTR_ERR(regmap);
 212
 213        clk_fabia_pll_configure(&gpu_cc_pll1, regmap, &gpu_cc_pll1_config);
 214
 215        /*
 216         * Configure gpu_cc_cx_gmu_clk with recommended
 217         * wakeup/sleep settings
 218         */
 219        mask = CX_GMU_CBCR_WAKE_MASK << CX_GMU_CBCR_WAKE_SHIFT;
 220        mask |= CX_GMU_CBCR_SLEEP_MASK << CX_GMU_CBCR_SLEEP_SHIFT;
 221        value = 0xf << CX_GMU_CBCR_WAKE_SHIFT | 0xf << CX_GMU_CBCR_SLEEP_SHIFT;
 222        regmap_update_bits(regmap, 0x1098, mask, value);
 223
 224        /* Configure clk_dis_wait for gpu_cx_gdsc */
 225        regmap_update_bits(regmap, 0x106c, CLK_DIS_WAIT_MASK,
 226                                                8 << CLK_DIS_WAIT_SHIFT);
 227
 228        return qcom_cc_really_probe(pdev, &gpu_cc_sdm845_desc, regmap);
 229}
 230
 231static struct platform_driver gpu_cc_sdm845_driver = {
 232        .probe = gpu_cc_sdm845_probe,
 233        .driver = {
 234                .name = "sdm845-gpucc",
 235                .of_match_table = gpu_cc_sdm845_match_table,
 236        },
 237};
 238
 239static int __init gpu_cc_sdm845_init(void)
 240{
 241        return platform_driver_register(&gpu_cc_sdm845_driver);
 242}
 243subsys_initcall(gpu_cc_sdm845_init);
 244
 245static void __exit gpu_cc_sdm845_exit(void)
 246{
 247        platform_driver_unregister(&gpu_cc_sdm845_driver);
 248}
 249module_exit(gpu_cc_sdm845_exit);
 250
 251MODULE_DESCRIPTION("QTI GPUCC SDM845 Driver");
 252MODULE_LICENSE("GPL v2");
 253