Commit | Line | Data |
---|---|---|
5e96da5d DW |
1 | /* |
2 | * Copyright (C) 2007 Google, Inc. | |
8cc7f533 | 3 | * Copyright (c) 2007-2012, The Linux Foundation. All rights reserved. |
5e96da5d DW |
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 | ||
8cc7f533 | 16 | #include <linux/kernel.h> |
5e96da5d | 17 | #include <linux/err.h> |
421faca0 SB |
18 | #include <linux/platform_device.h> |
19 | #include <linux/module.h> | |
8cc7f533 SB |
20 | #include <linux/clk-provider.h> |
21 | #include <linux/clkdev.h> | |
421faca0 | 22 | |
5e96da5d DW |
23 | #include <mach/clk.h> |
24 | ||
25 | #include "proc_comm.h" | |
26 | #include "clock.h" | |
ce1c80fb | 27 | #include "clock-pcom.h" |
5e96da5d | 28 | |
8cc7f533 SB |
29 | struct clk_pcom { |
30 | unsigned id; | |
31 | unsigned long flags; | |
32 | struct msm_clk msm_clk; | |
33 | }; | |
34 | ||
35 | static inline struct clk_pcom *to_clk_pcom(struct clk_hw *hw) | |
5e96da5d | 36 | { |
8cc7f533 SB |
37 | return container_of(to_msm_clk(hw), struct clk_pcom, msm_clk); |
38 | } | |
39 | ||
40 | static int pc_clk_enable(struct clk_hw *hw) | |
41 | { | |
42 | unsigned id = to_clk_pcom(hw)->id; | |
5e96da5d DW |
43 | int rc = msm_proc_comm(PCOM_CLKCTL_RPC_ENABLE, &id, NULL); |
44 | if (rc < 0) | |
45 | return rc; | |
46 | else | |
47 | return (int)id < 0 ? -EINVAL : 0; | |
48 | } | |
49 | ||
8cc7f533 | 50 | static void pc_clk_disable(struct clk_hw *hw) |
5e96da5d | 51 | { |
8cc7f533 | 52 | unsigned id = to_clk_pcom(hw)->id; |
5e96da5d DW |
53 | msm_proc_comm(PCOM_CLKCTL_RPC_DISABLE, &id, NULL); |
54 | } | |
55 | ||
8cc7f533 | 56 | static int pc_clk_reset(struct clk_hw *hw, enum clk_reset_action action) |
5e96da5d DW |
57 | { |
58 | int rc; | |
8cc7f533 | 59 | unsigned id = to_clk_pcom(hw)->id; |
5e96da5d DW |
60 | |
61 | if (action == CLK_RESET_ASSERT) | |
62 | rc = msm_proc_comm(PCOM_CLKCTL_RPC_RESET_ASSERT, &id, NULL); | |
63 | else | |
64 | rc = msm_proc_comm(PCOM_CLKCTL_RPC_RESET_DEASSERT, &id, NULL); | |
65 | ||
66 | if (rc < 0) | |
67 | return rc; | |
68 | else | |
69 | return (int)id < 0 ? -EINVAL : 0; | |
70 | } | |
71 | ||
8cc7f533 SB |
72 | static int pc_clk_set_rate(struct clk_hw *hw, unsigned long new_rate, |
73 | unsigned long p_rate) | |
5e96da5d | 74 | { |
8cc7f533 SB |
75 | struct clk_pcom *p = to_clk_pcom(hw); |
76 | unsigned id = p->id, rate = new_rate; | |
77 | int rc; | |
78 | ||
79 | /* | |
80 | * The rate _might_ be rounded off to the nearest KHz value by the | |
5e96da5d DW |
81 | * remote function. So a return value of 0 doesn't necessarily mean |
82 | * that the exact rate was set successfully. | |
83 | */ | |
8cc7f533 SB |
84 | if (p->flags & CLKFLAG_MIN) |
85 | rc = msm_proc_comm(PCOM_CLKCTL_RPC_MIN_RATE, &id, &rate); | |
5e96da5d | 86 | else |
8cc7f533 | 87 | rc = msm_proc_comm(PCOM_CLKCTL_RPC_SET_RATE, &id, &rate); |
5e96da5d DW |
88 | if (rc < 0) |
89 | return rc; | |
90 | else | |
91 | return (int)id < 0 ? -EINVAL : 0; | |
92 | } | |
93 | ||
8cc7f533 | 94 | static unsigned long pc_clk_recalc_rate(struct clk_hw *hw, unsigned long p_rate) |
5e96da5d | 95 | { |
8cc7f533 | 96 | unsigned id = to_clk_pcom(hw)->id; |
5e96da5d DW |
97 | if (msm_proc_comm(PCOM_CLKCTL_RPC_RATE, &id, NULL)) |
98 | return 0; | |
99 | else | |
100 | return id; | |
101 | } | |
102 | ||
8cc7f533 | 103 | static int pc_clk_is_enabled(struct clk_hw *hw) |
5e96da5d | 104 | { |
8cc7f533 | 105 | unsigned id = to_clk_pcom(hw)->id; |
5e96da5d DW |
106 | if (msm_proc_comm(PCOM_CLKCTL_RPC_ENABLED, &id, NULL)) |
107 | return 0; | |
108 | else | |
109 | return id; | |
110 | } | |
111 | ||
8cc7f533 SB |
112 | static long pc_clk_round_rate(struct clk_hw *hw, unsigned long rate, |
113 | unsigned long *p_rate) | |
5e96da5d | 114 | { |
5e96da5d DW |
115 | /* Not really supported; pc_clk_set_rate() does rounding on it's own. */ |
116 | return rate; | |
117 | } | |
118 | ||
8cc7f533 | 119 | static struct clk_ops clk_ops_pcom = { |
5e96da5d DW |
120 | .enable = pc_clk_enable, |
121 | .disable = pc_clk_disable, | |
5e96da5d | 122 | .set_rate = pc_clk_set_rate, |
8cc7f533 | 123 | .recalc_rate = pc_clk_recalc_rate, |
5e96da5d DW |
124 | .is_enabled = pc_clk_is_enabled, |
125 | .round_rate = pc_clk_round_rate, | |
126 | }; | |
421faca0 SB |
127 | |
128 | static int msm_clock_pcom_probe(struct platform_device *pdev) | |
129 | { | |
130 | const struct pcom_clk_pdata *pdata = pdev->dev.platform_data; | |
8cc7f533 SB |
131 | int i, ret; |
132 | ||
133 | for (i = 0; i < pdata->num_lookups; i++) { | |
134 | const struct clk_pcom_desc *desc = &pdata->lookup[i]; | |
135 | struct clk *c; | |
136 | struct clk_pcom *p; | |
137 | struct clk_hw *hw; | |
138 | struct clk_init_data init; | |
139 | ||
140 | p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); | |
141 | if (!p) | |
142 | return -ENOMEM; | |
143 | ||
144 | p->id = desc->id; | |
145 | p->flags = desc->flags; | |
146 | p->msm_clk.reset = pc_clk_reset; | |
147 | ||
148 | hw = &p->msm_clk.hw; | |
149 | hw->init = &init; | |
150 | ||
151 | init.name = desc->name; | |
152 | init.ops = &clk_ops_pcom; | |
153 | init.num_parents = 0; | |
154 | init.flags = CLK_IS_ROOT; | |
155 | ||
156 | if (!(p->flags & CLKFLAG_AUTO_OFF)) | |
157 | init.flags |= CLK_IGNORE_UNUSED; | |
158 | ||
159 | c = devm_clk_register(&pdev->dev, hw); | |
160 | ret = clk_register_clkdev(c, desc->con, desc->dev); | |
161 | if (ret) | |
162 | return ret; | |
163 | } | |
164 | ||
421faca0 SB |
165 | return 0; |
166 | } | |
167 | ||
168 | static struct platform_driver msm_clock_pcom_driver = { | |
169 | .probe = msm_clock_pcom_probe, | |
170 | .driver = { | |
171 | .name = "msm-clock-pcom", | |
172 | .owner = THIS_MODULE, | |
173 | }, | |
174 | }; | |
175 | module_platform_driver(msm_clock_pcom_driver); | |
176 | ||
177 | MODULE_LICENSE("GPL v2"); |