diff options
author | Linus Walleij <linus.walleij@linaro.org> | 2016-08-18 14:40:45 -0400 |
---|---|---|
committer | Lee Jones <lee.jones@linaro.org> | 2016-10-04 10:48:02 -0400 |
commit | 3526403353c2a1b94c3181f900582626d23c339b (patch) | |
tree | 3c784f6e23188b014d6138c867fc3a6d5ad050d4 | |
parent | 8c5d0571596efa5656cc53144172baa7c5c57b43 (diff) |
mfd: qcom_rpm: Handle message RAM clock
The MSM8660, APQ8060, IPQ806x and MSM8960 have a GCC clock
to the message RAM used by the RPM. This needs to be enabled
for messages to pass through. This is a crude solution that
simply prepare/enable at probe() and disable/unprepare
at remove(). More elaborate PM is probably possible to
add later.
The construction uses IS_ERR() to gracefully handle the
platforms that do not provide a message RAM clock. It will
bail out of probe only if the clock is hitting a probe
deferral situation.
Of course this requires the proper device tree set-up:
rpm: rpm@104000 {
compatible = "qcom,rpm-msm8660";
clocks = <&gcc RPM_MSG_RAM_H_CLK>;
clock-names = "ram";
...
};
I have provided this in the MSM8660 device tree, and will
provide patches for the other targets.
Cc: Björn Andersson <bjorn.andersson@linaro.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
-rw-r--r-- | drivers/mfd/qcom_rpm.c | 20 |
1 files changed, 20 insertions, 0 deletions
diff --git a/drivers/mfd/qcom_rpm.c b/drivers/mfd/qcom_rpm.c index d3300714c27b..52fafea06067 100644 --- a/drivers/mfd/qcom_rpm.c +++ b/drivers/mfd/qcom_rpm.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/mfd/qcom_rpm.h> | 21 | #include <linux/mfd/qcom_rpm.h> |
22 | #include <linux/mfd/syscon.h> | 22 | #include <linux/mfd/syscon.h> |
23 | #include <linux/regmap.h> | 23 | #include <linux/regmap.h> |
24 | #include <linux/clk.h> | ||
24 | 25 | ||
25 | #include <dt-bindings/mfd/qcom-rpm.h> | 26 | #include <dt-bindings/mfd/qcom-rpm.h> |
26 | 27 | ||
@@ -48,6 +49,7 @@ struct qcom_rpm { | |||
48 | struct regmap *ipc_regmap; | 49 | struct regmap *ipc_regmap; |
49 | unsigned ipc_offset; | 50 | unsigned ipc_offset; |
50 | unsigned ipc_bit; | 51 | unsigned ipc_bit; |
52 | struct clk *ramclk; | ||
51 | 53 | ||
52 | struct completion ack; | 54 | struct completion ack; |
53 | struct mutex lock; | 55 | struct mutex lock; |
@@ -552,6 +554,20 @@ static int qcom_rpm_probe(struct platform_device *pdev) | |||
552 | mutex_init(&rpm->lock); | 554 | mutex_init(&rpm->lock); |
553 | init_completion(&rpm->ack); | 555 | init_completion(&rpm->ack); |
554 | 556 | ||
557 | /* Enable message RAM clock */ | ||
558 | rpm->ramclk = devm_clk_get(&pdev->dev, "ram"); | ||
559 | if (IS_ERR(rpm->ramclk)) { | ||
560 | ret = PTR_ERR(rpm->ramclk); | ||
561 | if (ret == -EPROBE_DEFER) | ||
562 | return ret; | ||
563 | /* | ||
564 | * Fall through in all other cases, as the clock is | ||
565 | * optional. (Does not exist on all platforms.) | ||
566 | */ | ||
567 | rpm->ramclk = NULL; | ||
568 | } | ||
569 | clk_prepare_enable(rpm->ramclk); /* Accepts NULL */ | ||
570 | |||
555 | irq_ack = platform_get_irq_byname(pdev, "ack"); | 571 | irq_ack = platform_get_irq_byname(pdev, "ack"); |
556 | if (irq_ack < 0) { | 572 | if (irq_ack < 0) { |
557 | dev_err(&pdev->dev, "required ack interrupt missing\n"); | 573 | dev_err(&pdev->dev, "required ack interrupt missing\n"); |
@@ -672,7 +688,11 @@ static int qcom_rpm_probe(struct platform_device *pdev) | |||
672 | 688 | ||
673 | static int qcom_rpm_remove(struct platform_device *pdev) | 689 | static int qcom_rpm_remove(struct platform_device *pdev) |
674 | { | 690 | { |
691 | struct qcom_rpm *rpm = dev_get_drvdata(&pdev->dev); | ||
692 | |||
675 | of_platform_depopulate(&pdev->dev); | 693 | of_platform_depopulate(&pdev->dev); |
694 | clk_disable_unprepare(rpm->ramclk); | ||
695 | |||
676 | return 0; | 696 | return 0; |
677 | } | 697 | } |
678 | 698 | ||