blob: 658fa2a3bbce5a9abfc376a3bdc23d626a6eb5a1 [file] [log] [blame]
Daniel Walker5e96da52010-05-12 13:43:28 -07001/*
2 * Copyright (C) 2007 Google, Inc.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003 * Copyright (c) 2007-2011, Code Aurora Forum. 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>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070018
Daniel Walker5e96da52010-05-12 13:43:28 -070019#include <mach/clk.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070020#include <mach/socinfo.h>
Daniel Walker5e96da52010-05-12 13:43:28 -070021
22#include "proc_comm.h"
23#include "clock.h"
Stephen Boydce1c80f2011-01-26 16:20:53 -080024#include "clock-pcom.h"
Daniel Walker5e96da52010-05-12 13:43:28 -070025
26/*
27 * glue for the proc_comm interface
28 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070029static int pc_clk_enable(struct clk *clk)
Daniel Walker5e96da52010-05-12 13:43:28 -070030{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070031 int rc;
32 int id = to_pcom_clk(clk)->id;
33
34 /* Ignore clocks that are always on */
35 if (id == P_EBI1_CLK || id == P_EBI1_FIXED_CLK)
36 return 0;
37
38 rc = msm_proc_comm(PCOM_CLKCTL_RPC_ENABLE, &id, NULL);
Daniel Walker5e96da52010-05-12 13:43:28 -070039 if (rc < 0)
40 return rc;
41 else
42 return (int)id < 0 ? -EINVAL : 0;
43}
44
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070045static void pc_clk_disable(struct clk *clk)
Daniel Walker5e96da52010-05-12 13:43:28 -070046{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070047 int id = to_pcom_clk(clk)->id;
48
49 /* Ignore clocks that are always on */
50 if (id == P_EBI1_CLK || id == P_EBI1_FIXED_CLK)
51 return;
52
Daniel Walker5e96da52010-05-12 13:43:28 -070053 msm_proc_comm(PCOM_CLKCTL_RPC_DISABLE, &id, NULL);
54}
55
56int pc_clk_reset(unsigned id, enum clk_reset_action action)
57{
58 int rc;
59
60 if (action == CLK_RESET_ASSERT)
61 rc = msm_proc_comm(PCOM_CLKCTL_RPC_RESET_ASSERT, &id, NULL);
62 else
63 rc = msm_proc_comm(PCOM_CLKCTL_RPC_RESET_DEASSERT, &id, NULL);
64
65 if (rc < 0)
66 return rc;
67 else
68 return (int)id < 0 ? -EINVAL : 0;
69}
70
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070071static int pc_reset(struct clk *clk, enum clk_reset_action action)
72{
73 int id = to_pcom_clk(clk)->id;
74 return pc_clk_reset(id, action);
75}
76
Matt Wagantall77952c42011-11-08 18:45:48 -080077static int _pc_clk_set_rate(struct clk *clk, unsigned long rate)
Daniel Walker5e96da52010-05-12 13:43:28 -070078{
79 /* The rate _might_ be rounded off to the nearest KHz value by the
80 * remote function. So a return value of 0 doesn't necessarily mean
81 * that the exact rate was set successfully.
82 */
Matt Wagantall9de3bfb2011-11-03 20:13:12 -070083 unsigned r = rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070084 int id = to_pcom_clk(clk)->id;
Matt Wagantall9de3bfb2011-11-03 20:13:12 -070085 int rc = msm_proc_comm(PCOM_CLKCTL_RPC_SET_RATE, &id, &r);
Daniel Walker5e96da52010-05-12 13:43:28 -070086 if (rc < 0)
87 return rc;
88 else
89 return (int)id < 0 ? -EINVAL : 0;
90}
91
Matt Wagantall77952c42011-11-08 18:45:48 -080092static int _pc_clk_set_min_rate(struct clk *clk, unsigned long rate)
Daniel Walker5e96da52010-05-12 13:43:28 -070093{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070094 int rc;
95 int id = to_pcom_clk(clk)->id;
96 bool ignore_error = (cpu_is_msm7x27() && id == P_EBI1_CLK &&
97 rate >= INT_MAX);
Matt Wagantall9de3bfb2011-11-03 20:13:12 -070098 unsigned r = rate;
99 rc = msm_proc_comm(PCOM_CLKCTL_RPC_MIN_RATE, &id, &r);
Daniel Walker5e96da52010-05-12 13:43:28 -0700100 if (rc < 0)
101 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700102 else if (ignore_error)
103 return 0;
Daniel Walker5e96da52010-05-12 13:43:28 -0700104 else
105 return (int)id < 0 ? -EINVAL : 0;
106}
107
Matt Wagantall77952c42011-11-08 18:45:48 -0800108static int pc_clk_set_rate(struct clk *clk, unsigned long rate)
109{
110 if (clk->flags & CLKFLAG_MIN)
111 return _pc_clk_set_min_rate(clk, rate);
112 else
113 return _pc_clk_set_rate(clk, rate);
114}
115
Matt Wagantall9de3bfb2011-11-03 20:13:12 -0700116static int pc_clk_set_max_rate(struct clk *clk, unsigned long rate)
Daniel Walker5e96da52010-05-12 13:43:28 -0700117{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700118 int id = to_pcom_clk(clk)->id;
Matt Wagantall9de3bfb2011-11-03 20:13:12 -0700119 unsigned r = rate;
120 int rc = msm_proc_comm(PCOM_CLKCTL_RPC_MAX_RATE, &id, &r);
Daniel Walker5e96da52010-05-12 13:43:28 -0700121 if (rc < 0)
122 return rc;
123 else
124 return (int)id < 0 ? -EINVAL : 0;
125}
126
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700127static int pc_clk_set_flags(struct clk *clk, unsigned flags)
Daniel Walker5e96da52010-05-12 13:43:28 -0700128{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700129 int id = to_pcom_clk(clk)->id;
Daniel Walker5e96da52010-05-12 13:43:28 -0700130 int rc = msm_proc_comm(PCOM_CLKCTL_RPC_SET_FLAGS, &id, &flags);
131 if (rc < 0)
132 return rc;
133 else
134 return (int)id < 0 ? -EINVAL : 0;
135}
136
Matt Wagantall9de3bfb2011-11-03 20:13:12 -0700137static int pc_clk_set_ext_config(struct clk *clk, unsigned long config)
Daniel Walker5e96da52010-05-12 13:43:28 -0700138{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700139 int id = to_pcom_clk(clk)->id;
Matt Wagantall9de3bfb2011-11-03 20:13:12 -0700140 unsigned c = config;
141 int rc = msm_proc_comm(PCOM_CLKCTL_RPC_SET_EXT_CONFIG, &id, &c);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700142 if (rc < 0)
143 return rc;
144 else
145 return (int)id < 0 ? -EINVAL : 0;
146}
147
Matt Wagantall9de3bfb2011-11-03 20:13:12 -0700148static unsigned long pc_clk_get_rate(struct clk *clk)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700149{
150 int id = to_pcom_clk(clk)->id;
Daniel Walker5e96da52010-05-12 13:43:28 -0700151 if (msm_proc_comm(PCOM_CLKCTL_RPC_RATE, &id, NULL))
152 return 0;
153 else
154 return id;
155}
156
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700157static int pc_clk_is_enabled(struct clk *clk)
Daniel Walker5e96da52010-05-12 13:43:28 -0700158{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700159 int id = to_pcom_clk(clk)->id;
Daniel Walker5e96da52010-05-12 13:43:28 -0700160 if (msm_proc_comm(PCOM_CLKCTL_RPC_ENABLED, &id, NULL))
161 return 0;
162 else
163 return id;
164}
165
Matt Wagantall9de3bfb2011-11-03 20:13:12 -0700166static long pc_clk_round_rate(struct clk *clk, unsigned long rate)
Daniel Walker5e96da52010-05-12 13:43:28 -0700167{
168
169 /* Not really supported; pc_clk_set_rate() does rounding on it's own. */
170 return rate;
171}
172
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700173static bool pc_clk_is_local(struct clk *clk)
Stephen Boyd2a522202011-02-23 09:37:41 -0800174{
175 return false;
176}
177
Matt Wagantall30ccb702012-05-16 13:43:01 -0700178static enum handoff pc_clk_handoff(struct clk *clk)
179{
180 /*
181 * Handoff clock state only since querying and caching the rate here
182 * would incur more overhead than it would ever save.
183 */
184 if (pc_clk_is_enabled(clk))
185 return HANDOFF_ENABLED_CLK;
186
187 return HANDOFF_DISABLED_CLK;
188}
189
Daniel Walker5e96da52010-05-12 13:43:28 -0700190struct clk_ops clk_ops_pcom = {
191 .enable = pc_clk_enable,
192 .disable = pc_clk_disable,
193 .auto_off = 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,
208 .auto_off = pc_clk_disable,
209 .reset = pc_reset,
210 .set_rate = pc_clk_set_ext_config,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700211 .set_max_rate = pc_clk_set_max_rate,
212 .set_flags = pc_clk_set_flags,
213 .get_rate = pc_clk_get_rate,
214 .is_enabled = pc_clk_is_enabled,
215 .round_rate = pc_clk_round_rate,
216 .is_local = pc_clk_is_local,
Matt Wagantall30ccb702012-05-16 13:43:01 -0700217 .handoff = pc_clk_handoff,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700218};
219