Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'clk-qcom-rpm' into clk-next

* clk-qcom-rpm:
clk: qcom: Add support for RPM Clocks
clk: qcom: Add support for SMD-RPM Clocks
clk: qcom: Always add factor clock for xo clocks

+1201 -8
+37
Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
··· 1 + Qualcomm RPM Clock Controller Binding 2 + ------------------------------------------------ 3 + The RPM is a dedicated hardware engine for managing the shared 4 + SoC resources in order to keep the lowest power profile. It 5 + communicates with other hardware subsystems via shared memory 6 + and accepts clock requests, aggregates the requests and turns 7 + the clocks on/off or scales them on demand. 8 + 9 + Required properties : 10 + - compatible : shall contain only one of the following. The generic 11 + compatible "qcom,rpmcc" should be also included. 12 + 13 + "qcom,rpmcc-msm8916", "qcom,rpmcc" 14 + "qcom,rpmcc-apq8064", "qcom,rpmcc" 15 + 16 + - #clock-cells : shall contain 1 17 + 18 + Example: 19 + smd { 20 + compatible = "qcom,smd"; 21 + 22 + rpm { 23 + interrupts = <0 168 1>; 24 + qcom,ipc = <&apcs 8 0>; 25 + qcom,smd-edge = <15>; 26 + 27 + rpm_requests { 28 + compatible = "qcom,rpm-msm8916"; 29 + qcom,smd-channels = "rpm_requests"; 30 + 31 + rpmcc: clock-controller { 32 + compatible = "qcom,rpmcc-msm8916", "qcom,rpmcc"; 33 + #clock-cells = <1>; 34 + }; 35 + }; 36 + }; 37 + };
+29
drivers/clk/qcom/Kconfig
··· 2 2 bool 3 3 select PM_GENERIC_DOMAINS if PM 4 4 5 + config QCOM_RPMCC 6 + bool 7 + 5 8 config COMMON_CLK_QCOM 6 9 tristate "Support for Qualcomm's clock controllers" 7 10 depends on OF 8 11 depends on ARCH_QCOM || COMPILE_TEST 9 12 select REGMAP_MMIO 10 13 select RESET_CONTROLLER 14 + 15 + config QCOM_CLK_RPM 16 + tristate "RPM based Clock Controller" 17 + depends on COMMON_CLK_QCOM && MFD_QCOM_RPM 18 + select QCOM_RPMCC 19 + help 20 + The RPM (Resource Power Manager) is a dedicated hardware engine for 21 + managing the shared SoC resources in order to keep the lowest power 22 + profile. It communicates with other hardware subsystems via shared 23 + memory and accepts clock requests, aggregates the requests and turns 24 + the clocks on/off or scales them on demand. 25 + Say Y if you want to support the clocks exposed by the RPM on 26 + platforms such as apq8064, msm8660, msm8960 etc. 27 + 28 + config QCOM_CLK_SMD_RPM 29 + tristate "RPM over SMD based Clock Controller" 30 + depends on COMMON_CLK_QCOM && QCOM_SMD_RPM 31 + select QCOM_RPMCC 32 + help 33 + The RPM (Resource Power Manager) is a dedicated hardware engine for 34 + managing the shared SoC resources in order to keep the lowest power 35 + profile. It communicates with other hardware subsystems via shared 36 + memory and accepts clock requests, aggregates the requests and turns 37 + the clocks on/off or scales them on demand. 38 + Say Y if you want to support the clocks exposed by the RPM on 39 + platforms such as apq8016, apq8084, msm8974 etc. 11 40 12 41 config APQ_GCC_8084 13 42 tristate "APQ8084 Global Clock Controller"
+2
drivers/clk/qcom/Makefile
··· 30 30 obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o 31 31 obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o 32 32 obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o 33 + obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o 34 + obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
+489
drivers/clk/qcom/clk-rpm.c
··· 1 + /* 2 + * Copyright (c) 2016, Linaro Limited 3 + * Copyright (c) 2014, The Linux Foundation. All rights reserved. 4 + * 5 + * This software is licensed under the terms of the GNU General Public 6 + * License version 2, as published by the Free Software Foundation, and 7 + * may be copied, distributed, and modified under those terms. 8 + * 9 + * This program is distributed in the hope that it will be useful, 10 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + * GNU General Public License for more details. 13 + */ 14 + 15 + #include <linux/clk-provider.h> 16 + #include <linux/err.h> 17 + #include <linux/export.h> 18 + #include <linux/init.h> 19 + #include <linux/kernel.h> 20 + #include <linux/module.h> 21 + #include <linux/mutex.h> 22 + #include <linux/mfd/qcom_rpm.h> 23 + #include <linux/of.h> 24 + #include <linux/of_device.h> 25 + #include <linux/platform_device.h> 26 + 27 + #include <dt-bindings/mfd/qcom-rpm.h> 28 + #include <dt-bindings/clock/qcom,rpmcc.h> 29 + 30 + #define QCOM_RPM_MISC_CLK_TYPE 0x306b6c63 31 + #define QCOM_RPM_SCALING_ENABLE_ID 0x2 32 + 33 + #define DEFINE_CLK_RPM(_platform, _name, _active, r_id) \ 34 + static struct clk_rpm _platform##_##_active; \ 35 + static struct clk_rpm _platform##_##_name = { \ 36 + .rpm_clk_id = (r_id), \ 37 + .peer = &_platform##_##_active, \ 38 + .rate = INT_MAX, \ 39 + .hw.init = &(struct clk_init_data){ \ 40 + .ops = &clk_rpm_ops, \ 41 + .name = #_name, \ 42 + .parent_names = (const char *[]){ "pxo_board" }, \ 43 + .num_parents = 1, \ 44 + }, \ 45 + }; \ 46 + static struct clk_rpm _platform##_##_active = { \ 47 + .rpm_clk_id = (r_id), \ 48 + .peer = &_platform##_##_name, \ 49 + .active_only = true, \ 50 + .rate = INT_MAX, \ 51 + .hw.init = &(struct clk_init_data){ \ 52 + .ops = &clk_rpm_ops, \ 53 + .name = #_active, \ 54 + .parent_names = (const char *[]){ "pxo_board" }, \ 55 + .num_parents = 1, \ 56 + }, \ 57 + } 58 + 59 + #define DEFINE_CLK_RPM_PXO_BRANCH(_platform, _name, _active, r_id, r) \ 60 + static struct clk_rpm _platform##_##_active; \ 61 + static struct clk_rpm _platform##_##_name = { \ 62 + .rpm_clk_id = (r_id), \ 63 + .active_only = true, \ 64 + .peer = &_platform##_##_active, \ 65 + .rate = (r), \ 66 + .branch = true, \ 67 + .hw.init = &(struct clk_init_data){ \ 68 + .ops = &clk_rpm_branch_ops, \ 69 + .name = #_name, \ 70 + .parent_names = (const char *[]){ "pxo_board" }, \ 71 + .num_parents = 1, \ 72 + }, \ 73 + }; \ 74 + static struct clk_rpm _platform##_##_active = { \ 75 + .rpm_clk_id = (r_id), \ 76 + .peer = &_platform##_##_name, \ 77 + .rate = (r), \ 78 + .branch = true, \ 79 + .hw.init = &(struct clk_init_data){ \ 80 + .ops = &clk_rpm_branch_ops, \ 81 + .name = #_active, \ 82 + .parent_names = (const char *[]){ "pxo_board" }, \ 83 + .num_parents = 1, \ 84 + }, \ 85 + } 86 + 87 + #define DEFINE_CLK_RPM_CXO_BRANCH(_platform, _name, _active, r_id, r) \ 88 + static struct clk_rpm _platform##_##_active; \ 89 + static struct clk_rpm _platform##_##_name = { \ 90 + .rpm_clk_id = (r_id), \ 91 + .peer = &_platform##_##_active, \ 92 + .rate = (r), \ 93 + .branch = true, \ 94 + .hw.init = &(struct clk_init_data){ \ 95 + .ops = &clk_rpm_branch_ops, \ 96 + .name = #_name, \ 97 + .parent_names = (const char *[]){ "cxo_board" }, \ 98 + .num_parents = 1, \ 99 + }, \ 100 + }; \ 101 + static struct clk_rpm _platform##_##_active = { \ 102 + .rpm_clk_id = (r_id), \ 103 + .active_only = true, \ 104 + .peer = &_platform##_##_name, \ 105 + .rate = (r), \ 106 + .branch = true, \ 107 + .hw.init = &(struct clk_init_data){ \ 108 + .ops = &clk_rpm_branch_ops, \ 109 + .name = #_active, \ 110 + .parent_names = (const char *[]){ "cxo_board" }, \ 111 + .num_parents = 1, \ 112 + }, \ 113 + } 114 + 115 + #define to_clk_rpm(_hw) container_of(_hw, struct clk_rpm, hw) 116 + 117 + struct clk_rpm { 118 + const int rpm_clk_id; 119 + const bool active_only; 120 + unsigned long rate; 121 + bool enabled; 122 + bool branch; 123 + struct clk_rpm *peer; 124 + struct clk_hw hw; 125 + struct qcom_rpm *rpm; 126 + }; 127 + 128 + struct rpm_cc { 129 + struct qcom_rpm *rpm; 130 + struct clk_hw_onecell_data data; 131 + struct clk_hw *hws[]; 132 + }; 133 + 134 + struct rpm_clk_desc { 135 + struct clk_rpm **clks; 136 + size_t num_clks; 137 + }; 138 + 139 + static DEFINE_MUTEX(rpm_clk_lock); 140 + 141 + static int clk_rpm_handoff(struct clk_rpm *r) 142 + { 143 + int ret; 144 + u32 value = INT_MAX; 145 + 146 + ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, 147 + r->rpm_clk_id, &value, 1); 148 + if (ret) 149 + return ret; 150 + ret = qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE, 151 + r->rpm_clk_id, &value, 1); 152 + if (ret) 153 + return ret; 154 + 155 + return 0; 156 + } 157 + 158 + static int clk_rpm_set_rate_active(struct clk_rpm *r, unsigned long rate) 159 + { 160 + u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */ 161 + 162 + return qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, 163 + r->rpm_clk_id, &value, 1); 164 + } 165 + 166 + static int clk_rpm_set_rate_sleep(struct clk_rpm *r, unsigned long rate) 167 + { 168 + u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */ 169 + 170 + return qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE, 171 + r->rpm_clk_id, &value, 1); 172 + } 173 + 174 + static void to_active_sleep(struct clk_rpm *r, unsigned long rate, 175 + unsigned long *active, unsigned long *sleep) 176 + { 177 + *active = rate; 178 + 179 + /* 180 + * Active-only clocks don't care what the rate is during sleep. So, 181 + * they vote for zero. 182 + */ 183 + if (r->active_only) 184 + *sleep = 0; 185 + else 186 + *sleep = *active; 187 + } 188 + 189 + static int clk_rpm_prepare(struct clk_hw *hw) 190 + { 191 + struct clk_rpm *r = to_clk_rpm(hw); 192 + struct clk_rpm *peer = r->peer; 193 + unsigned long this_rate = 0, this_sleep_rate = 0; 194 + unsigned long peer_rate = 0, peer_sleep_rate = 0; 195 + unsigned long active_rate, sleep_rate; 196 + int ret = 0; 197 + 198 + mutex_lock(&rpm_clk_lock); 199 + 200 + /* Don't send requests to the RPM if the rate has not been set. */ 201 + if (!r->rate) 202 + goto out; 203 + 204 + to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate); 205 + 206 + /* Take peer clock's rate into account only if it's enabled. */ 207 + if (peer->enabled) 208 + to_active_sleep(peer, peer->rate, 209 + &peer_rate, &peer_sleep_rate); 210 + 211 + active_rate = max(this_rate, peer_rate); 212 + 213 + if (r->branch) 214 + active_rate = !!active_rate; 215 + 216 + ret = clk_rpm_set_rate_active(r, active_rate); 217 + if (ret) 218 + goto out; 219 + 220 + sleep_rate = max(this_sleep_rate, peer_sleep_rate); 221 + if (r->branch) 222 + sleep_rate = !!sleep_rate; 223 + 224 + ret = clk_rpm_set_rate_sleep(r, sleep_rate); 225 + if (ret) 226 + /* Undo the active set vote and restore it */ 227 + ret = clk_rpm_set_rate_active(r, peer_rate); 228 + 229 + out: 230 + if (!ret) 231 + r->enabled = true; 232 + 233 + mutex_unlock(&rpm_clk_lock); 234 + 235 + return ret; 236 + } 237 + 238 + static void clk_rpm_unprepare(struct clk_hw *hw) 239 + { 240 + struct clk_rpm *r = to_clk_rpm(hw); 241 + struct clk_rpm *peer = r->peer; 242 + unsigned long peer_rate = 0, peer_sleep_rate = 0; 243 + unsigned long active_rate, sleep_rate; 244 + int ret; 245 + 246 + mutex_lock(&rpm_clk_lock); 247 + 248 + if (!r->rate) 249 + goto out; 250 + 251 + /* Take peer clock's rate into account only if it's enabled. */ 252 + if (peer->enabled) 253 + to_active_sleep(peer, peer->rate, &peer_rate, 254 + &peer_sleep_rate); 255 + 256 + active_rate = r->branch ? !!peer_rate : peer_rate; 257 + ret = clk_rpm_set_rate_active(r, active_rate); 258 + if (ret) 259 + goto out; 260 + 261 + sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate; 262 + ret = clk_rpm_set_rate_sleep(r, sleep_rate); 263 + if (ret) 264 + goto out; 265 + 266 + r->enabled = false; 267 + 268 + out: 269 + mutex_unlock(&rpm_clk_lock); 270 + } 271 + 272 + static int clk_rpm_set_rate(struct clk_hw *hw, 273 + unsigned long rate, unsigned long parent_rate) 274 + { 275 + struct clk_rpm *r = to_clk_rpm(hw); 276 + struct clk_rpm *peer = r->peer; 277 + unsigned long active_rate, sleep_rate; 278 + unsigned long this_rate = 0, this_sleep_rate = 0; 279 + unsigned long peer_rate = 0, peer_sleep_rate = 0; 280 + int ret = 0; 281 + 282 + mutex_lock(&rpm_clk_lock); 283 + 284 + if (!r->enabled) 285 + goto out; 286 + 287 + to_active_sleep(r, rate, &this_rate, &this_sleep_rate); 288 + 289 + /* Take peer clock's rate into account only if it's enabled. */ 290 + if (peer->enabled) 291 + to_active_sleep(peer, peer->rate, 292 + &peer_rate, &peer_sleep_rate); 293 + 294 + active_rate = max(this_rate, peer_rate); 295 + ret = clk_rpm_set_rate_active(r, active_rate); 296 + if (ret) 297 + goto out; 298 + 299 + sleep_rate = max(this_sleep_rate, peer_sleep_rate); 300 + ret = clk_rpm_set_rate_sleep(r, sleep_rate); 301 + if (ret) 302 + goto out; 303 + 304 + r->rate = rate; 305 + 306 + out: 307 + mutex_unlock(&rpm_clk_lock); 308 + 309 + return ret; 310 + } 311 + 312 + static long clk_rpm_round_rate(struct clk_hw *hw, unsigned long rate, 313 + unsigned long *parent_rate) 314 + { 315 + /* 316 + * RPM handles rate rounding and we don't have a way to 317 + * know what the rate will be, so just return whatever 318 + * rate is requested. 319 + */ 320 + return rate; 321 + } 322 + 323 + static unsigned long clk_rpm_recalc_rate(struct clk_hw *hw, 324 + unsigned long parent_rate) 325 + { 326 + struct clk_rpm *r = to_clk_rpm(hw); 327 + 328 + /* 329 + * RPM handles rate rounding and we don't have a way to 330 + * know what the rate will be, so just return whatever 331 + * rate was set. 332 + */ 333 + return r->rate; 334 + } 335 + 336 + static const struct clk_ops clk_rpm_ops = { 337 + .prepare = clk_rpm_prepare, 338 + .unprepare = clk_rpm_unprepare, 339 + .set_rate = clk_rpm_set_rate, 340 + .round_rate = clk_rpm_round_rate, 341 + .recalc_rate = clk_rpm_recalc_rate, 342 + }; 343 + 344 + static const struct clk_ops clk_rpm_branch_ops = { 345 + .prepare = clk_rpm_prepare, 346 + .unprepare = clk_rpm_unprepare, 347 + .round_rate = clk_rpm_round_rate, 348 + .recalc_rate = clk_rpm_recalc_rate, 349 + }; 350 + 351 + /* apq8064 */ 352 + DEFINE_CLK_RPM(apq8064, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); 353 + DEFINE_CLK_RPM(apq8064, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); 354 + DEFINE_CLK_RPM(apq8064, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); 355 + DEFINE_CLK_RPM(apq8064, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK); 356 + DEFINE_CLK_RPM(apq8064, mmfab_clk, mmfab_a_clk, QCOM_RPM_MM_FABRIC_CLK); 357 + DEFINE_CLK_RPM(apq8064, mmfpb_clk, mmfpb_a_clk, QCOM_RPM_MMFPB_CLK); 358 + DEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK); 359 + DEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); 360 + DEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK); 361 + 362 + static struct clk_rpm *apq8064_clks[] = { 363 + [RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk, 364 + [RPM_APPS_FABRIC_A_CLK] = &apq8064_afab_a_clk, 365 + [RPM_CFPB_CLK] = &apq8064_cfpb_clk, 366 + [RPM_CFPB_A_CLK] = &apq8064_cfpb_a_clk, 367 + [RPM_DAYTONA_FABRIC_CLK] = &apq8064_daytona_clk, 368 + [RPM_DAYTONA_FABRIC_A_CLK] = &apq8064_daytona_a_clk, 369 + [RPM_EBI1_CLK] = &apq8064_ebi1_clk, 370 + [RPM_EBI1_A_CLK] = &apq8064_ebi1_a_clk, 371 + [RPM_MM_FABRIC_CLK] = &apq8064_mmfab_clk, 372 + [RPM_MM_FABRIC_A_CLK] = &apq8064_mmfab_a_clk, 373 + [RPM_MMFPB_CLK] = &apq8064_mmfpb_clk, 374 + [RPM_MMFPB_A_CLK] = &apq8064_mmfpb_a_clk, 375 + [RPM_SYS_FABRIC_CLK] = &apq8064_sfab_clk, 376 + [RPM_SYS_FABRIC_A_CLK] = &apq8064_sfab_a_clk, 377 + [RPM_SFPB_CLK] = &apq8064_sfpb_clk, 378 + [RPM_SFPB_A_CLK] = &apq8064_sfpb_a_clk, 379 + [RPM_QDSS_CLK] = &apq8064_qdss_clk, 380 + [RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk, 381 + }; 382 + 383 + static const struct rpm_clk_desc rpm_clk_apq8064 = { 384 + .clks = apq8064_clks, 385 + .num_clks = ARRAY_SIZE(apq8064_clks), 386 + }; 387 + 388 + static const struct of_device_id rpm_clk_match_table[] = { 389 + { .compatible = "qcom,rpmcc-apq8064", .data = &rpm_clk_apq8064 }, 390 + { } 391 + }; 392 + MODULE_DEVICE_TABLE(of, rpm_clk_match_table); 393 + 394 + static int rpm_clk_probe(struct platform_device *pdev) 395 + { 396 + struct clk_hw **hws; 397 + struct rpm_cc *rcc; 398 + struct clk_hw_onecell_data *data; 399 + int ret; 400 + size_t num_clks, i; 401 + struct qcom_rpm *rpm; 402 + struct clk_rpm **rpm_clks; 403 + const struct rpm_clk_desc *desc; 404 + 405 + rpm = dev_get_drvdata(pdev->dev.parent); 406 + if (!rpm) { 407 + dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n"); 408 + return -ENODEV; 409 + } 410 + 411 + desc = of_device_get_match_data(&pdev->dev); 412 + if (!desc) 413 + return -EINVAL; 414 + 415 + rpm_clks = desc->clks; 416 + num_clks = desc->num_clks; 417 + 418 + rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*hws) * num_clks, 419 + GFP_KERNEL); 420 + if (!rcc) 421 + return -ENOMEM; 422 + 423 + hws = rcc->hws; 424 + data = &rcc->data; 425 + data->num = num_clks; 426 + 427 + for (i = 0; i < num_clks; i++) { 428 + if (!rpm_clks[i]) 429 + continue; 430 + 431 + rpm_clks[i]->rpm = rpm; 432 + 433 + ret = clk_rpm_handoff(rpm_clks[i]); 434 + if (ret) 435 + goto err; 436 + } 437 + 438 + for (i = 0; i < num_clks; i++) { 439 + if (!rpm_clks[i]) { 440 + data->hws[i] = ERR_PTR(-ENOENT); 441 + continue; 442 + } 443 + 444 + ret = devm_clk_hw_register(&pdev->dev, &rpm_clks[i]->hw); 445 + if (ret) 446 + goto err; 447 + } 448 + 449 + ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get, 450 + data); 451 + if (ret) 452 + goto err; 453 + 454 + return 0; 455 + err: 456 + dev_err(&pdev->dev, "Error registering RPM Clock driver (%d)\n", ret); 457 + return ret; 458 + } 459 + 460 + static int rpm_clk_remove(struct platform_device *pdev) 461 + { 462 + of_clk_del_provider(pdev->dev.of_node); 463 + return 0; 464 + } 465 + 466 + static struct platform_driver rpm_clk_driver = { 467 + .driver = { 468 + .name = "qcom-clk-rpm", 469 + .of_match_table = rpm_clk_match_table, 470 + }, 471 + .probe = rpm_clk_probe, 472 + .remove = rpm_clk_remove, 473 + }; 474 + 475 + static int __init rpm_clk_init(void) 476 + { 477 + return platform_driver_register(&rpm_clk_driver); 478 + } 479 + core_initcall(rpm_clk_init); 480 + 481 + static void __exit rpm_clk_exit(void) 482 + { 483 + platform_driver_unregister(&rpm_clk_driver); 484 + } 485 + module_exit(rpm_clk_exit); 486 + 487 + MODULE_DESCRIPTION("Qualcomm RPM Clock Controller Driver"); 488 + MODULE_LICENSE("GPL v2"); 489 + MODULE_ALIAS("platform:qcom-clk-rpm");
+570
drivers/clk/qcom/clk-smd-rpm.c
··· 1 + /* 2 + * Copyright (c) 2016, Linaro Limited 3 + * Copyright (c) 2014, The Linux Foundation. All rights reserved. 4 + * 5 + * This software is licensed under the terms of the GNU General Public 6 + * License version 2, as published by the Free Software Foundation, and 7 + * may be copied, distributed, and modified under those terms. 8 + * 9 + * This program is distributed in the hope that it will be useful, 10 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + * GNU General Public License for more details. 13 + */ 14 + 15 + #include <linux/clk-provider.h> 16 + #include <linux/err.h> 17 + #include <linux/export.h> 18 + #include <linux/init.h> 19 + #include <linux/kernel.h> 20 + #include <linux/module.h> 21 + #include <linux/mutex.h> 22 + #include <linux/of.h> 23 + #include <linux/of_device.h> 24 + #include <linux/platform_device.h> 25 + #include <linux/soc/qcom/smd-rpm.h> 26 + 27 + #include <dt-bindings/clock/qcom,rpmcc.h> 28 + #include <dt-bindings/mfd/qcom-rpm.h> 29 + 30 + #define QCOM_RPM_KEY_SOFTWARE_ENABLE 0x6e657773 31 + #define QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY 0x62636370 32 + #define QCOM_RPM_SMD_KEY_RATE 0x007a484b 33 + #define QCOM_RPM_SMD_KEY_ENABLE 0x62616e45 34 + #define QCOM_RPM_SMD_KEY_STATE 0x54415453 35 + #define QCOM_RPM_SCALING_ENABLE_ID 0x2 36 + 37 + #define __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, stat_id, \ 38 + key) \ 39 + static struct clk_smd_rpm _platform##_##_active; \ 40 + static struct clk_smd_rpm _platform##_##_name = { \ 41 + .rpm_res_type = (type), \ 42 + .rpm_clk_id = (r_id), \ 43 + .rpm_status_id = (stat_id), \ 44 + .rpm_key = (key), \ 45 + .peer = &_platform##_##_active, \ 46 + .rate = INT_MAX, \ 47 + .hw.init = &(struct clk_init_data){ \ 48 + .ops = &clk_smd_rpm_ops, \ 49 + .name = #_name, \ 50 + .parent_names = (const char *[]){ "xo_board" }, \ 51 + .num_parents = 1, \ 52 + }, \ 53 + }; \ 54 + static struct clk_smd_rpm _platform##_##_active = { \ 55 + .rpm_res_type = (type), \ 56 + .rpm_clk_id = (r_id), \ 57 + .rpm_status_id = (stat_id), \ 58 + .active_only = true, \ 59 + .rpm_key = (key), \ 60 + .peer = &_platform##_##_name, \ 61 + .rate = INT_MAX, \ 62 + .hw.init = &(struct clk_init_data){ \ 63 + .ops = &clk_smd_rpm_ops, \ 64 + .name = #_active, \ 65 + .parent_names = (const char *[]){ "xo_board" }, \ 66 + .num_parents = 1, \ 67 + }, \ 68 + } 69 + 70 + #define __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, \ 71 + stat_id, r, key) \ 72 + static struct clk_smd_rpm _platform##_##_active; \ 73 + static struct clk_smd_rpm _platform##_##_name = { \ 74 + .rpm_res_type = (type), \ 75 + .rpm_clk_id = (r_id), \ 76 + .rpm_status_id = (stat_id), \ 77 + .rpm_key = (key), \ 78 + .branch = true, \ 79 + .peer = &_platform##_##_active, \ 80 + .rate = (r), \ 81 + .hw.init = &(struct clk_init_data){ \ 82 + .ops = &clk_smd_rpm_branch_ops, \ 83 + .name = #_name, \ 84 + .parent_names = (const char *[]){ "xo_board" }, \ 85 + .num_parents = 1, \ 86 + }, \ 87 + }; \ 88 + static struct clk_smd_rpm _platform##_##_active = { \ 89 + .rpm_res_type = (type), \ 90 + .rpm_clk_id = (r_id), \ 91 + .rpm_status_id = (stat_id), \ 92 + .active_only = true, \ 93 + .rpm_key = (key), \ 94 + .branch = true, \ 95 + .peer = &_platform##_##_name, \ 96 + .rate = (r), \ 97 + .hw.init = &(struct clk_init_data){ \ 98 + .ops = &clk_smd_rpm_branch_ops, \ 99 + .name = #_active, \ 100 + .parent_names = (const char *[]){ "xo_board" }, \ 101 + .num_parents = 1, \ 102 + }, \ 103 + } 104 + 105 + #define DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id) \ 106 + __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \ 107 + 0, QCOM_RPM_SMD_KEY_RATE) 108 + 109 + #define DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, r) \ 110 + __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, \ 111 + r_id, 0, r, QCOM_RPM_SMD_KEY_ENABLE) 112 + 113 + #define DEFINE_CLK_SMD_RPM_QDSS(_platform, _name, _active, type, r_id) \ 114 + __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \ 115 + 0, QCOM_RPM_SMD_KEY_STATE) 116 + 117 + #define DEFINE_CLK_SMD_RPM_XO_BUFFER(_platform, _name, _active, r_id) \ 118 + __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \ 119 + QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \ 120 + QCOM_RPM_KEY_SOFTWARE_ENABLE) 121 + 122 + #define DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(_platform, _name, _active, r_id) \ 123 + __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \ 124 + QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \ 125 + QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY) 126 + 127 + #define to_clk_smd_rpm(_hw) container_of(_hw, struct clk_smd_rpm, hw) 128 + 129 + struct clk_smd_rpm { 130 + const int rpm_res_type; 131 + const int rpm_key; 132 + const int rpm_clk_id; 133 + const int rpm_status_id; 134 + const bool active_only; 135 + bool enabled; 136 + bool branch; 137 + struct clk_smd_rpm *peer; 138 + struct clk_hw hw; 139 + unsigned long rate; 140 + struct qcom_smd_rpm *rpm; 141 + }; 142 + 143 + struct clk_smd_rpm_req { 144 + __le32 key; 145 + __le32 nbytes; 146 + __le32 value; 147 + }; 148 + 149 + struct rpm_cc { 150 + struct qcom_rpm *rpm; 151 + struct clk_hw_onecell_data data; 152 + struct clk_hw *hws[]; 153 + }; 154 + 155 + struct rpm_smd_clk_desc { 156 + struct clk_smd_rpm **clks; 157 + size_t num_clks; 158 + }; 159 + 160 + static DEFINE_MUTEX(rpm_smd_clk_lock); 161 + 162 + static int clk_smd_rpm_handoff(struct clk_smd_rpm *r) 163 + { 164 + int ret; 165 + struct clk_smd_rpm_req req = { 166 + .key = cpu_to_le32(r->rpm_key), 167 + .nbytes = cpu_to_le32(sizeof(u32)), 168 + .value = cpu_to_le32(INT_MAX), 169 + }; 170 + 171 + ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE, 172 + r->rpm_res_type, r->rpm_clk_id, &req, 173 + sizeof(req)); 174 + if (ret) 175 + return ret; 176 + ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE, 177 + r->rpm_res_type, r->rpm_clk_id, &req, 178 + sizeof(req)); 179 + if (ret) 180 + return ret; 181 + 182 + return 0; 183 + } 184 + 185 + static int clk_smd_rpm_set_rate_active(struct clk_smd_rpm *r, 186 + unsigned long rate) 187 + { 188 + struct clk_smd_rpm_req req = { 189 + .key = cpu_to_le32(r->rpm_key), 190 + .nbytes = cpu_to_le32(sizeof(u32)), 191 + .value = cpu_to_le32(DIV_ROUND_UP(rate, 1000)), /* to kHz */ 192 + }; 193 + 194 + return qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE, 195 + r->rpm_res_type, r->rpm_clk_id, &req, 196 + sizeof(req)); 197 + } 198 + 199 + static int clk_smd_rpm_set_rate_sleep(struct clk_smd_rpm *r, 200 + unsigned long rate) 201 + { 202 + struct clk_smd_rpm_req req = { 203 + .key = cpu_to_le32(r->rpm_key), 204 + .nbytes = cpu_to_le32(sizeof(u32)), 205 + .value = cpu_to_le32(DIV_ROUND_UP(rate, 1000)), /* to kHz */ 206 + }; 207 + 208 + return qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE, 209 + r->rpm_res_type, r->rpm_clk_id, &req, 210 + sizeof(req)); 211 + } 212 + 213 + static void to_active_sleep(struct clk_smd_rpm *r, unsigned long rate, 214 + unsigned long *active, unsigned long *sleep) 215 + { 216 + *active = rate; 217 + 218 + /* 219 + * Active-only clocks don't care what the rate is during sleep. So, 220 + * they vote for zero. 221 + */ 222 + if (r->active_only) 223 + *sleep = 0; 224 + else 225 + *sleep = *active; 226 + } 227 + 228 + static int clk_smd_rpm_prepare(struct clk_hw *hw) 229 + { 230 + struct clk_smd_rpm *r = to_clk_smd_rpm(hw); 231 + struct clk_smd_rpm *peer = r->peer; 232 + unsigned long this_rate = 0, this_sleep_rate = 0; 233 + unsigned long peer_rate = 0, peer_sleep_rate = 0; 234 + unsigned long active_rate, sleep_rate; 235 + int ret = 0; 236 + 237 + mutex_lock(&rpm_smd_clk_lock); 238 + 239 + /* Don't send requests to the RPM if the rate has not been set. */ 240 + if (!r->rate) 241 + goto out; 242 + 243 + to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate); 244 + 245 + /* Take peer clock's rate into account only if it's enabled. */ 246 + if (peer->enabled) 247 + to_active_sleep(peer, peer->rate, 248 + &peer_rate, &peer_sleep_rate); 249 + 250 + active_rate = max(this_rate, peer_rate); 251 + 252 + if (r->branch) 253 + active_rate = !!active_rate; 254 + 255 + ret = clk_smd_rpm_set_rate_active(r, active_rate); 256 + if (ret) 257 + goto out; 258 + 259 + sleep_rate = max(this_sleep_rate, peer_sleep_rate); 260 + if (r->branch) 261 + sleep_rate = !!sleep_rate; 262 + 263 + ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate); 264 + if (ret) 265 + /* Undo the active set vote and restore it */ 266 + ret = clk_smd_rpm_set_rate_active(r, peer_rate); 267 + 268 + out: 269 + if (!ret) 270 + r->enabled = true; 271 + 272 + mutex_unlock(&rpm_smd_clk_lock); 273 + 274 + return ret; 275 + } 276 + 277 + static void clk_smd_rpm_unprepare(struct clk_hw *hw) 278 + { 279 + struct clk_smd_rpm *r = to_clk_smd_rpm(hw); 280 + struct clk_smd_rpm *peer = r->peer; 281 + unsigned long peer_rate = 0, peer_sleep_rate = 0; 282 + unsigned long active_rate, sleep_rate; 283 + int ret; 284 + 285 + mutex_lock(&rpm_smd_clk_lock); 286 + 287 + if (!r->rate) 288 + goto out; 289 + 290 + /* Take peer clock's rate into account only if it's enabled. */ 291 + if (peer->enabled) 292 + to_active_sleep(peer, peer->rate, &peer_rate, 293 + &peer_sleep_rate); 294 + 295 + active_rate = r->branch ? !!peer_rate : peer_rate; 296 + ret = clk_smd_rpm_set_rate_active(r, active_rate); 297 + if (ret) 298 + goto out; 299 + 300 + sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate; 301 + ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate); 302 + if (ret) 303 + goto out; 304 + 305 + r->enabled = false; 306 + 307 + out: 308 + mutex_unlock(&rpm_smd_clk_lock); 309 + } 310 + 311 + static int clk_smd_rpm_set_rate(struct clk_hw *hw, unsigned long rate, 312 + unsigned long parent_rate) 313 + { 314 + struct clk_smd_rpm *r = to_clk_smd_rpm(hw); 315 + struct clk_smd_rpm *peer = r->peer; 316 + unsigned long active_rate, sleep_rate; 317 + unsigned long this_rate = 0, this_sleep_rate = 0; 318 + unsigned long peer_rate = 0, peer_sleep_rate = 0; 319 + int ret = 0; 320 + 321 + mutex_lock(&rpm_smd_clk_lock); 322 + 323 + if (!r->enabled) 324 + goto out; 325 + 326 + to_active_sleep(r, rate, &this_rate, &this_sleep_rate); 327 + 328 + /* Take peer clock's rate into account only if it's enabled. */ 329 + if (peer->enabled) 330 + to_active_sleep(peer, peer->rate, 331 + &peer_rate, &peer_sleep_rate); 332 + 333 + active_rate = max(this_rate, peer_rate); 334 + ret = clk_smd_rpm_set_rate_active(r, active_rate); 335 + if (ret) 336 + goto out; 337 + 338 + sleep_rate = max(this_sleep_rate, peer_sleep_rate); 339 + ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate); 340 + if (ret) 341 + goto out; 342 + 343 + r->rate = rate; 344 + 345 + out: 346 + mutex_unlock(&rpm_smd_clk_lock); 347 + 348 + return ret; 349 + } 350 + 351 + static long clk_smd_rpm_round_rate(struct clk_hw *hw, unsigned long rate, 352 + unsigned long *parent_rate) 353 + { 354 + /* 355 + * RPM handles rate rounding and we don't have a way to 356 + * know what the rate will be, so just return whatever 357 + * rate is requested. 358 + */ 359 + return rate; 360 + } 361 + 362 + static unsigned long clk_smd_rpm_recalc_rate(struct clk_hw *hw, 363 + unsigned long parent_rate) 364 + { 365 + struct clk_smd_rpm *r = to_clk_smd_rpm(hw); 366 + 367 + /* 368 + * RPM handles rate rounding and we don't have a way to 369 + * know what the rate will be, so just return whatever 370 + * rate was set. 371 + */ 372 + return r->rate; 373 + } 374 + 375 + static int clk_smd_rpm_enable_scaling(struct qcom_smd_rpm *rpm) 376 + { 377 + int ret; 378 + struct clk_smd_rpm_req req = { 379 + .key = cpu_to_le32(QCOM_RPM_SMD_KEY_ENABLE), 380 + .nbytes = cpu_to_le32(sizeof(u32)), 381 + .value = cpu_to_le32(1), 382 + }; 383 + 384 + ret = qcom_rpm_smd_write(rpm, QCOM_SMD_RPM_SLEEP_STATE, 385 + QCOM_SMD_RPM_MISC_CLK, 386 + QCOM_RPM_SCALING_ENABLE_ID, &req, sizeof(req)); 387 + if (ret) { 388 + pr_err("RPM clock scaling (sleep set) not enabled!\n"); 389 + return ret; 390 + } 391 + 392 + ret = qcom_rpm_smd_write(rpm, QCOM_SMD_RPM_ACTIVE_STATE, 393 + QCOM_SMD_RPM_MISC_CLK, 394 + QCOM_RPM_SCALING_ENABLE_ID, &req, sizeof(req)); 395 + if (ret) { 396 + pr_err("RPM clock scaling (active set) not enabled!\n"); 397 + return ret; 398 + } 399 + 400 + pr_debug("%s: RPM clock scaling is enabled\n", __func__); 401 + return 0; 402 + } 403 + 404 + static const struct clk_ops clk_smd_rpm_ops = { 405 + .prepare = clk_smd_rpm_prepare, 406 + .unprepare = clk_smd_rpm_unprepare, 407 + .set_rate = clk_smd_rpm_set_rate, 408 + .round_rate = clk_smd_rpm_round_rate, 409 + .recalc_rate = clk_smd_rpm_recalc_rate, 410 + }; 411 + 412 + static const struct clk_ops clk_smd_rpm_branch_ops = { 413 + .prepare = clk_smd_rpm_prepare, 414 + .unprepare = clk_smd_rpm_unprepare, 415 + .round_rate = clk_smd_rpm_round_rate, 416 + .recalc_rate = clk_smd_rpm_recalc_rate, 417 + }; 418 + 419 + /* msm8916 */ 420 + DEFINE_CLK_SMD_RPM(msm8916, pcnoc_clk, pcnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); 421 + DEFINE_CLK_SMD_RPM(msm8916, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); 422 + DEFINE_CLK_SMD_RPM(msm8916, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0); 423 + DEFINE_CLK_SMD_RPM_QDSS(msm8916, qdss_clk, qdss_a_clk, QCOM_SMD_RPM_MISC_CLK, 1); 424 + DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk1, bb_clk1_a, 1); 425 + DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk2, bb_clk2_a, 2); 426 + DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, rf_clk1, rf_clk1_a, 4); 427 + DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, rf_clk2, rf_clk2_a, 5); 428 + DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, bb_clk1_pin, bb_clk1_a_pin, 1); 429 + DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, bb_clk2_pin, bb_clk2_a_pin, 2); 430 + DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk1_pin, rf_clk1_a_pin, 4); 431 + DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk2_pin, rf_clk2_a_pin, 5); 432 + 433 + static struct clk_smd_rpm *msm8916_clks[] = { 434 + [RPM_SMD_PCNOC_CLK] = &msm8916_pcnoc_clk, 435 + [RPM_SMD_PCNOC_A_CLK] = &msm8916_pcnoc_a_clk, 436 + [RPM_SMD_SNOC_CLK] = &msm8916_snoc_clk, 437 + [RPM_SMD_SNOC_A_CLK] = &msm8916_snoc_a_clk, 438 + [RPM_SMD_BIMC_CLK] = &msm8916_bimc_clk, 439 + [RPM_SMD_BIMC_A_CLK] = &msm8916_bimc_a_clk, 440 + [RPM_SMD_QDSS_CLK] = &msm8916_qdss_clk, 441 + [RPM_SMD_QDSS_A_CLK] = &msm8916_qdss_a_clk, 442 + [RPM_SMD_BB_CLK1] = &msm8916_bb_clk1, 443 + [RPM_SMD_BB_CLK1_A] = &msm8916_bb_clk1_a, 444 + [RPM_SMD_BB_CLK2] = &msm8916_bb_clk2, 445 + [RPM_SMD_BB_CLK2_A] = &msm8916_bb_clk2_a, 446 + [RPM_SMD_RF_CLK1] = &msm8916_rf_clk1, 447 + [RPM_SMD_RF_CLK1_A] = &msm8916_rf_clk1_a, 448 + [RPM_SMD_RF_CLK2] = &msm8916_rf_clk2, 449 + [RPM_SMD_RF_CLK2_A] = &msm8916_rf_clk2_a, 450 + [RPM_SMD_BB_CLK1_PIN] = &msm8916_bb_clk1_pin, 451 + [RPM_SMD_BB_CLK1_A_PIN] = &msm8916_bb_clk1_a_pin, 452 + [RPM_SMD_BB_CLK2_PIN] = &msm8916_bb_clk2_pin, 453 + [RPM_SMD_BB_CLK2_A_PIN] = &msm8916_bb_clk2_a_pin, 454 + [RPM_SMD_RF_CLK1_PIN] = &msm8916_rf_clk1_pin, 455 + [RPM_SMD_RF_CLK1_A_PIN] = &msm8916_rf_clk1_a_pin, 456 + [RPM_SMD_RF_CLK2_PIN] = &msm8916_rf_clk2_pin, 457 + [RPM_SMD_RF_CLK2_A_PIN] = &msm8916_rf_clk2_a_pin, 458 + }; 459 + 460 + static const struct rpm_smd_clk_desc rpm_clk_msm8916 = { 461 + .clks = msm8916_clks, 462 + .num_clks = ARRAY_SIZE(msm8916_clks), 463 + }; 464 + 465 + static const struct of_device_id rpm_smd_clk_match_table[] = { 466 + { .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916 }, 467 + { } 468 + }; 469 + MODULE_DEVICE_TABLE(of, rpm_smd_clk_match_table); 470 + 471 + static int rpm_smd_clk_probe(struct platform_device *pdev) 472 + { 473 + struct clk_hw **hws; 474 + struct rpm_cc *rcc; 475 + struct clk_hw_onecell_data *data; 476 + int ret; 477 + size_t num_clks, i; 478 + struct qcom_smd_rpm *rpm; 479 + struct clk_smd_rpm **rpm_smd_clks; 480 + const struct rpm_smd_clk_desc *desc; 481 + 482 + rpm = dev_get_drvdata(pdev->dev.parent); 483 + if (!rpm) { 484 + dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n"); 485 + return -ENODEV; 486 + } 487 + 488 + desc = of_device_get_match_data(&pdev->dev); 489 + if (!desc) 490 + return -EINVAL; 491 + 492 + rpm_smd_clks = desc->clks; 493 + num_clks = desc->num_clks; 494 + 495 + rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*hws) * num_clks, 496 + GFP_KERNEL); 497 + if (!rcc) 498 + return -ENOMEM; 499 + 500 + hws = rcc->hws; 501 + data = &rcc->data; 502 + data->num = num_clks; 503 + 504 + for (i = 0; i < num_clks; i++) { 505 + if (!rpm_smd_clks[i]) 506 + continue; 507 + 508 + rpm_smd_clks[i]->rpm = rpm; 509 + 510 + ret = clk_smd_rpm_handoff(rpm_smd_clks[i]); 511 + if (ret) 512 + goto err; 513 + } 514 + 515 + ret = clk_smd_rpm_enable_scaling(rpm); 516 + if (ret) 517 + goto err; 518 + 519 + for (i = 0; i < num_clks; i++) { 520 + if (!rpm_smd_clks[i]) { 521 + data->hws[i] = ERR_PTR(-ENOENT); 522 + continue; 523 + } 524 + 525 + ret = devm_clk_hw_register(&pdev->dev, &rpm_smd_clks[i]->hw); 526 + if (ret) 527 + goto err; 528 + } 529 + 530 + ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get, 531 + data); 532 + if (ret) 533 + goto err; 534 + 535 + return 0; 536 + err: 537 + dev_err(&pdev->dev, "Error registering SMD clock driver (%d)\n", ret); 538 + return ret; 539 + } 540 + 541 + static int rpm_smd_clk_remove(struct platform_device *pdev) 542 + { 543 + of_clk_del_provider(pdev->dev.of_node); 544 + return 0; 545 + } 546 + 547 + static struct platform_driver rpm_smd_clk_driver = { 548 + .driver = { 549 + .name = "qcom-clk-smd-rpm", 550 + .of_match_table = rpm_smd_clk_match_table, 551 + }, 552 + .probe = rpm_smd_clk_probe, 553 + .remove = rpm_smd_clk_remove, 554 + }; 555 + 556 + static int __init rpm_smd_clk_init(void) 557 + { 558 + return platform_driver_register(&rpm_smd_clk_driver); 559 + } 560 + core_initcall(rpm_smd_clk_init); 561 + 562 + static void __exit rpm_smd_clk_exit(void) 563 + { 564 + platform_driver_unregister(&rpm_smd_clk_driver); 565 + } 566 + module_exit(rpm_smd_clk_exit); 567 + 568 + MODULE_DESCRIPTION("Qualcomm RPM over SMD Clock Controller Driver"); 569 + MODULE_LICENSE("GPL v2"); 570 + MODULE_ALIAS("platform:qcom-clk-smd-rpm");
+5 -8
drivers/clk/qcom/common.c
··· 174 174 const char *name, unsigned long rate) 175 175 { 176 176 bool add_factor = true; 177 - struct device_node *node; 178 177 179 - /* The RPM clock driver will add the factor clock if present */ 180 - if (IS_ENABLED(CONFIG_QCOM_RPMCC)) { 181 - node = of_find_compatible_node(NULL, NULL, "qcom,rpmcc"); 182 - if (of_device_is_available(node)) 183 - add_factor = false; 184 - of_node_put(node); 185 - } 178 + /* 179 + * TODO: The RPM clock driver currently does not support the xo clock. 180 + * When xo is added to the RPM clock driver, we should change this 181 + * function to skip registration of xo factor clocks. 182 + */ 186 183 187 184 return _qcom_cc_register_board_clk(dev, path, name, rate, add_factor); 188 185 }
+69
include/dt-bindings/clock/qcom,rpmcc.h
··· 1 + /* 2 + * Copyright 2015 Linaro Limited 3 + * 4 + * This software is licensed under the terms of the GNU General Public 5 + * License version 2, as published by the Free Software Foundation, and 6 + * may be copied, distributed, and modified under those terms. 7 + * 8 + * This program is distributed in the hope that it will be useful, 9 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 + * GNU General Public License for more details. 12 + */ 13 + 14 + #ifndef _DT_BINDINGS_CLK_MSM_RPMCC_H 15 + #define _DT_BINDINGS_CLK_MSM_RPMCC_H 16 + 17 + /* apq8064 */ 18 + #define RPM_PXO_CLK 0 19 + #define RPM_PXO_A_CLK 1 20 + #define RPM_CXO_CLK 2 21 + #define RPM_CXO_A_CLK 3 22 + #define RPM_APPS_FABRIC_CLK 4 23 + #define RPM_APPS_FABRIC_A_CLK 5 24 + #define RPM_CFPB_CLK 6 25 + #define RPM_CFPB_A_CLK 7 26 + #define RPM_QDSS_CLK 8 27 + #define RPM_QDSS_A_CLK 9 28 + #define RPM_DAYTONA_FABRIC_CLK 10 29 + #define RPM_DAYTONA_FABRIC_A_CLK 11 30 + #define RPM_EBI1_CLK 12 31 + #define RPM_EBI1_A_CLK 13 32 + #define RPM_MM_FABRIC_CLK 14 33 + #define RPM_MM_FABRIC_A_CLK 15 34 + #define RPM_MMFPB_CLK 16 35 + #define RPM_MMFPB_A_CLK 17 36 + #define RPM_SYS_FABRIC_CLK 18 37 + #define RPM_SYS_FABRIC_A_CLK 19 38 + #define RPM_SFPB_CLK 20 39 + #define RPM_SFPB_A_CLK 21 40 + 41 + /* msm8916 */ 42 + #define RPM_SMD_XO_CLK_SRC 0 43 + #define RPM_SMD_XO_A_CLK_SRC 1 44 + #define RPM_SMD_PCNOC_CLK 2 45 + #define RPM_SMD_PCNOC_A_CLK 3 46 + #define RPM_SMD_SNOC_CLK 4 47 + #define RPM_SMD_SNOC_A_CLK 5 48 + #define RPM_SMD_BIMC_CLK 6 49 + #define RPM_SMD_BIMC_A_CLK 7 50 + #define RPM_SMD_QDSS_CLK 8 51 + #define RPM_SMD_QDSS_A_CLK 9 52 + #define RPM_SMD_BB_CLK1 10 53 + #define RPM_SMD_BB_CLK1_A 11 54 + #define RPM_SMD_BB_CLK2 12 55 + #define RPM_SMD_BB_CLK2_A 13 56 + #define RPM_SMD_RF_CLK1 14 57 + #define RPM_SMD_RF_CLK1_A 15 58 + #define RPM_SMD_RF_CLK2 16 59 + #define RPM_SMD_RF_CLK2_A 17 60 + #define RPM_SMD_BB_CLK1_PIN 18 61 + #define RPM_SMD_BB_CLK1_A_PIN 19 62 + #define RPM_SMD_BB_CLK2_PIN 20 63 + #define RPM_SMD_BB_CLK2_A_PIN 21 64 + #define RPM_SMD_RF_CLK1_PIN 22 65 + #define RPM_SMD_RF_CLK1_A_PIN 23 66 + #define RPM_SMD_RF_CLK2_PIN 24 67 + #define RPM_SMD_RF_CLK2_A_PIN 25 68 + 69 + #endif