222 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2018, The Linux Foundation. All rights reserved.4 */5 6#include <linux/bitops.h>7#include <linux/clk-provider.h>8#include <linux/err.h>9#include <linux/module.h>10#include <linux/platform_device.h>11#include <linux/pm_clock.h>12#include <linux/pm_runtime.h>13#include <linux/regmap.h>14 15#include <dt-bindings/clock/qcom,q6sstopcc-qcs404.h>16 17#include "clk-regmap.h"18#include "clk-branch.h"19#include "common.h"20#include "reset.h"21 22static struct clk_branch lcc_ahbfabric_cbc_clk = {23 .halt_reg = 0x1b004,24 .halt_check = BRANCH_HALT,25 .clkr = {26 .enable_reg = 0x1b004,27 .enable_mask = BIT(0),28 .hw.init = &(struct clk_init_data){29 .name = "lcc_ahbfabric_cbc_clk",30 .ops = &clk_branch2_ops,31 },32 },33};34 35static struct clk_branch lcc_q6ss_ahbs_cbc_clk = {36 .halt_reg = 0x22000,37 .halt_check = BRANCH_VOTED,38 .clkr = {39 .enable_reg = 0x22000,40 .enable_mask = BIT(0),41 .hw.init = &(struct clk_init_data){42 .name = "lcc_q6ss_ahbs_cbc_clk",43 .ops = &clk_branch2_ops,44 },45 },46};47 48static struct clk_branch lcc_q6ss_tcm_slave_cbc_clk = {49 .halt_reg = 0x1c000,50 .halt_check = BRANCH_VOTED,51 .clkr = {52 .enable_reg = 0x1c000,53 .enable_mask = BIT(0),54 .hw.init = &(struct clk_init_data){55 .name = "lcc_q6ss_tcm_slave_cbc_clk",56 .ops = &clk_branch2_ops,57 },58 },59};60 61static struct clk_branch lcc_q6ss_ahbm_cbc_clk = {62 .halt_reg = 0x22004,63 .halt_check = BRANCH_VOTED,64 .clkr = {65 .enable_reg = 0x22004,66 .enable_mask = BIT(0),67 .hw.init = &(struct clk_init_data){68 .name = "lcc_q6ss_ahbm_cbc_clk",69 .ops = &clk_branch2_ops,70 },71 },72};73 74static struct clk_branch lcc_q6ss_axim_cbc_clk = {75 .halt_reg = 0x1c004,76 .halt_check = BRANCH_VOTED,77 .clkr = {78 .enable_reg = 0x1c004,79 .enable_mask = BIT(0),80 .hw.init = &(struct clk_init_data){81 .name = "lcc_q6ss_axim_cbc_clk",82 .ops = &clk_branch2_ops,83 },84 },85};86 87static struct clk_branch lcc_q6ss_bcr_sleep_clk = {88 .halt_reg = 0x6004,89 .halt_check = BRANCH_VOTED,90 .clkr = {91 .enable_reg = 0x6004,92 .enable_mask = BIT(0),93 .hw.init = &(struct clk_init_data){94 .name = "lcc_q6ss_bcr_sleep_clk",95 .ops = &clk_branch2_ops,96 },97 },98};99 100/* TCSR clock */101static struct clk_branch tcsr_lcc_csr_cbcr_clk = {102 .halt_reg = 0x8008,103 .halt_check = BRANCH_VOTED,104 .clkr = {105 .enable_reg = 0x8008,106 .enable_mask = BIT(0),107 .hw.init = &(struct clk_init_data){108 .name = "tcsr_lcc_csr_cbcr_clk",109 .ops = &clk_branch2_ops,110 },111 },112};113 114static struct regmap_config q6sstop_regmap_config = {115 .reg_bits = 32,116 .reg_stride = 4,117 .val_bits = 32,118 .fast_io = true,119};120 121static struct clk_regmap *q6sstop_qcs404_clocks[] = {122 [LCC_AHBFABRIC_CBC_CLK] = &lcc_ahbfabric_cbc_clk.clkr,123 [LCC_Q6SS_AHBS_CBC_CLK] = &lcc_q6ss_ahbs_cbc_clk.clkr,124 [LCC_Q6SS_TCM_SLAVE_CBC_CLK] = &lcc_q6ss_tcm_slave_cbc_clk.clkr,125 [LCC_Q6SS_AHBM_CBC_CLK] = &lcc_q6ss_ahbm_cbc_clk.clkr,126 [LCC_Q6SS_AXIM_CBC_CLK] = &lcc_q6ss_axim_cbc_clk.clkr,127 [LCC_Q6SS_BCR_SLEEP_CLK] = &lcc_q6ss_bcr_sleep_clk.clkr,128};129 130static const struct qcom_reset_map q6sstop_qcs404_resets[] = {131 [Q6SSTOP_BCR_RESET] = { 0x6000 },132};133 134static const struct qcom_cc_desc q6sstop_qcs404_desc = {135 .config = &q6sstop_regmap_config,136 .clks = q6sstop_qcs404_clocks,137 .num_clks = ARRAY_SIZE(q6sstop_qcs404_clocks),138 .resets = q6sstop_qcs404_resets,139 .num_resets = ARRAY_SIZE(q6sstop_qcs404_resets),140};141 142static struct clk_regmap *tcsr_qcs404_clocks[] = {143 [TCSR_Q6SS_LCC_CBCR_CLK] = &tcsr_lcc_csr_cbcr_clk.clkr,144};145 146static const struct qcom_cc_desc tcsr_qcs404_desc = {147 .config = &q6sstop_regmap_config,148 .clks = tcsr_qcs404_clocks,149 .num_clks = ARRAY_SIZE(tcsr_qcs404_clocks),150};151 152static const struct of_device_id q6sstopcc_qcs404_match_table[] = {153 { .compatible = "qcom,qcs404-q6sstopcc" },154 { }155};156MODULE_DEVICE_TABLE(of, q6sstopcc_qcs404_match_table);157 158static int q6sstopcc_qcs404_probe(struct platform_device *pdev)159{160 const struct qcom_cc_desc *desc;161 int ret;162 163 ret = devm_pm_runtime_enable(&pdev->dev);164 if (ret)165 return ret;166 167 ret = devm_pm_clk_create(&pdev->dev);168 if (ret)169 return ret;170 171 ret = pm_clk_add(&pdev->dev, NULL);172 if (ret < 0) {173 dev_err(&pdev->dev, "failed to acquire iface clock\n");174 return ret;175 }176 177 ret = pm_runtime_resume_and_get(&pdev->dev);178 if (ret)179 return ret;180 181 q6sstop_regmap_config.name = "q6sstop_tcsr";182 desc = &tcsr_qcs404_desc;183 184 ret = qcom_cc_probe_by_index(pdev, 1, desc);185 if (ret)186 goto err_put_rpm;187 188 q6sstop_regmap_config.name = "q6sstop_cc";189 desc = &q6sstop_qcs404_desc;190 191 ret = qcom_cc_probe_by_index(pdev, 0, desc);192 if (ret)193 goto err_put_rpm;194 195 pm_runtime_put(&pdev->dev);196 197 return 0;198 199err_put_rpm:200 pm_runtime_put_sync(&pdev->dev);201 202 return ret;203}204 205static const struct dev_pm_ops q6sstopcc_pm_ops = {206 SET_RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL)207};208 209static struct platform_driver q6sstopcc_qcs404_driver = {210 .probe = q6sstopcc_qcs404_probe,211 .driver = {212 .name = "qcs404-q6sstopcc",213 .of_match_table = q6sstopcc_qcs404_match_table,214 .pm = &q6sstopcc_pm_ops,215 },216};217 218module_platform_driver(q6sstopcc_qcs404_driver);219 220MODULE_DESCRIPTION("QTI QCS404 Q6SSTOP Clock Controller Driver");221MODULE_LICENSE("GPL v2");222