1/* 2 ************************************************************************** 3 * Copyright (c) 2014,2015, The Linux Foundation. All rights reserved. 4 * Permission to use, copy, modify, and/or distribute this software for 5 * any purpose with or without fee is hereby granted, provided that the 6 * above copyright notice and this permission notice appear in all copies. 7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 13 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 ************************************************************************** 15 */ 16 17/* 18 * nss_pm.c 19 * NSS Power Management APIs 20 * 21 */ 22#include <linux/debugfs.h> 23#include <linux/module.h> 24#include <linux/netdevice.h> 25#include <nss_hal.h> 26#include <nss_api_if.h> 27 28#if (NSS_PM_SUPPORT == 1) 29#include "nss_pm.h" 30 31/* 32 * Global NSS PM structure 33 */ 34struct nss_pm_global_ctx ctx; 35 36/* 37 * Bus vector table for GMAC driver 38 */ 39static struct msm_bus_paths nss_gmac_bw_level_tbl[NSS_PM_PERF_MAX_LEVELS] = { 40 [NSS_PM_PERF_LEVEL_SUSPEND] = GMAC_BW_MBPS(0, 0), 41 /* 0 MHz to DDR, 0 MHz to TCM */ 42 [NSS_PM_PERF_LEVEL_IDLE] = GMAC_BW_MBPS(133, 5), 43 /* 133 MHz to DDR, 5 MHz to TCM */ 44 [NSS_PM_PERF_LEVEL_NOMINAL] = GMAC_BW_MBPS(200, 400), 45 /* 200 MHz to DDR, 10 MHz to TCM */ 46 [NSS_PM_PERF_LEVEL_TURBO] = GMAC_BW_MBPS(266, 533), 47 /* 266 MHz to DDR, 20 MHz to TCM */ 48}; 49 50/* 51 * Bus vector table for Crypto driver 52 */ 53static struct msm_bus_paths nss_crypto_bw_level_tbl[NSS_PM_PERF_MAX_LEVELS] = { 54 [NSS_PM_PERF_LEVEL_SUSPEND] = CRYPTO_BW_MBPS(0, 0), 55 /* 0 MHz to DDR, 0 MHz to TCM */ 56 [NSS_PM_PERF_LEVEL_IDLE] = CRYPTO_BW_MBPS(133, 5), 57 /* 133 MHz to DDR, 5 MHz to TCM */ 58 [NSS_PM_PERF_LEVEL_NOMINAL] = CRYPTO_BW_MBPS(200, 400), 59 /* 200 MHz to DDR, 10 MHz to TCM */ 60 [NSS_PM_PERF_LEVEL_TURBO] = CRYPTO_BW_MBPS(266, 533), 61 /* 266 MHz to DDR, 20 MHz to TCM */ 62}; 63 64#ifdef NSS_PM_NETAP_GMAC_SCALING 65 66/* 67 * Bus vector table for NSS HLOS driver 68 * This requests bw for both NSS Fab0 and Fab1 on behalf of GMAC and NSS Drivers 69 */ 70static struct msm_bus_paths nss_netap_bw_level_tbl[NSS_PM_PERF_MAX_LEVELS] = { 71 [NSS_PM_PERF_LEVEL_SUSPEND] = GMAC_BW_MBPS(0, 0), 72 /* 0 MHz to DDR, 0 MHz to TCM */ 73 [NSS_PM_PERF_LEVEL_IDLE] = GMAC_BW_MBPS(133, 5), 74 /* 133 MHz to DDR, 5 MHz to TCM */ 75 [NSS_PM_PERF_LEVEL_NOMINAL] = GMAC_BW_MBPS(200, 400), 76 /* 200 MHz to DDR, 10 MHz to TCM */ 77 [NSS_PM_PERF_LEVEL_TURBO] = GMAC_BW_MBPS(266, 533), 78 /* 266 MHz to DDR, 20 MHz to TCM */ 79}; 80 81#else 82 83/* 84 * Bus vector table for NSS HLOS driver 85 */ 86static struct msm_bus_paths nss_netap_bw_level_tbl[NSS_PM_PERF_MAX_LEVELS] = { 87 [NSS_PM_PERF_LEVEL_SUSPEND] = NETAP_BW_MBPS(0, 0), 88 /* 0 MHz to DDR, 0 MHz to TCM */ 89 [NSS_PM_PERF_LEVEL_IDLE] = NETAP_BW_MBPS(133, 133), 90 /* 133 MHz to DDR and TCM */ 91 [NSS_PM_PERF_LEVEL_NOMINAL] = NETAP_BW_MBPS(400, 400), 92 /* 400 MHz to DDR and TCM */ 93 [NSS_PM_PERF_LEVEL_TURBO] = NETAP_BW_MBPS(533, 533), 94 /* 533 MHz to DDR and TCM */ 95}; 96 97#endif 98 99/* 100 * Bus Driver Platform data for GMAC, Crypto and Netap clients 101 */ 102static struct msm_bus_scale_pdata nss_bus_scale[] = { 103 [NSS_PM_CLIENT_GMAC] = { 104 .usecase = nss_gmac_bw_level_tbl, 105 .num_usecases = ARRAY_SIZE(nss_gmac_bw_level_tbl), 106 .active_only = 1, 107 .name = "qca-nss-gmac", 108 }, 109 110 [NSS_PM_CLIENT_CRYPTO] = { 111 .usecase = nss_crypto_bw_level_tbl, 112 .num_usecases = ARRAY_SIZE(nss_crypto_bw_level_tbl), 113 .active_only = 1, 114 .name = "qca-nss-crypto", 115 }, 116 117 [NSS_PM_CLIENT_NETAP] = { 118 .usecase = nss_netap_bw_level_tbl, 119 .num_usecases = ARRAY_SIZE(nss_netap_bw_level_tbl), 120 .active_only = 1, 121 .name = "qca-nss-drv", 122 }, 123}; 124 125/* 126 * nss_pm_dbg_perf_level_get 127 * debugfs hook to get the current performance level 128 */ 129static int nss_pm_dbg_perf_level_get(void *data, u64 *val) 130{ 131 nss_pm_client_data_t *pm_client; 132 133 pm_client = (nss_pm_client_data_t *)data; 134 *val = pm_client->current_perf_lvl; 135 136 return NSS_PM_API_SUCCESS; 137} 138 139/* 140 * nss_pm_dbg_autoscale_get 141 * debugfs hook to get the current autoscale setting 142 */ 143static int nss_pm_dbg_autoscale_get(void *data, u64 *val) 144{ 145 nss_pm_client_data_t *pm_client; 146 147 pm_client = (nss_pm_client_data_t *)data; 148 *val = pm_client->auto_scale; 149 150 return NSS_PM_API_SUCCESS; 151} 152 153/* 154 * nss_pm_dbg_perf_level_set 155 * debugfs hook to set perf level for a client 156 */ 157static int nss_pm_dbg_perf_level_set(void *data, u64 val) 158{ 159 uint32_t perf_level; 160 161 perf_level = (uint32_t) val; 162 163 if (perf_level >= NSS_PM_PERF_MAX_LEVELS || 164 perf_level < NSS_PM_PERF_LEVEL_IDLE) { 165 nss_pm_warning("unsupported performance level %d \n", perf_level); 166 return NSS_PM_API_FAILED; 167 } 168 169 nss_pm_set_perf_level(data, perf_level); 170 return NSS_PM_API_SUCCESS; 171} 172 173/* 174 * nss_pm_dbg_autoscale_set 175 * debugfs hook to enable auto scaling for a client 176 */ 177static int nss_pm_dbg_autoscale_set(void *data, u64 val) 178{ 179 nss_pm_client_data_t *pm_client; 180 181 if (val > 1) { 182 nss_pm_warning(" Invalid set value, valid values are 0/1 \n"); 183 return NSS_PM_API_FAILED; 184 } 185 186 pm_client->auto_scale = (uint32_t)val; 187 return NSS_PM_API_SUCCESS; 188} 189 190DEFINE_SIMPLE_ATTRIBUTE(perf_level_fops, nss_pm_dbg_perf_level_get, nss_pm_dbg_perf_level_set, "%llu\n"); 191 192DEFINE_SIMPLE_ATTRIBUTE(autoscale_fops, nss_pm_dbg_autoscale_get, nss_pm_dbg_autoscale_set, "%llu\n"); 193#endif /** (NSS_PM_SUPPORT == 1) */ 194 195/* 196 * nss_pm_client_register 197 * Initialize GMAC specific PM parameters 198 * 199 * Creates debugfs hooks for user-space control of NSS Client PM 200 * Initializes Bus BW to Idle Perf level 201 * Returns PM handle to the caller. 202 * 203 */ 204void *nss_pm_client_register(nss_pm_client_t client_id) 205{ 206#if (NSS_PM_SUPPORT == 1) 207 int ret; 208 struct dentry *pm_dentry; 209 nss_pm_client_data_t *pm_client; 210 211 if (unlikely(client_id >= NSS_PM_MAX_CLIENTS)) { 212 nss_pm_warning("nss_pm_client_register invalid client id %d \n", client_id); 213 goto error; 214 } 215 216 pm_client = &ctx.nss_pm_client[client_id]; 217 218 pm_client->bus_perf_client = msm_bus_scale_register_client(&nss_bus_scale[client_id]); 219 if (!pm_client->bus_perf_client) { 220 nss_pm_warning("unable to register bus client \n"); 221 goto error; 222 } 223 224 ret = msm_bus_scale_client_update_request(pm_client->bus_perf_client, NSS_PM_PERF_LEVEL_IDLE); 225 if (ret) { 226 nss_pm_warning("initial bandwidth req failed (%d)\n", ret); 227 msm_bus_scale_unregister_client((uint32_t) pm_client->bus_perf_client); 228 goto error; 229 } 230 231 pm_client->current_perf_lvl = NSS_PM_PERF_LEVEL_IDLE; 232 233 switch (client_id) { 234 case NSS_PM_CLIENT_GMAC: 235 pm_dentry = debugfs_create_dir("gmac" , ctx.pm_dentry); 236 break; 237 238 case NSS_PM_CLIENT_CRYPTO: 239 pm_dentry = debugfs_create_dir("crypto" , ctx.pm_dentry); 240 break; 241 242 case NSS_PM_CLIENT_NETAP: 243 pm_dentry = debugfs_create_dir("netap" , ctx.pm_dentry); 244 break; 245 246 default: 247 nss_pm_warning("debugfs create failed invalid client id %d \n", client_id); 248 msm_bus_scale_unregister_client((uint32_t) pm_client->bus_perf_client); 249 goto error; 250 251 } 252 253 if (unlikely(pm_dentry == NULL)) { 254 nss_pm_info("debugfs not created for %d client pm \n", client_id); 255 goto out; 256 } 257 258 pm_client->dentry = pm_dentry; 259 pm_client->client_id = client_id; 260 261 if (!debugfs_create_file("perf_level", S_IRUGO | S_IWUSR, pm_dentry, pm_client, &perf_level_fops)) { 262 nss_pm_info("debugfs perf_level file not created for %d client pm \n", client_id); 263 } 264 265 if (!debugfs_create_file("auto-scale", S_IRUGO | S_IWUSR, pm_dentry, pm_client, &autoscale_fops)) { 266 nss_pm_info("debugfs auto-scale file not created for %d client pm \n", client_id); 267 } 268 269out: 270 return (void *)pm_client; 271error: 272#endif 273 return NULL; 274} 275EXPORT_SYMBOL(nss_pm_client_register); 276 277/* 278 * nss_pm_client_unregister 279 * Unregister the client for any PM operations 280 */ 281int nss_pm_client_unregister(nss_pm_client_t client_id) 282{ 283#if (NSS_PM_SUPPORT == 1) 284 nss_pm_client_data_t *pm_client; 285 286 if (unlikely(client_id >= NSS_PM_MAX_CLIENTS)) { 287 nss_pm_warning("nss_pm_client_unregister invalid client id %d \n", client_id); 288 goto error; 289 } 290 291 pm_client = &ctx.nss_pm_client[client_id]; 292 293 if (unlikely(pm_client == NULL)) { 294 nss_pm_warning("nss_pm_client_unregister client not registered %d \n", client_id); 295 goto error; 296 } 297 298 if (pm_client->bus_perf_client) { 299 msm_bus_scale_unregister_client((uint32_t) pm_client->bus_perf_client); 300 } else { 301 nss_pm_info("nss_pm_client_unregister: client not registered \n"); 302 } 303 304 if (likely(pm_client->dentry != NULL)) { 305 debugfs_remove_recursive(pm_client->dentry); 306 } 307 308 return NSS_PM_API_SUCCESS; 309 310error: 311#endif 312 return NSS_PM_API_FAILED; 313} 314 315/* 316 * nss_pm_set_perf_level() 317 * Sets the performance level of client specific Fabrics and Clocks to requested level 318 */ 319nss_pm_interface_status_t nss_pm_set_perf_level(void *handle, nss_pm_perf_level_t lvl) 320{ 321#if (NSS_PM_SUPPORT == 1) 322 int ret = 0; 323 nss_pm_client_data_t *pm_client; 324 325 pm_client = (nss_pm_client_data_t *) handle; 326 if (pm_client->current_perf_lvl == lvl) { 327 nss_pm_trace("Already at perf level %d , ignoring request \n", lvl); 328 return NSS_PM_API_SUCCESS; 329 } 330 331 if (!pm_client->bus_perf_client) { 332 nss_pm_warning("Bus driver client not registered.request failed \n"); 333 return NSS_PM_API_FAILED; 334 } 335 336 /* 337 * Do client specific operations here 338 */ 339 if (pm_client->client_id == NSS_PM_CLIENT_NETAP) { 340 if ((lvl == NSS_PM_PERF_LEVEL_TURBO) && (ctx.turbo_support == true)) { 341 /* 342 * For turbo perf level, switch TCM source to 343 * SRC1 to set TCM clock = 400 MHz 344 * SRC0 and SRC1 are set to 266 and 400 MHz resp. 345 * in nss_hal/ipq806x/nss_hal_pvt.c 346 */ 347 writel(0x3, NSSTCM_CLK_SRC_CTL); 348 } else { 349 /* 350 * For Nominal and Idle perf level, switch to SRC0 to 351 * set TCM clock = 266 MHz 352 */ 353 writel(0x2, NSSTCM_CLK_SRC_CTL); 354 355 if (lvl == NSS_PM_PERF_LEVEL_TURBO) { 356 lvl = NSS_PM_PERF_LEVEL_NOMINAL; 357 } 358 } 359 } 360 361 if (pm_client->client_id == NSS_PM_CLIENT_CRYPTO) { 362 if ((lvl == NSS_PM_PERF_LEVEL_TURBO) && (ctx.turbo_support == true)) { 363 /* 364 * For Turbo mode, set Crypto core and 365 * Fabric port clocks to 213 MHz 366 */ 367 writel(0x23, CE5_ACLK_SRC0_NS); 368 writel(0x23, CE5_HCLK_SRC0_NS); 369 writel(0x23, CE5_CORE_CLK_SRC0_NS); 370 371 writel(0x2, CE5_ACLK_SRC_CTL); 372 writel(0x2, CE5_HCLK_SRC_CTL); 373 writel(0x2, CE5_CORE_CLK_SRC_CTL); 374 } else { 375 lvl = NSS_PM_PERF_LEVEL_NOMINAL; 376 } 377 } 378 379 /* Update bandwidth if request has changed. This may sleep. */ 380 ret = msm_bus_scale_client_update_request(pm_client->bus_perf_client, lvl); 381 if (ret) { 382 nss_pm_warning("bandwidth request failed (%d)\n", ret); 383 return NSS_PM_API_FAILED; 384 } 385 386 nss_pm_info("perf level request, current: %d new: %d \n", pm_client->current_perf_lvl, lvl); 387 pm_client->current_perf_lvl = lvl; 388#endif 389 return NSS_PM_API_SUCCESS; 390} 391EXPORT_SYMBOL(nss_pm_set_perf_level); 392 393#if (NSS_PM_SUPPORT == 1) 394/* 395 * nss_pm_set_turbo() 396 * Sets the turbo support flag globally for all clients 397 */ 398void nss_pm_set_turbo() { 399 400 nss_pm_info("NSS Bus PM - Platform supports Turbo Mode \n"); 401 ctx.turbo_support = true; 402} 403 404/* 405 * nss_pm_init() 406 * Initialize NSS PM top level structures 407 */ 408void nss_pm_init(void) { 409 410 nss_pm_info("NSS Bus PM (platform - IPQ806x, build - %s:%s)\n", __DATE__, __TIME__); 411 412 ctx.pm_dentry = debugfs_create_dir("qca-nss-pm", NULL); 413 414 /* Default turbo support is set to off */ 415 ctx.turbo_support = false; 416 417 if (unlikely(ctx.pm_dentry == NULL)) { 418 nss_pm_warning("Failed to create qca-nss-drv directory in debugfs"); 419 } 420} 421#endif 422