/* Copyright (c) 2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include #include #include #include #include #include #define DCVS_CMD_REGISTER_CORE 2 #define DCVS_CMD_SET_ALGO_PARAM 3 #define DCVS_CMD_EVENT 4 #define DCVS_CMD_INIT 5 #define DCVS_CMD_SET_POWER_PARAM 6 struct scm_register_core { uint32_t core_id; phys_addr_t core_param_phy; }; struct scm_algo { uint32_t core_id; phys_addr_t algo_phy; }; struct scm_init { uint32_t phy; uint32_t size; }; struct scm_pwr_param { uint32_t core_id; phys_addr_t pwr_param_phy; phys_addr_t freq_phy; phys_addr_t coeffs_phy; }; struct msm_algo_param { enum msm_dcvs_algo_param_type type; union { struct msm_dcvs_algo_param dcvs_param; struct msm_mpd_algo_param mpd_param; } u; }; int msm_dcvs_scm_init(size_t size) { int ret = 0; struct scm_init init; uint32_t p = 0; /* Allocate word aligned non-cacheable memory */ p = allocate_contiguous_ebi_nomap(size, 4); if (!p) return -ENOMEM; init.phy = p; init.size = size; ret = scm_call(SCM_SVC_DCVS, DCVS_CMD_INIT, &init, sizeof(init), NULL, 0); /* Not freed if the initialization succeeds */ if (ret) free_contiguous_memory_by_paddr(p); return ret; } EXPORT_SYMBOL(msm_dcvs_scm_init); int msm_dcvs_scm_register_core(uint32_t core_id, struct msm_dcvs_core_param *param) { int ret = 0; struct scm_register_core reg_data; struct msm_dcvs_core_param *p = NULL; p = kzalloc(PAGE_ALIGN(sizeof(struct msm_dcvs_core_param)), GFP_KERNEL); if (!p) return -ENOMEM; memcpy(p, param, sizeof(struct msm_dcvs_core_param)); reg_data.core_id = core_id; reg_data.core_param_phy = virt_to_phys(p); ret = scm_call(SCM_SVC_DCVS, DCVS_CMD_REGISTER_CORE, ®_data, sizeof(reg_data), NULL, 0); kfree(p); return ret; } EXPORT_SYMBOL(msm_dcvs_scm_register_core); int msm_dcvs_scm_set_algo_params(uint32_t core_id, struct msm_dcvs_algo_param *param) { int ret = 0; struct scm_algo algo; struct msm_algo_param *p = NULL; p = kzalloc(PAGE_ALIGN(sizeof(struct msm_algo_param)), GFP_KERNEL); if (!p) return -ENOMEM; p->type = MSM_DCVS_ALGO_DCVS_PARAM; memcpy(&p->u.dcvs_param, param, sizeof(struct msm_dcvs_algo_param)); algo.core_id = core_id; algo.algo_phy = virt_to_phys(p); ret = scm_call(SCM_SVC_DCVS, DCVS_CMD_SET_ALGO_PARAM, &algo, sizeof(algo), NULL, 0); kfree(p); return ret; } EXPORT_SYMBOL(msm_dcvs_scm_set_algo_params); int msm_mpd_scm_set_algo_params(struct msm_mpd_algo_param *param) { int ret = 0; struct scm_algo algo; struct msm_algo_param *p = NULL; p = kzalloc(PAGE_ALIGN(sizeof(struct msm_algo_param)), GFP_KERNEL); if (!p) return -ENOMEM; p->type = MSM_DCVS_ALGO_MPD_PARAM; memcpy(&p->u.mpd_param, param, sizeof(struct msm_mpd_algo_param)); algo.core_id = 0; algo.algo_phy = virt_to_phys(p); ret = scm_call(SCM_SVC_DCVS, DCVS_CMD_SET_ALGO_PARAM, &algo, sizeof(algo), NULL, 0); kfree(p); return ret; } EXPORT_SYMBOL(msm_mpd_scm_set_algo_params); int msm_dcvs_scm_set_power_params(uint32_t core_id, struct msm_dcvs_power_params *pwr_param, struct msm_dcvs_freq_entry *freq_entry, struct msm_dcvs_energy_curve_coeffs *coeffs) { int ret = 0; struct scm_pwr_param pwr; struct msm_dcvs_power_params *pwrt = NULL; struct msm_dcvs_freq_entry *freqt = NULL; struct msm_dcvs_energy_curve_coeffs *coefft = NULL; pwrt = kzalloc(PAGE_ALIGN(sizeof(struct msm_dcvs_power_params)), GFP_KERNEL); if (!pwrt) return -ENOMEM; freqt = kzalloc(PAGE_ALIGN(sizeof(struct msm_dcvs_freq_entry) * pwr_param->num_freq), GFP_KERNEL); if (!freqt) { kfree(pwrt); return -ENOMEM; } coefft = kzalloc(PAGE_ALIGN( sizeof(struct msm_dcvs_energy_curve_coeffs)), GFP_KERNEL); if (!coefft) { kfree(pwrt); kfree(freqt); return -ENOMEM; } memcpy(pwrt, pwr_param, sizeof(struct msm_dcvs_power_params)); memcpy(freqt, freq_entry, sizeof(struct msm_dcvs_freq_entry)*pwr_param->num_freq); memcpy(coefft, coeffs, sizeof(struct msm_dcvs_energy_curve_coeffs)); pwr.core_id = core_id; pwr.pwr_param_phy = virt_to_phys(pwrt); pwr.freq_phy = virt_to_phys(freqt); pwr.coeffs_phy = virt_to_phys(coefft); ret = scm_call(SCM_SVC_DCVS, DCVS_CMD_SET_POWER_PARAM, &pwr, sizeof(pwr), NULL, 0); kfree(pwrt); kfree(freqt); kfree(coefft); return ret; } EXPORT_SYMBOL(msm_dcvs_scm_set_power_params); int msm_dcvs_scm_event(uint32_t core_id, enum msm_dcvs_scm_event event_id, uint32_t param0, uint32_t param1, uint32_t *ret0, uint32_t *ret1) { int ret = -EINVAL; if (!ret0 || !ret1) return ret; ret = scm_call_atomic4_3(SCM_SVC_DCVS, DCVS_CMD_EVENT, core_id, event_id, param0, param1, ret0, ret1); trace_msm_dcvs_scm_event(core_id, (int)event_id, param0, param1, *ret0, *ret1); return ret; } EXPORT_SYMBOL(msm_dcvs_scm_event);