diff options
Diffstat (limited to 'drivers/clk')
| -rw-r--r-- | drivers/clk/msm/clock-osm.c | 39 | ||||
| -rw-r--r-- | drivers/clk/msm/mdss/mdss-dsi-pll-8996-util.c | 4 | ||||
| -rw-r--r-- | drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c | 4 | ||||
| -rw-r--r-- | drivers/clk/qcom/Kconfig | 11 | ||||
| -rw-r--r-- | drivers/clk/qcom/Makefile | 1 | ||||
| -rw-r--r-- | drivers/clk/qcom/clk-rcg2.c | 1 | ||||
| -rw-r--r-- | drivers/clk/qcom/gpucc-msmfalcon.c | 482 |
7 files changed, 527 insertions, 15 deletions
diff --git a/drivers/clk/msm/clock-osm.c b/drivers/clk/msm/clock-osm.c index 0d733f49f184..d6cdbbc78827 100644 --- a/drivers/clk/msm/clock-osm.c +++ b/drivers/clk/msm/clock-osm.c @@ -79,6 +79,7 @@ enum clk_osm_trace_packet_id { #define MEM_ACC_INSTR_COMP(n) (0x67 + ((n) * 0x40)) #define MEM_ACC_SEQ_REG_VAL_START(n) (SEQ_REG(60 + (n))) #define SEQ_REG1_MSMCOBALT_V2 0x1048 +#define VERSION_REG 0x0 #define OSM_TABLE_SIZE 40 #define MAX_CLUSTER_CNT 2 @@ -182,7 +183,9 @@ enum clk_osm_trace_packet_id { #define DROOP_UNSTALL_TIMER_CTRL_REG 0x10AC #define DROOP_WAIT_TO_RELEASE_TIMER_CTRL0_REG 0x10B0 #define DROOP_WAIT_TO_RELEASE_TIMER_CTRL1_REG 0x10B4 +#define OSM_PLL_SW_OVERRIDE_EN 0x10C0 +#define PLL_SW_OVERRIDE_DROOP_EN BIT(0) #define DCVS_DROOP_TIMER_CTRL 0x10B8 #define SEQ_MEM_ADDR 0x500 #define SEQ_CFG_BR_ADDR 0x170 @@ -377,6 +380,11 @@ static inline int clk_osm_read_reg_no_log(struct clk_osm *c, u32 offset) return readl_relaxed_no_log((char *)c->vbases[OSM_BASE] + offset); } +static inline int clk_osm_mb(struct clk_osm *c, int base) +{ + return readl_relaxed_no_log((char *)c->vbases[base] + VERSION_REG); +} + static inline int clk_osm_count_ns(struct clk_osm *c, u64 nsec) { u64 temp; @@ -478,7 +486,7 @@ static int clk_osm_set_rate(struct clk *c, unsigned long rate) } /* Make sure the write goes through before proceeding */ - mb(); + clk_osm_mb(cpuclk, OSM_BASE); return 0; } @@ -490,7 +498,7 @@ static int clk_osm_enable(struct clk *c) clk_osm_write_reg(cpuclk, 1, ENABLE_REG); /* Make sure the write goes through before proceeding */ - mb(); + clk_osm_mb(cpuclk, OSM_BASE); /* Wait for 5us for OSM hardware to enable */ udelay(5); @@ -1101,14 +1109,14 @@ static void clk_osm_setup_cluster_pll(struct clk_osm *c) PLL_MODE); /* Ensure writes complete before delaying */ - mb(); + clk_osm_mb(c, PLL_BASE); udelay(PLL_WAIT_LOCK_TIME_US); writel_relaxed(0x6, c->vbases[PLL_BASE] + PLL_MODE); /* Ensure write completes before delaying */ - mb(); + clk_osm_mb(c, PLL_BASE); usleep_range(50, 75); @@ -1153,7 +1161,7 @@ static int clk_osm_setup_hw_table(struct clk_osm *c) } /* Make sure all writes go through */ - mb(); + clk_osm_mb(c, OSM_BASE); return 0; } @@ -1272,7 +1280,7 @@ static int clk_osm_set_cc_policy(struct platform_device *pdev) } /* Wait for the writes to complete */ - mb(); + clk_osm_mb(&perfcl_clk, OSM_BASE); rc = of_property_read_bool(pdev->dev.of_node, "qcom,set-ret-inactive"); if (rc) { @@ -1297,7 +1305,7 @@ static int clk_osm_set_cc_policy(struct platform_device *pdev) clk_osm_write_reg(&perfcl_clk, val, SPM_CC_DCVS_DISABLE); /* Wait for the writes to complete */ - mb(); + clk_osm_mb(&perfcl_clk, OSM_BASE); devm_kfree(&pdev->dev, array); return 0; @@ -1392,7 +1400,7 @@ static int clk_osm_set_llm_freq_policy(struct platform_device *pdev) clk_osm_write_reg(&perfcl_clk, regval, LLM_INTF_DCVS_DISABLE); /* Wait for the write to complete */ - mb(); + clk_osm_mb(&perfcl_clk, OSM_BASE); devm_kfree(&pdev->dev, array); return 0; @@ -1467,7 +1475,7 @@ static int clk_osm_set_llm_volt_policy(struct platform_device *pdev) clk_osm_write_reg(&perfcl_clk, val, LLM_INTF_DCVS_DISABLE); /* Wait for the writes to complete */ - mb(); + clk_osm_mb(&perfcl_clk, OSM_BASE); devm_kfree(&pdev->dev, array); return 0; @@ -1668,7 +1676,7 @@ static void clk_osm_setup_osm_was(struct clk_osm *c) } /* Ensure writes complete before returning */ - mb(); + clk_osm_mb(c, OSM_BASE); } static void clk_osm_setup_fsms(struct clk_osm *c) @@ -1778,7 +1786,7 @@ static void clk_osm_setup_fsms(struct clk_osm *c) val = clk_osm_read_reg(c, DROOP_WAIT_TO_RELEASE_TIMER_CTRL0_REG); - val |= BVAL(15, 0, clk_osm_count_ns(c, 500)); + val |= BVAL(15, 0, clk_osm_count_ns(c, 15000)); clk_osm_write_reg(c, val, DROOP_WAIT_TO_RELEASE_TIMER_CTRL0_REG); } @@ -1792,7 +1800,7 @@ static void clk_osm_setup_fsms(struct clk_osm *c) if (c->wfx_fsm_en || c->ps_fsm_en || c->droop_fsm_en) { clk_osm_write_reg(c, 0x1, DROOP_PROG_SYNC_DELAY_REG); - clk_osm_write_reg(c, clk_osm_count_ns(c, 250), + clk_osm_write_reg(c, clk_osm_count_ns(c, 500), DROOP_RELEASE_TIMER_CTRL); clk_osm_write_reg(c, clk_osm_count_ns(c, 500), DCVS_DROOP_TIMER_CTRL); @@ -1801,6 +1809,11 @@ static void clk_osm_setup_fsms(struct clk_osm *c) BVAL(6, 0, 0x8); clk_osm_write_reg(c, val, DROOP_CTRL_REG); } + + /* Enable the PLL Droop Override */ + val = clk_osm_read_reg(c, OSM_PLL_SW_OVERRIDE_EN); + val |= PLL_SW_OVERRIDE_DROOP_EN; + clk_osm_write_reg(c, val, OSM_PLL_SW_OVERRIDE_EN); } static void clk_osm_do_additional_setup(struct clk_osm *c, @@ -1869,7 +1882,7 @@ static void clk_osm_apm_vc_setup(struct clk_osm *c) SEQ_REG(76)); /* Ensure writes complete before returning */ - mb(); + clk_osm_mb(c, OSM_BASE); } else { if (msmcobalt_v1) { scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(1), diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-8996-util.c b/drivers/clk/msm/mdss/mdss-dsi-pll-8996-util.c index f6c85cf8d9a4..5f779ec9bcc3 100644 --- a/drivers/clk/msm/mdss/mdss-dsi-pll-8996-util.c +++ b/drivers/clk/msm/mdss/mdss-dsi-pll-8996-util.c @@ -685,6 +685,10 @@ static void pll_db_commit_8996(struct mdss_pll_resources *pll, MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CTRL_1, 0); wmb(); /* make sure register committed */ + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_VCO_TUNE, 0); + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_KVCO_CODE, 0); + wmb(); /* make sure register committed */ + data = pdb->in.dsiclk_sel; /* set dsiclk_sel = 1 */ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CLK_CFG1, data); diff --git a/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c b/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c index d5d55a58bf7f..c4f77e01b682 100644 --- a/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c +++ b/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c @@ -145,7 +145,7 @@ static void hdmi_cobalt_get_div(struct cobalt_reg_cfg *cfg, unsigned long pclk) u32 const min_freq = 8000, max_freq = 12000; u32 const cmp_cnt = 1024; u32 const th_min = 500, th_max = 1000; - u64 bit_clk = pclk * HDMI_BIT_CLK_TO_PIX_CLK_RATIO; + u64 bit_clk = ((u64)pclk) * HDMI_BIT_CLK_TO_PIX_CLK_RATIO; u32 half_rate_mode = 0; u32 freq_optimal, list_elements; int optimal_index; @@ -161,7 +161,7 @@ find_optimal_index: for (i = 0; i < sz_ratio; i++) { for (j = 0; j < sz_band; j++) { - u64 freq = (bit_clk / (1 << half_rate_mode)); + u64 freq = div_u64(bit_clk, (1 << half_rate_mode)); freq *= (ratio_list[i] * (1 << band_list[j])); do_div(freq, (u64) HDMI_MHZ_TO_HZ); diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 5b9ce12c1e02..e39686ca4feb 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -155,6 +155,7 @@ config MSM_MMCC_8996 config MSM_GCC_FALCON tristate "MSMFALCON Global Clock Controller" + select QCOM_GDSC depends on COMMON_CLK_QCOM ---help--- Support for the global clock controller on Qualcomm Technologies, Inc @@ -162,6 +163,16 @@ config MSM_GCC_FALCON Say Y if you want to use peripheral devices such as UART, SPI, I2C, USB, UFS, SD/eMMC, PCIe, etc. +config MSM_GPUCC_FALCON + tristate "MSMFALCON Graphics Clock Controller" + select MSM_GCC_FALCON + depends on COMMON_CLK_QCOM + help + Support for the graphics clock controller on Qualcomm Technologies, Inc + MSMfalcon devices. + Say Y if you want to support graphics controller devices which will + be required to enable those device. + config QCOM_HFPLL tristate "High-Frequency PLL (HFPLL) Clock Controller" depends on COMMON_CLK_QCOM diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index af58f206bc4a..7ee0294e9dc7 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_MSM_GCC_FALCON) += gcc-msmfalcon.o obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o +obj-$(CONFIG_MSM_GPUCC_FALCON) += gpucc-msmfalcon.o obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o obj-$(CONFIG_QCOM_HFPLL) += hfpll.o obj-$(CONFIG_KRAITCC) += krait-cc.o diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index a075859771d3..933a208392bd 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -870,6 +870,7 @@ EXPORT_SYMBOL_GPL(clk_gfx3d_ops); static int clk_gfx3d_src_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { + struct clk_rcg2 *rcg = to_clk_rcg2(hw); struct clk_rate_request parent_req = { }; struct clk_hw *p1, *p3, *xo, *curr_p; const struct freq_tbl *f; diff --git a/drivers/clk/qcom/gpucc-msmfalcon.c b/drivers/clk/qcom/gpucc-msmfalcon.c new file mode 100644 index 000000000000..a2127e2629c7 --- /dev/null +++ b/drivers/clk/qcom/gpucc-msmfalcon.c @@ -0,0 +1,482 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/reset-controller.h> +#include <dt-bindings/clock/qcom,gpu-msmfalcon.h> + +#include "clk-alpha-pll.h" +#include "common.h" +#include "clk-regmap.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-branch.h" +#include "vdd-level-falcon.h" + +#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } +#define F_GFX(f, s, h, m, n, sf) { (f), (s), (2 * (h) - 1), (m), (n), (sf) } + +static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner, NULL); +static DEFINE_VDD_REGULATORS(vdd_mx, VDD_DIG_NUM, 1, vdd_corner, NULL); +static DEFINE_VDD_REGS_INIT(vdd_gfx, 1); + +enum { + P_CORE_BI_PLL_TEST_SE, + P_GPLL0_OUT_MAIN, + P_GPLL0_OUT_MAIN_DIV, + P_GPU_PLL0_PLL_OUT_MAIN, + P_GPU_PLL1_PLL_OUT_MAIN, + P_XO, +}; + +static const struct parent_map gpucc_parent_map_0[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 5 }, + { P_GPLL0_OUT_MAIN_DIV, 6 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gpucc_parent_names_0[] = { + "cxo_a", + "gcc_gpu_gpll0_clk", + "gcc_gpu_gpll0_div_clk", + "core_bi_pll_test_se", +}; + +static const struct parent_map gpucc_parent_map_1[] = { + { P_XO, 0 }, + { P_GPU_PLL0_PLL_OUT_MAIN, 1 }, + { P_GPU_PLL1_PLL_OUT_MAIN, 3 }, + { P_GPLL0_OUT_MAIN, 5 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const gpucc_parent_names_1[] = { + "xo", + "gpu_pll0_pll_out_main", + "gpu_pll1_pll_out_main", + "gcc_gpu_gpll0_clk", + "core_bi_pll_test_se", +}; + +static struct pll_vco gpu_vco[] = { + { 1000000000, 2000000000, 0 }, + { 500000000, 1000000000, 2 }, + { 250000000, 500000000, 3 }, +}; + +/* 640MHz configuration */ +static const struct pll_config gpu_pll0_config = { + .l = 0x21, + .config_ctl_val = 0x4001055b, + .alpha = 0x55555600, + .alpha_u = 0x55, + .alpha_en_mask = BIT(24), + .vco_val = 0x2 << 20, + .vco_mask = 0x3 << 20, + .main_output_mask = 0x1, +}; + +static struct pll_vco_data pll_data[] = { + /* Frequency post-div */ + { 640000000, 0x1 }, +}; + +static struct clk_alpha_pll gpu_pll0_pll_out_main = { + .offset = 0x0, + .vco_table = gpu_vco, + .num_vco = ARRAY_SIZE(gpu_vco), + .vco_data = pll_data, + .num_vco_data = ARRAY_SIZE(pll_data), + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "gpu_pll0_pll_out_main", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, + VDD_GPU_PLL_FMAX_MAP6( + MIN, 266000000, + LOWER, 432000000, + LOW, 640000000, + LOW_L1, 800000000, + NOMINAL, 1020000000, + HIGH, 1500000000), + }, + }, +}; + +static struct clk_alpha_pll gpu_pll1_pll_out_main = { + .offset = 0x40, + .vco_table = gpu_vco, + .num_vco = ARRAY_SIZE(gpu_vco), + .vco_data = pll_data, + .num_vco_data = ARRAY_SIZE(pll_data), + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "gpu_pll1_pll_out_main", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, + VDD_GPU_PLL_FMAX_MAP6( + MIN, 266000000, + LOWER, 432000000, + LOW, 640000000, + LOW_L1, 800000000, + NOMINAL, 1020000000, + HIGH, 1500000000), + }, + }, +}; + +/* GFX clock init data */ +static struct clk_init_data gpu_clks_init[] = { + [0] = { + .name = "gfx3d_clk_src", + .parent_names = gpucc_parent_names_1, + .num_parents = 3, + .ops = &clk_gfx3d_src_ops, + .flags = CLK_SET_RATE_PARENT, + }, + [1] = { + .name = "gpucc_gfx3d_clk", + .parent_names = (const char *[]){ + "gfx3d_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + .flags = CLK_SET_RATE_PARENT, + .vdd_class = &vdd_gfx, + }, +}; + +/* + * Frequencies and PLL configuration + * The PLL source would be to ping-pong between GPU-PLL0 + * and GPU-PLL1. + * ==================================================== + * | F | PLL SRC Freq | PLL postdiv | RCG Div | + * ==================================================== + * | 160000000 | 640000000 | 2 | 2 | + * | 266000000 | 532000000 | 1 | 2 | + * | 370000000 | 740000000 | 1 | 2 | + * | 465000000 | 930000000 | 1 | 2 | + * | 588000000 | 1176000000 | 1 | 2 | + * | 647000000 | 1294000000 | 1 | 2 | + * | 750000000 | 1500000000 | 1 | 2 | + * ==================================================== +*/ + +static const struct freq_tbl ftbl_gfx3d_clk_src[] = { + F_GFX( 19200000, 0, 1, 0, 0, 0), + F_GFX(160000000, 0, 2, 0, 0, 640000000), + F_GFX(266000000, 0, 2, 0, 0, 532000000), + F_GFX(370000000, 0, 2, 0, 0, 740000000), + F_GFX(465000000, 0, 2, 0, 0, 930000000), + F_GFX(588000000, 0, 2, 0, 0, 1176000000), + F_GFX(647000000, 0, 2, 0, 0, 1294000000), + F_GFX(750000000, 0, 2, 0, 0, 1500000000), + { } +}; + +static struct clk_rcg2 gfx3d_clk_src = { + .cmd_rcgr = 0x1070, + .mnd_width = 0, + .hid_width = 5, + .freq_tbl = ftbl_gfx3d_clk_src, + .parent_map = gpucc_parent_map_1, + .flags = FORCE_ENABLE_RCGR, + .clkr.hw.init = &gpu_clks_init[0], +}; + +static const struct freq_tbl ftbl_rbbmtimer_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 rbbmtimer_clk_src = { + .cmd_rcgr = 0x10b0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gpucc_parent_map_0, + .freq_tbl = ftbl_rbbmtimer_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "rbbmtimer_clk_src", + .parent_names = gpucc_parent_names_0, + .num_parents = 4, + .ops = &clk_rcg2_ops, + VDD_DIG_FMAX_MAP1(MIN, 19200000), + }, +}; + +static const struct freq_tbl ftbl_rbcpr_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(50000000, P_GPLL0_OUT_MAIN_DIV, 6, 0, 0), + { } +}; + +static struct clk_rcg2 rbcpr_clk_src = { + .cmd_rcgr = 0x1030, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gpucc_parent_map_0, + .freq_tbl = ftbl_rbcpr_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "rbcpr_clk_src", + .parent_names = gpucc_parent_names_0, + .num_parents = 4, + .ops = &clk_rcg2_ops, + VDD_DIG_FMAX_MAP2( + MIN, 19200000, + NOMINAL, 50000000), + }, +}; + +static struct clk_branch gpucc_cxo_clk = { + .halt_reg = 0x1020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1020, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpucc_cxo_clk", + .parent_names = (const char *[]) { + "cxo_a", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpucc_gfx3d_clk = { + .halt_reg = 0x1098, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1098, + .enable_mask = BIT(0), + .hw.init = &gpu_clks_init[1], + }, +}; + +static struct clk_branch gpucc_rbbmtimer_clk = { + .halt_reg = 0x10d0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x10d0, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpucc_rbbmtimer_clk", + .parent_names = (const char *[]){ + "rbbmtimer_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpucc_rbcpr_clk = { + .halt_reg = 0x1054, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1054, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpucc_rbcpr_clk", + .parent_names = (const char *[]){ + "rbcpr_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap *gpucc_falcon_clocks[] = { + [GFX3D_CLK_SRC] = &gfx3d_clk_src.clkr, + [GPU_PLL0_PLL] = &gpu_pll0_pll_out_main.clkr, + [GPU_PLL1_PLL] = &gpu_pll1_pll_out_main.clkr, + [GPUCC_CXO_CLK] = &gpucc_cxo_clk.clkr, + [GPUCC_GFX3D_CLK] = &gpucc_gfx3d_clk.clkr, + [GPUCC_RBBMTIMER_CLK] = &gpucc_rbbmtimer_clk.clkr, + [GPUCC_RBCPR_CLK] = &gpucc_rbcpr_clk.clkr, + [RBBMTIMER_CLK_SRC] = &rbbmtimer_clk_src.clkr, + [RBCPR_CLK_SRC] = &rbcpr_clk_src.clkr, +}; + +static const struct regmap_config gpucc_falcon_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x9034, + .fast_io = true, +}; + +static const struct qcom_cc_desc gpucc_falcon_desc = { + .config = &gpucc_falcon_regmap_config, + .clks = gpucc_falcon_clocks, + .num_clks = ARRAY_SIZE(gpucc_falcon_clocks), +}; + +static const struct of_device_id gpucc_falcon_match_table[] = { + { .compatible = "qcom,gpucc-msmfalcon" }, + { } +}; +MODULE_DEVICE_TABLE(of, gpucc_falcon_match_table); + +static int of_get_fmax_vdd_class(struct platform_device *pdev, + struct clk_hw *hw, char *prop_name, u32 index) +{ + struct device_node *of = pdev->dev.of_node; + int prop_len, i, j; + struct clk_vdd_class *vdd = hw->init->vdd_class; + int num = vdd->num_regulators + 1; + u32 *array; + + if (!of_find_property(of, prop_name, &prop_len)) { + dev_err(&pdev->dev, "missing %s\n", prop_name); + return -EINVAL; + } + + prop_len /= sizeof(u32); + if (prop_len % num) { + dev_err(&pdev->dev, "bad length %d\n", prop_len); + return -EINVAL; + } + + prop_len /= num; + vdd->level_votes = devm_kzalloc(&pdev->dev, prop_len * sizeof(int), + GFP_KERNEL); + if (!vdd->level_votes) + return -ENOMEM; + + vdd->vdd_uv = devm_kzalloc(&pdev->dev, + prop_len * sizeof(int) * (num - 1), GFP_KERNEL); + if (!vdd->vdd_uv) + return -ENOMEM; + + gpu_clks_init[index].fmax = devm_kzalloc(&pdev->dev, prop_len * + sizeof(unsigned long), GFP_KERNEL); + if (!gpu_clks_init[index].fmax) + return -ENOMEM; + + array = devm_kzalloc(&pdev->dev, prop_len * sizeof(u32) * num, + GFP_KERNEL); + if (!array) + return -ENOMEM; + + of_property_read_u32_array(of, prop_name, array, prop_len * num); + for (i = 0; i < prop_len; i++) { + gpu_clks_init[index].fmax[i] = array[num * i]; + for (j = 1; j < num; j++) { + vdd->vdd_uv[(num - 1) * i + (j - 1)] = + array[num * i + j]; + } + } + + devm_kfree(&pdev->dev, array); + vdd->num_levels = prop_len; + vdd->cur_level = prop_len; + gpu_clks_init[index].num_fmax = prop_len; + + return 0; +} + +static int gpucc_falcon_probe(struct platform_device *pdev) +{ + int ret = 0; + struct regmap *regmap; + + regmap = qcom_cc_map(pdev, &gpucc_falcon_desc); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* CX Regulator for RBBMTimer and RBCPR clock */ + vdd_dig.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_dig_gfx"); + if (IS_ERR(vdd_dig.regulator[0])) { + if (!(PTR_ERR(vdd_dig.regulator[0]) == -EPROBE_DEFER)) + dev_err(&pdev->dev, + "Unable to get vdd_dig regulator\n"); + return PTR_ERR(vdd_dig.regulator[0]); + } + + /* Mx Regulator for GPU-PLLs */ + vdd_mx.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_mx_gfx"); + if (IS_ERR(vdd_mx.regulator[0])) { + if (!(PTR_ERR(vdd_mx.regulator[0]) == -EPROBE_DEFER)) + dev_err(&pdev->dev, + "Unable to get vdd_mx regulator\n"); + return PTR_ERR(vdd_mx.regulator[0]); + } + + /* GFX Rail Regulator for GFX3D clock */ + vdd_gfx.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_gfx"); + if (IS_ERR(vdd_gfx.regulator[0])) { + if (!(PTR_ERR(vdd_gfx.regulator[0]) == -EPROBE_DEFER)) + dev_err(&pdev->dev, + "Unable to get vdd_gfx regulator\n"); + return PTR_ERR(vdd_gfx.regulator[0]); + } + + /* GFX rail fmax data linked to branch clock */ + of_get_fmax_vdd_class(pdev, &gpucc_gfx3d_clk.clkr.hw, + "qcom,gfxfreq-corner", 1); + + clk_alpha_pll_configure(&gpu_pll0_pll_out_main, regmap, + &gpu_pll0_config); + clk_alpha_pll_configure(&gpu_pll1_pll_out_main, regmap, + &gpu_pll0_config); + + ret = qcom_cc_really_probe(pdev, &gpucc_falcon_desc, regmap); + if (ret) { + dev_err(&pdev->dev, "Failed to register GPUCC clocks\n"); + return ret; + } + + clk_prepare_enable(gpucc_cxo_clk.clkr.hw.clk); + + dev_info(&pdev->dev, "Registered GPUCC clocks\n"); + + return ret; +} + +static struct platform_driver gpucc_falcon_driver = { + .probe = gpucc_falcon_probe, + .driver = { + .name = "gpucc-msmfalcon", + .of_match_table = gpucc_falcon_match_table, + }, +}; + +static int __init gpucc_falcon_init(void) +{ + return platform_driver_register(&gpucc_falcon_driver); +} +core_initcall_sync(gpucc_falcon_init); + +static void __exit gpucc_falcon_exit(void) +{ + platform_driver_unregister(&gpucc_falcon_driver); +} +module_exit(gpucc_falcon_exit); |
