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