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

Configure Feed

Select the types of activity you want to include in your feed.

at v5.11-rc5 318 lines 8.0 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2 3/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. 4 * Copyright (C) 2018-2020 Linaro Ltd. 5 */ 6 7#include <linux/refcount.h> 8#include <linux/mutex.h> 9#include <linux/clk.h> 10#include <linux/device.h> 11#include <linux/interconnect.h> 12 13#include "ipa.h" 14#include "ipa_clock.h" 15#include "ipa_modem.h" 16#include "ipa_data.h" 17 18/** 19 * DOC: IPA Clocking 20 * 21 * The "IPA Clock" manages both the IPA core clock and the interconnects 22 * (buses) the IPA depends on as a single logical entity. A reference count 23 * is incremented by "get" operations and decremented by "put" operations. 24 * Transitions of that count from 0 to 1 result in the clock and interconnects 25 * being enabled, and transitions of the count from 1 to 0 cause them to be 26 * disabled. We currently operate the core clock at a fixed clock rate, and 27 * all buses at a fixed average and peak bandwidth. As more advanced IPA 28 * features are enabled, we can make better use of clock and bus scaling. 29 * 30 * An IPA clock reference must be held for any access to IPA hardware. 31 */ 32 33/** 34 * struct ipa_clock - IPA clocking information 35 * @count: Clocking reference count 36 * @mutex: Protects clock enable/disable 37 * @core: IPA core clock 38 * @memory_path: Memory interconnect 39 * @imem_path: Internal memory interconnect 40 * @config_path: Configuration space interconnect 41 * @interconnect_data: Interconnect configuration data 42 */ 43struct ipa_clock { 44 refcount_t count; 45 struct mutex mutex; /* protects clock enable/disable */ 46 struct clk *core; 47 struct icc_path *memory_path; 48 struct icc_path *imem_path; 49 struct icc_path *config_path; 50 const struct ipa_interconnect_data *interconnect_data; 51}; 52 53static struct icc_path * 54ipa_interconnect_init_one(struct device *dev, const char *name) 55{ 56 struct icc_path *path; 57 58 path = of_icc_get(dev, name); 59 if (IS_ERR(path)) 60 dev_err(dev, "error %ld getting %s interconnect\n", 61 PTR_ERR(path), name); 62 63 return path; 64} 65 66/* Initialize interconnects required for IPA operation */ 67static int ipa_interconnect_init(struct ipa_clock *clock, struct device *dev) 68{ 69 struct icc_path *path; 70 71 path = ipa_interconnect_init_one(dev, "memory"); 72 if (IS_ERR(path)) 73 goto err_return; 74 clock->memory_path = path; 75 76 path = ipa_interconnect_init_one(dev, "imem"); 77 if (IS_ERR(path)) 78 goto err_memory_path_put; 79 clock->imem_path = path; 80 81 path = ipa_interconnect_init_one(dev, "config"); 82 if (IS_ERR(path)) 83 goto err_imem_path_put; 84 clock->config_path = path; 85 86 return 0; 87 88err_imem_path_put: 89 icc_put(clock->imem_path); 90err_memory_path_put: 91 icc_put(clock->memory_path); 92err_return: 93 return PTR_ERR(path); 94} 95 96/* Inverse of ipa_interconnect_init() */ 97static void ipa_interconnect_exit(struct ipa_clock *clock) 98{ 99 icc_put(clock->config_path); 100 icc_put(clock->imem_path); 101 icc_put(clock->memory_path); 102} 103 104/* Currently we only use one bandwidth level, so just "enable" interconnects */ 105static int ipa_interconnect_enable(struct ipa *ipa) 106{ 107 const struct ipa_interconnect_data *data; 108 struct ipa_clock *clock = ipa->clock; 109 int ret; 110 111 data = &clock->interconnect_data[IPA_INTERCONNECT_MEMORY]; 112 ret = icc_set_bw(clock->memory_path, data->average_rate, 113 data->peak_rate); 114 if (ret) 115 return ret; 116 117 data = &clock->interconnect_data[IPA_INTERCONNECT_IMEM]; 118 ret = icc_set_bw(clock->imem_path, data->average_rate, 119 data->peak_rate); 120 if (ret) 121 goto err_memory_path_disable; 122 123 data = &clock->interconnect_data[IPA_INTERCONNECT_CONFIG]; 124 ret = icc_set_bw(clock->config_path, data->average_rate, 125 data->peak_rate); 126 if (ret) 127 goto err_imem_path_disable; 128 129 return 0; 130 131err_imem_path_disable: 132 (void)icc_set_bw(clock->imem_path, 0, 0); 133err_memory_path_disable: 134 (void)icc_set_bw(clock->memory_path, 0, 0); 135 136 return ret; 137} 138 139/* To disable an interconnect, we just its bandwidth to 0 */ 140static int ipa_interconnect_disable(struct ipa *ipa) 141{ 142 const struct ipa_interconnect_data *data; 143 struct ipa_clock *clock = ipa->clock; 144 int ret; 145 146 ret = icc_set_bw(clock->memory_path, 0, 0); 147 if (ret) 148 return ret; 149 150 ret = icc_set_bw(clock->imem_path, 0, 0); 151 if (ret) 152 goto err_memory_path_reenable; 153 154 ret = icc_set_bw(clock->config_path, 0, 0); 155 if (ret) 156 goto err_imem_path_reenable; 157 158 return 0; 159 160err_imem_path_reenable: 161 data = &clock->interconnect_data[IPA_INTERCONNECT_IMEM]; 162 (void)icc_set_bw(clock->imem_path, data->average_rate, 163 data->peak_rate); 164err_memory_path_reenable: 165 data = &clock->interconnect_data[IPA_INTERCONNECT_MEMORY]; 166 (void)icc_set_bw(clock->memory_path, data->average_rate, 167 data->peak_rate); 168 169 return ret; 170} 171 172/* Turn on IPA clocks, including interconnects */ 173static int ipa_clock_enable(struct ipa *ipa) 174{ 175 int ret; 176 177 ret = ipa_interconnect_enable(ipa); 178 if (ret) 179 return ret; 180 181 ret = clk_prepare_enable(ipa->clock->core); 182 if (ret) 183 ipa_interconnect_disable(ipa); 184 185 return ret; 186} 187 188/* Inverse of ipa_clock_enable() */ 189static void ipa_clock_disable(struct ipa *ipa) 190{ 191 clk_disable_unprepare(ipa->clock->core); 192 (void)ipa_interconnect_disable(ipa); 193} 194 195/* Get an IPA clock reference, but only if the reference count is 196 * already non-zero. Returns true if the additional reference was 197 * added successfully, or false otherwise. 198 */ 199bool ipa_clock_get_additional(struct ipa *ipa) 200{ 201 return refcount_inc_not_zero(&ipa->clock->count); 202} 203 204/* Get an IPA clock reference. If the reference count is non-zero, it is 205 * incremented and return is immediate. Otherwise it is checked again 206 * under protection of the mutex, and if appropriate the IPA clock 207 * is enabled. 208 * 209 * Incrementing the reference count is intentionally deferred until 210 * after the clock is running and endpoints are resumed. 211 */ 212void ipa_clock_get(struct ipa *ipa) 213{ 214 struct ipa_clock *clock = ipa->clock; 215 int ret; 216 217 /* If the clock is running, just bump the reference count */ 218 if (ipa_clock_get_additional(ipa)) 219 return; 220 221 /* Otherwise get the mutex and check again */ 222 mutex_lock(&clock->mutex); 223 224 /* A reference might have been added before we got the mutex. */ 225 if (ipa_clock_get_additional(ipa)) 226 goto out_mutex_unlock; 227 228 ret = ipa_clock_enable(ipa); 229 if (ret) { 230 dev_err(&ipa->pdev->dev, "error %d enabling IPA clock\n", ret); 231 goto out_mutex_unlock; 232 } 233 234 refcount_set(&clock->count, 1); 235 236out_mutex_unlock: 237 mutex_unlock(&clock->mutex); 238} 239 240/* Attempt to remove an IPA clock reference. If this represents the 241 * last reference, disable the IPA clock under protection of the mutex. 242 */ 243void ipa_clock_put(struct ipa *ipa) 244{ 245 struct ipa_clock *clock = ipa->clock; 246 247 /* If this is not the last reference there's nothing more to do */ 248 if (!refcount_dec_and_mutex_lock(&clock->count, &clock->mutex)) 249 return; 250 251 ipa_clock_disable(ipa); 252 253 mutex_unlock(&clock->mutex); 254} 255 256/* Return the current IPA core clock rate */ 257u32 ipa_clock_rate(struct ipa *ipa) 258{ 259 return ipa->clock ? (u32)clk_get_rate(ipa->clock->core) : 0; 260} 261 262/* Initialize IPA clocking */ 263struct ipa_clock * 264ipa_clock_init(struct device *dev, const struct ipa_clock_data *data) 265{ 266 struct ipa_clock *clock; 267 struct clk *clk; 268 int ret; 269 270 clk = clk_get(dev, "core"); 271 if (IS_ERR(clk)) { 272 dev_err(dev, "error %ld getting core clock\n", PTR_ERR(clk)); 273 return ERR_CAST(clk); 274 } 275 276 ret = clk_set_rate(clk, data->core_clock_rate); 277 if (ret) { 278 dev_err(dev, "error %d setting core clock rate to %u\n", 279 ret, data->core_clock_rate); 280 goto err_clk_put; 281 } 282 283 clock = kzalloc(sizeof(*clock), GFP_KERNEL); 284 if (!clock) { 285 ret = -ENOMEM; 286 goto err_clk_put; 287 } 288 clock->core = clk; 289 clock->interconnect_data = data->interconnect; 290 291 ret = ipa_interconnect_init(clock, dev); 292 if (ret) 293 goto err_kfree; 294 295 mutex_init(&clock->mutex); 296 refcount_set(&clock->count, 0); 297 298 return clock; 299 300err_kfree: 301 kfree(clock); 302err_clk_put: 303 clk_put(clk); 304 305 return ERR_PTR(ret); 306} 307 308/* Inverse of ipa_clock_init() */ 309void ipa_clock_exit(struct ipa_clock *clock) 310{ 311 struct clk *clk = clock->core; 312 313 WARN_ON(refcount_read(&clock->count) != 0); 314 mutex_destroy(&clock->mutex); 315 ipa_interconnect_exit(clock); 316 kfree(clock); 317 clk_put(clk); 318}