blob: 72bb0b4e5ca631e1bba4fe2b7a625d0ea81e30d6 [file] [log] [blame]
Daniel Walker5e96da52010-05-12 13:43:28 -07001/*
2 * Copyright (C) 2007 Google, Inc.
Duy Truong790f06d2013-02-13 16:38:12 -08003 * Copyright (c) 2007-2011, The Linux Foundation. All rights reserved.
Daniel Walker5e96da52010-05-12 13:43:28 -07004 *
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
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070016#include <linux/kernel.h>
Daniel Walker5e96da52010-05-12 13:43:28 -070017#include <linux/err.h>
Daniel Walker5e96da52010-05-12 13:43:28 -070018
Daniel Walker5e96da52010-05-12 13:43:28 -070019#include <mach/clk.h>
Matt Wagantall33d01f52012-02-23 23:27:44 -080020#include <mach/clk-provider.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070021#include <mach/socinfo.h>
Steve Mucklef132c6c2012-06-06 18:30:57 -070022#include <mach/proc_comm.h>
Daniel Walker5e96da52010-05-12 13:43:28 -070023
Daniel Walker5e96da52010-05-12 13:43:28 -070024#include "clock.h"
Stephen Boydce1c80f2011-01-26 16:20:53 -080025#include "clock-pcom.h"
Daniel Walker5e96da52010-05-12 13:43:28 -070026
27/*
28 * glue for the proc_comm interface
29 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070030static int pc_clk_enable(struct clk *clk)
Daniel Walker5e96da52010-05-12 13:43:28 -070031{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070032 int rc;
33 int id = to_pcom_clk(clk)->id;
34
35 /* Ignore clocks that are always on */
36 if (id == P_EBI1_CLK || id == P_EBI1_FIXED_CLK)
37 return 0;
38
39 rc = msm_proc_comm(PCOM_CLKCTL_RPC_ENABLE, &id, NULL);
Daniel Walker5e96da52010-05-12 13:43:28 -070040 if (rc < 0)
41 return rc;
42 else
43 return (int)id < 0 ? -EINVAL : 0;
44}
45
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070046static void pc_clk_disable(struct clk *clk)
Daniel Walker5e96da52010-05-12 13:43:28 -070047{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070048 int id = to_pcom_clk(clk)->id;
49
50 /* Ignore clocks that are always on */
51 if (id == P_EBI1_CLK || id == P_EBI1_FIXED_CLK)
52 return;
53
Daniel Walker5e96da52010-05-12 13:43:28 -070054 msm_proc_comm(PCOM_CLKCTL_RPC_DISABLE, &id, NULL);
55}
56
57int pc_clk_reset(unsigned id, enum clk_reset_action action)
58{
59 int rc;
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
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070072static int pc_reset(struct clk *clk, enum clk_reset_action action)
73{
74 int id = to_pcom_clk(clk)->id;
75 return pc_clk_reset(id, action);
76}
77
Matt Wagantall77952c42011-11-08 18:45:48 -080078static int _pc_clk_set_rate(struct clk *clk, unsigned long rate)
Daniel Walker5e96da52010-05-12 13:43:28 -070079{
80 /* The rate _might_ be rounded off to the nearest KHz value by the
81 * remote function. So a return value of 0 doesn't necessarily mean
82 * that the exact rate was set successfully.
83 */
Matt Wagantall9de3bfb2011-11-03 20:13:12 -070084 unsigned r = rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070085 int id = to_pcom_clk(clk)->id;
Matt Wagantall9de3bfb2011-11-03 20:13:12 -070086 int rc = msm_proc_comm(PCOM_CLKCTL_RPC_SET_RATE, &id, &r);
Daniel Walker5e96da52010-05-12 13:43:28 -070087 if (rc < 0)
88 return rc;
89 else
90 return (int)id < 0 ? -EINVAL : 0;
91}
92
Matt Wagantall77952c42011-11-08 18:45:48 -080093static int _pc_clk_set_min_rate(struct clk *clk, unsigned long rate)
Daniel Walker5e96da52010-05-12 13:43:28 -070094{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070095 int rc;
96 int id = to_pcom_clk(clk)->id;
97 bool ignore_error = (cpu_is_msm7x27() && id == P_EBI1_CLK &&
98 rate >= INT_MAX);
Matt Wagantall9de3bfb2011-11-03 20:13:12 -070099 unsigned r = rate;
100 rc = msm_proc_comm(PCOM_CLKCTL_RPC_MIN_RATE, &id, &r);
Daniel Walker5e96da52010-05-12 13:43:28 -0700101 if (rc < 0)
102 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700103 else if (ignore_error)
104 return 0;
Daniel Walker5e96da52010-05-12 13:43:28 -0700105 else
106 return (int)id < 0 ? -EINVAL : 0;
107}
108
Matt Wagantall77952c42011-11-08 18:45:48 -0800109static int pc_clk_set_rate(struct clk *clk, unsigned long rate)
110{
111 if (clk->flags & CLKFLAG_MIN)
112 return _pc_clk_set_min_rate(clk, rate);
113 else
114 return _pc_clk_set_rate(clk, rate);
115}
116
Matt Wagantall9de3bfb2011-11-03 20:13:12 -0700117static int pc_clk_set_max_rate(struct clk *clk, unsigned long rate)
Daniel Walker5e96da52010-05-12 13:43:28 -0700118{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700119 int id = to_pcom_clk(clk)->id;
Matt Wagantall9de3bfb2011-11-03 20:13:12 -0700120 unsigned r = rate;
121 int rc = msm_proc_comm(PCOM_CLKCTL_RPC_MAX_RATE, &id, &r);
Daniel Walker5e96da52010-05-12 13:43:28 -0700122 if (rc < 0)
123 return rc;
124 else
125 return (int)id < 0 ? -EINVAL : 0;
126}
127
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700128static int pc_clk_set_flags(struct clk *clk, unsigned flags)
Daniel Walker5e96da52010-05-12 13:43:28 -0700129{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700130 int id = to_pcom_clk(clk)->id;
Daniel Walker5e96da52010-05-12 13:43:28 -0700131 int rc = msm_proc_comm(PCOM_CLKCTL_RPC_SET_FLAGS, &id, &flags);
132 if (rc < 0)
133 return rc;
134 else
135 return (int)id < 0 ? -EINVAL : 0;
136}
137
Matt Wagantall9de3bfb2011-11-03 20:13:12 -0700138static int pc_clk_set_ext_config(struct clk *clk, unsigned long config)
Daniel Walker5e96da52010-05-12 13:43:28 -0700139{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700140 int id = to_pcom_clk(clk)->id;
Matt Wagantall9de3bfb2011-11-03 20:13:12 -0700141 unsigned c = config;
142 int rc = msm_proc_comm(PCOM_CLKCTL_RPC_SET_EXT_CONFIG, &id, &c);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700143 if (rc < 0)
144 return rc;
145 else
146 return (int)id < 0 ? -EINVAL : 0;
147}
148
Matt Wagantall9de3bfb2011-11-03 20:13:12 -0700149static unsigned long pc_clk_get_rate(struct clk *clk)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700150{
151 int id = to_pcom_clk(clk)->id;
Daniel Walker5e96da52010-05-12 13:43:28 -0700152 if (msm_proc_comm(PCOM_CLKCTL_RPC_RATE, &id, NULL))
153 return 0;
154 else
155 return id;
156}
157
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700158static int pc_clk_is_enabled(struct clk *clk)
Daniel Walker5e96da52010-05-12 13:43:28 -0700159{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700160 int id = to_pcom_clk(clk)->id;
Daniel Walker5e96da52010-05-12 13:43:28 -0700161 if (msm_proc_comm(PCOM_CLKCTL_RPC_ENABLED, &id, NULL))
162 return 0;
163 else
164 return id;
165}
166
Matt Wagantall9de3bfb2011-11-03 20:13:12 -0700167static long pc_clk_round_rate(struct clk *clk, unsigned long rate)
Daniel Walker5e96da52010-05-12 13:43:28 -0700168{
169
170 /* Not really supported; pc_clk_set_rate() does rounding on it's own. */
171 return rate;
172}
173
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700174static bool pc_clk_is_local(struct clk *clk)
Stephen Boyd2a522202011-02-23 09:37:41 -0800175{
176 return false;
177}
178
Matt Wagantall30ccb702012-05-16 13:43:01 -0700179static enum handoff pc_clk_handoff(struct clk *clk)
180{
181 /*
182 * Handoff clock state only since querying and caching the rate here
183 * would incur more overhead than it would ever save.
184 */
185 if (pc_clk_is_enabled(clk))
186 return HANDOFF_ENABLED_CLK;
187
188 return HANDOFF_DISABLED_CLK;
189}
190
Daniel Walker5e96da52010-05-12 13:43:28 -0700191struct clk_ops clk_ops_pcom = {
192 .enable = pc_clk_enable,
193 .disable = pc_clk_disable,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700194 .reset = pc_reset,
Daniel Walker5e96da52010-05-12 13:43:28 -0700195 .set_rate = pc_clk_set_rate,
Daniel Walker5e96da52010-05-12 13:43:28 -0700196 .set_max_rate = pc_clk_set_max_rate,
197 .set_flags = pc_clk_set_flags,
198 .get_rate = pc_clk_get_rate,
199 .is_enabled = pc_clk_is_enabled,
200 .round_rate = pc_clk_round_rate,
Stephen Boyd2a522202011-02-23 09:37:41 -0800201 .is_local = pc_clk_is_local,
Matt Wagantall30ccb702012-05-16 13:43:01 -0700202 .handoff = pc_clk_handoff,
Daniel Walker5e96da52010-05-12 13:43:28 -0700203};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700204
205struct clk_ops clk_ops_pcom_ext_config = {
206 .enable = pc_clk_enable,
207 .disable = pc_clk_disable,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700208 .reset = pc_reset,
209 .set_rate = pc_clk_set_ext_config,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700210 .set_max_rate = pc_clk_set_max_rate,
211 .set_flags = pc_clk_set_flags,
212 .get_rate = pc_clk_get_rate,
213 .is_enabled = pc_clk_is_enabled,
214 .round_rate = pc_clk_round_rate,
215 .is_local = pc_clk_is_local,
Matt Wagantall30ccb702012-05-16 13:43:01 -0700216 .handoff = pc_clk_handoff,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700217};
218