msm: camera: utils: Add camera clk wrapper infrastructure
Source clocks that are shared with multiple devices need to be consolidated before setting clock rate on them. If not, a set call with lower freq from one device overwrites the frequency that previously set by another device, causing issues. Clk wrapper helps to consolidate the frequency among multiple devices and set the max frequency required by all of them. A shared clock notation is defined in DT and go through clk wrapper based on that. CRs-Fixed: 2901925 Change-Id: Ia5b2b5fd3c0619c994e27d96fad6e11d126de182 Signed-off-by: Pavan Kumar Chilamkurthi <pchilamk@codeaurora.org>
This commit is contained in:
@@ -13,8 +13,274 @@
|
||||
#include "cam_cx_ipeak.h"
|
||||
#include "cam_mem_mgr.h"
|
||||
|
||||
#define CAM_TO_MASK(bitn) (1 << (int)(bitn))
|
||||
#define CAM_IS_BIT_SET(mask, bit) ((mask) & CAM_TO_MASK(bit))
|
||||
#define CAM_SET_BIT(mask, bit) ((mask) |= CAM_TO_MASK(bit))
|
||||
#define CAM_CLEAR_BIT(mask, bit) ((mask) &= ~CAM_TO_MASK(bit))
|
||||
|
||||
/**
|
||||
* struct cam_clk_wrapper_clk: This represents an entry corresponding to a
|
||||
* shared clock in Clk wrapper. Clients that share
|
||||
* the same clock are registered to this clk entry
|
||||
* and set rate from them is consolidated before
|
||||
* setting it to clk driver.
|
||||
*
|
||||
* @list: List pointer to point to next shared clk entry
|
||||
* @clk_id: Clk Id of this clock
|
||||
* @curr_clk_rate: Current clock rate set for this clock
|
||||
* @client_list: List of clients registered to this shared clock entry
|
||||
* @num_clients: Number of clients
|
||||
**/
|
||||
struct cam_clk_wrapper_clk {
|
||||
struct list_head list;
|
||||
uint32_t clk_id;
|
||||
int64_t curr_clk_rate;
|
||||
struct list_head client_list;
|
||||
uint32_t num_clients;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cam_clk_wrapper_client: This represents a client (device) that wants
|
||||
* to share the clock with some other client.
|
||||
*
|
||||
* @list: List pointer to point to next client that share the
|
||||
* same clock
|
||||
* @soc_info: soc_info of client. This is used as unique identifier
|
||||
* for a client
|
||||
* @clk: Clk handle
|
||||
* @curr_clk_rate: Current clock rate set for this client
|
||||
**/
|
||||
struct cam_clk_wrapper_client {
|
||||
struct list_head list;
|
||||
struct cam_hw_soc_info *soc_info;
|
||||
struct clk *clk;
|
||||
int64_t curr_clk_rate;
|
||||
};
|
||||
|
||||
static char supported_clk_info[256];
|
||||
|
||||
static DEFINE_MUTEX(wrapper_lock);
|
||||
static LIST_HEAD(wrapper_clk_list);
|
||||
|
||||
static int cam_soc_util_clk_wrapper_register_entry(
|
||||
uint32_t clk_id, struct clk *clk, struct cam_hw_soc_info *soc_info,
|
||||
const char *clk_name)
|
||||
{
|
||||
struct cam_clk_wrapper_clk *wrapper_clk;
|
||||
struct cam_clk_wrapper_client *wrapper_client;
|
||||
bool clock_found = false;
|
||||
int rc = 0;
|
||||
|
||||
mutex_lock(&wrapper_lock);
|
||||
|
||||
list_for_each_entry(wrapper_clk, &wrapper_clk_list, list) {
|
||||
CAM_DBG(CAM_UTIL, "Clk list id %d num clients %d",
|
||||
wrapper_clk->clk_id, wrapper_clk->num_clients);
|
||||
|
||||
if (wrapper_clk->clk_id == clk_id) {
|
||||
clock_found = true;
|
||||
list_for_each_entry(wrapper_client,
|
||||
&wrapper_clk->client_list, list) {
|
||||
CAM_DBG(CAM_UTIL,
|
||||
"Clk id %d entry client %s",
|
||||
wrapper_clk->clk_id,
|
||||
wrapper_client->soc_info->dev_name);
|
||||
if (wrapper_client->soc_info == soc_info) {
|
||||
CAM_ERR(CAM_UTIL,
|
||||
"Register with same soc info, clk id %d, client %s",
|
||||
clk_id, soc_info->dev_name);
|
||||
rc = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!clock_found) {
|
||||
CAM_DBG(CAM_UTIL, "Adding new entry for clk id %d", clk_id);
|
||||
wrapper_clk = kzalloc(sizeof(struct cam_clk_wrapper_clk),
|
||||
GFP_KERNEL);
|
||||
if (!wrapper_clk) {
|
||||
CAM_ERR(CAM_UTIL,
|
||||
"Failed in allocating new clk entry %d",
|
||||
clk_id);
|
||||
rc = -ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
wrapper_clk->clk_id = clk_id;
|
||||
INIT_LIST_HEAD(&wrapper_clk->list);
|
||||
INIT_LIST_HEAD(&wrapper_clk->client_list);
|
||||
list_add_tail(&wrapper_clk->list, &wrapper_clk_list);
|
||||
}
|
||||
wrapper_client = kzalloc(sizeof(struct cam_clk_wrapper_client),
|
||||
GFP_KERNEL);
|
||||
if (!wrapper_client) {
|
||||
CAM_ERR(CAM_UTIL, "Failed in allocating new client entry %d",
|
||||
clk_id);
|
||||
rc = -ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
wrapper_client->soc_info = soc_info;
|
||||
wrapper_client->clk = clk;
|
||||
|
||||
INIT_LIST_HEAD(&wrapper_client->list);
|
||||
list_add_tail(&wrapper_client->list, &wrapper_clk->client_list);
|
||||
wrapper_clk->num_clients++;
|
||||
|
||||
CAM_DBG(CAM_UTIL,
|
||||
"Adding new client %s for clk[%s] id %d, num clients %d",
|
||||
soc_info->dev_name, clk_name, clk_id, wrapper_clk->num_clients);
|
||||
end:
|
||||
mutex_unlock(&wrapper_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cam_soc_util_clk_wrapper_unregister_entry(
|
||||
uint32_t clk_id, struct cam_hw_soc_info *soc_info)
|
||||
{
|
||||
struct cam_clk_wrapper_clk *wrapper_clk;
|
||||
struct cam_clk_wrapper_client *wrapper_client;
|
||||
bool clock_found = false;
|
||||
bool client_found = false;
|
||||
int rc = 0;
|
||||
|
||||
mutex_lock(&wrapper_lock);
|
||||
|
||||
list_for_each_entry(wrapper_clk, &wrapper_clk_list, list) {
|
||||
CAM_DBG(CAM_UTIL, "Clk list id %d num clients %d",
|
||||
wrapper_clk->clk_id, wrapper_clk->num_clients);
|
||||
|
||||
if (wrapper_clk->clk_id == clk_id) {
|
||||
clock_found = true;
|
||||
list_for_each_entry(wrapper_client,
|
||||
&wrapper_clk->client_list, list) {
|
||||
CAM_DBG(CAM_UTIL, "Clk id %d entry client %s",
|
||||
wrapper_clk->clk_id,
|
||||
wrapper_client->soc_info->dev_name);
|
||||
if (wrapper_client->soc_info == soc_info) {
|
||||
client_found = true;
|
||||
wrapper_clk->num_clients--;
|
||||
list_del_init(&wrapper_client->list);
|
||||
kfree(wrapper_client);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!clock_found) {
|
||||
CAM_ERR(CAM_UTIL, "Shared clk id %d entry not found", clk_id);
|
||||
rc = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!client_found) {
|
||||
CAM_ERR(CAM_UTIL,
|
||||
"Client %pK for Shared clk id %d entry not found",
|
||||
soc_info, clk_id);
|
||||
rc = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
CAM_DBG(CAM_UTIL, "Unregister client %s for clk id %d, num clients %d",
|
||||
soc_info->dev_name, clk_id, wrapper_clk->num_clients);
|
||||
|
||||
if (!wrapper_clk->num_clients) {
|
||||
list_del_init(&wrapper_clk->list);
|
||||
kfree(wrapper_clk);
|
||||
}
|
||||
end:
|
||||
mutex_unlock(&wrapper_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cam_soc_util_clk_wrapper_set_clk_rate(
|
||||
uint32_t clk_id, struct cam_hw_soc_info *soc_info,
|
||||
struct clk *clk, int64_t clk_rate)
|
||||
{
|
||||
struct cam_clk_wrapper_clk *wrapper_clk;
|
||||
struct cam_clk_wrapper_client *wrapper_client;
|
||||
bool clk_found = false;
|
||||
bool client_found = false;
|
||||
int rc = 0;
|
||||
int64_t final_clk_rate = 0;
|
||||
|
||||
if (!soc_info || !clk) {
|
||||
CAM_ERR(CAM_UTIL, "Invalid param soc_info %pK clk %pK",
|
||||
soc_info, clk);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&wrapper_lock);
|
||||
|
||||
list_for_each_entry(wrapper_clk, &wrapper_clk_list, list) {
|
||||
CAM_DBG(CAM_UTIL, "Clk list id %d num clients %d",
|
||||
wrapper_clk->clk_id, wrapper_clk->num_clients);
|
||||
if (wrapper_clk->clk_id == clk_id) {
|
||||
clk_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!clk_found) {
|
||||
CAM_ERR(CAM_UTIL, "Clk entry not found id %d client %s",
|
||||
clk_id, soc_info->dev_name);
|
||||
rc = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
list_for_each_entry(wrapper_client, &wrapper_clk->client_list, list) {
|
||||
CAM_DBG(CAM_UTIL, "Clk id %d client %s, clk rate %lld",
|
||||
wrapper_clk->clk_id, wrapper_client->soc_info->dev_name,
|
||||
wrapper_client->curr_clk_rate);
|
||||
if (wrapper_client->soc_info == soc_info) {
|
||||
client_found = true;
|
||||
CAM_DBG(CAM_UTIL,
|
||||
"Clk enable clk id %d, client %s curr %ld new %ld",
|
||||
clk_id, wrapper_client->soc_info->dev_name,
|
||||
wrapper_client->curr_clk_rate, clk_rate);
|
||||
|
||||
wrapper_client->curr_clk_rate = clk_rate;
|
||||
}
|
||||
|
||||
if (final_clk_rate < wrapper_client->curr_clk_rate)
|
||||
final_clk_rate = wrapper_client->curr_clk_rate;
|
||||
}
|
||||
|
||||
if (!client_found) {
|
||||
CAM_ERR(CAM_UTIL,
|
||||
"Wrapper clk enable without client entry clk id %d client %s",
|
||||
clk_id, soc_info->dev_name);
|
||||
rc = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
CAM_DBG(CAM_UTIL,
|
||||
"Clk id %d, client %s, clients rate %ld, curr %ld final %ld",
|
||||
wrapper_clk->clk_id, soc_info->dev_name, clk_rate,
|
||||
wrapper_clk->curr_clk_rate, final_clk_rate);
|
||||
|
||||
if (final_clk_rate != wrapper_clk->curr_clk_rate) {
|
||||
if (final_clk_rate) {
|
||||
rc = clk_set_rate(clk, final_clk_rate);
|
||||
if (rc) {
|
||||
CAM_ERR(CAM_UTIL, "set_rate failed on clk %d",
|
||||
wrapper_clk->clk_id);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
wrapper_clk->curr_clk_rate = final_clk_rate;
|
||||
}
|
||||
|
||||
end:
|
||||
mutex_unlock(&wrapper_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int cam_soc_util_get_clk_level(struct cam_hw_soc_info *soc_info,
|
||||
int64_t clk_rate, int clk_idx, int32_t *clk_lvl)
|
||||
{
|
||||
@@ -366,18 +632,26 @@ long cam_soc_util_get_clk_round_rate(struct cam_hw_soc_info *soc_info,
|
||||
* @clk: Clock structure information for which rate is to be set
|
||||
* @clk_name: Name of the clock for which rate is being set
|
||||
* @clk_rate: Clock rate to be set
|
||||
* @shared_clk: Whether this is a shared clk
|
||||
* @clk_id: Clock ID
|
||||
* @applied_clk_rate: Final clock rate set to the clk
|
||||
*
|
||||
* @return: Success or failure
|
||||
*/
|
||||
static int cam_soc_util_set_clk_rate(struct clk *clk, const char *clk_name,
|
||||
int64_t clk_rate, unsigned long *applied_clk_rate)
|
||||
static int cam_soc_util_set_clk_rate(struct cam_hw_soc_info *soc_info,
|
||||
struct clk *clk, const char *clk_name,
|
||||
int64_t clk_rate, bool shared_clk, uint32_t clk_id,
|
||||
unsigned long *applied_clk_rate)
|
||||
{
|
||||
int rc = 0;
|
||||
long clk_rate_round = -1;
|
||||
bool set_rate = false;
|
||||
|
||||
if (!clk || !clk_name)
|
||||
if (!clk || !clk_name) {
|
||||
CAM_ERR(CAM_UTIL, "Invalid input clk %pK clk_name %pK",
|
||||
clk, clk_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
CAM_DBG(CAM_UTIL, "set %s, rate %lld", clk_name, clk_rate);
|
||||
if (clk_rate > 0) {
|
||||
@@ -388,11 +662,7 @@ static int cam_soc_util_set_clk_rate(struct clk *clk, const char *clk_name,
|
||||
clk_name, clk_rate_round);
|
||||
return clk_rate_round;
|
||||
}
|
||||
rc = clk_set_rate(clk, clk_rate_round);
|
||||
if (rc) {
|
||||
CAM_ERR(CAM_UTIL, "set_rate failed on %s", clk_name);
|
||||
return rc;
|
||||
}
|
||||
set_rate = true;
|
||||
} else if (clk_rate == INIT_RATE) {
|
||||
clk_rate_round = clk_get_rate(clk);
|
||||
CAM_DBG(CAM_UTIL, "init new_rate %ld", clk_rate_round);
|
||||
@@ -404,10 +674,23 @@ static int cam_soc_util_set_clk_rate(struct clk *clk, const char *clk_name,
|
||||
return clk_rate_round;
|
||||
}
|
||||
}
|
||||
rc = clk_set_rate(clk, clk_rate_round);
|
||||
if (rc) {
|
||||
CAM_ERR(CAM_UTIL, "set_rate failed on %s", clk_name);
|
||||
return rc;
|
||||
set_rate = true;
|
||||
}
|
||||
|
||||
if (set_rate) {
|
||||
if (shared_clk) {
|
||||
CAM_DBG(CAM_UTIL,
|
||||
"Dev %s clk %s id %d Set Shared clk %ld",
|
||||
soc_info->dev_name, clk_name, clk_id,
|
||||
clk_rate_round);
|
||||
cam_soc_util_clk_wrapper_set_clk_rate(
|
||||
clk_id, soc_info, clk, clk_rate_round);
|
||||
} else {
|
||||
rc = clk_set_rate(clk, clk_rate_round);
|
||||
if (rc) {
|
||||
CAM_ERR(CAM_UTIL, "set_rate failed on %s", clk_name);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -461,8 +744,10 @@ int cam_soc_util_set_src_clk_rate(struct cam_hw_soc_info *soc_info,
|
||||
apply_level);
|
||||
}
|
||||
|
||||
rc = cam_soc_util_set_clk_rate(clk,
|
||||
rc = cam_soc_util_set_clk_rate(soc_info, clk,
|
||||
soc_info->clk_name[src_clk_idx], clk_rate,
|
||||
CAM_IS_BIT_SET(soc_info->shared_clk_mask, src_clk_idx),
|
||||
soc_info->clk_id[src_clk_idx],
|
||||
&soc_info->applied_src_clk_rate);
|
||||
if (rc) {
|
||||
CAM_ERR(CAM_UTIL,
|
||||
@@ -481,9 +766,11 @@ int cam_soc_util_set_src_clk_rate(struct cam_hw_soc_info *soc_info,
|
||||
continue;
|
||||
}
|
||||
clk = soc_info->clk[scl_clk_idx];
|
||||
rc = cam_soc_util_set_clk_rate(clk,
|
||||
rc = cam_soc_util_set_clk_rate(soc_info, clk,
|
||||
soc_info->clk_name[scl_clk_idx],
|
||||
soc_info->clk_rate[apply_level][scl_clk_idx],
|
||||
CAM_IS_BIT_SET(soc_info->shared_clk_mask, scl_clk_idx),
|
||||
soc_info->clk_id[scl_clk_idx],
|
||||
NULL);
|
||||
if (rc) {
|
||||
CAM_WARN(CAM_UTIL,
|
||||
@@ -497,21 +784,26 @@ int cam_soc_util_set_src_clk_rate(struct cam_hw_soc_info *soc_info,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cam_soc_util_clk_put(struct clk **clk)
|
||||
int cam_soc_util_put_optional_clk(struct cam_hw_soc_info *soc_info,
|
||||
int32_t clk_indx)
|
||||
{
|
||||
if (!(*clk)) {
|
||||
CAM_ERR(CAM_UTIL, "Invalid params clk");
|
||||
if (clk_indx < 0) {
|
||||
CAM_ERR(CAM_UTIL, "Invalid params clk %d", clk_indx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
clk_put(*clk);
|
||||
*clk = NULL;
|
||||
if (CAM_IS_BIT_SET(soc_info->optional_shared_clk_mask, clk_indx))
|
||||
cam_soc_util_clk_wrapper_unregister_entry(
|
||||
soc_info->optional_clk_id[clk_indx], soc_info);
|
||||
|
||||
clk_put(soc_info->optional_clk[clk_indx]);
|
||||
soc_info->optional_clk[clk_indx] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct clk *cam_soc_util_option_clk_get(struct device_node *np,
|
||||
int index)
|
||||
int index, uint32_t *clk_id)
|
||||
{
|
||||
struct of_phandle_args clkspec;
|
||||
struct clk *clk;
|
||||
@@ -526,23 +818,25 @@ static struct clk *cam_soc_util_option_clk_get(struct device_node *np,
|
||||
return ERR_PTR(rc);
|
||||
|
||||
clk = of_clk_get_from_provider(&clkspec);
|
||||
|
||||
*clk_id = clkspec.args[0];
|
||||
of_node_put(clkspec.np);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
int cam_soc_util_get_option_clk_by_name(struct cam_hw_soc_info *soc_info,
|
||||
const char *clk_name, struct clk **clk, int32_t *clk_index,
|
||||
int32_t *clk_rate)
|
||||
const char *clk_name, int32_t *clk_index)
|
||||
{
|
||||
int index = 0;
|
||||
int rc = 0;
|
||||
struct device_node *of_node = NULL;
|
||||
uint32_t shared_clk_val;
|
||||
|
||||
if (!soc_info || !clk_name || !clk) {
|
||||
if (!soc_info || !clk_name || !clk_index) {
|
||||
CAM_ERR(CAM_UTIL,
|
||||
"Invalid params soc_info %pK clk_name %s clk %pK",
|
||||
soc_info, clk_name, clk);
|
||||
"Invalid params soc_info %pK clk_name %s clk_index %pK",
|
||||
soc_info, clk_name, clk_index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -553,52 +847,129 @@ int cam_soc_util_get_option_clk_by_name(struct cam_hw_soc_info *soc_info,
|
||||
if (index < 0) {
|
||||
CAM_DBG(CAM_UTIL, "No clk data for %s", clk_name);
|
||||
*clk_index = -1;
|
||||
*clk = ERR_PTR(-EINVAL);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*clk = cam_soc_util_option_clk_get(of_node, index);
|
||||
if (IS_ERR(*clk)) {
|
||||
if (index >= CAM_SOC_MAX_OPT_CLK) {
|
||||
CAM_ERR(CAM_UTIL, "Insufficient optional clk entries %d %d",
|
||||
index, CAM_SOC_MAX_OPT_CLK);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
of_property_read_string_index(of_node, "clock-names-option",
|
||||
index, &(soc_info->optional_clk_name[index]));
|
||||
|
||||
soc_info->optional_clk[index] = cam_soc_util_option_clk_get(of_node,
|
||||
index, &soc_info->optional_clk_id[index]);
|
||||
if (IS_ERR(soc_info->optional_clk[index])) {
|
||||
CAM_ERR(CAM_UTIL, "No clk named %s found. Dev %s", clk_name,
|
||||
soc_info->dev_name);
|
||||
*clk_index = -1;
|
||||
*clk = NULL;
|
||||
return -EFAULT;
|
||||
}
|
||||
*clk_index = index;
|
||||
|
||||
rc = of_property_read_u32_index(of_node, "clock-rates-option",
|
||||
index, clk_rate);
|
||||
index, &soc_info->optional_clk_rate[index]);
|
||||
if (rc) {
|
||||
CAM_ERR(CAM_UTIL,
|
||||
"Error reading clock-rates clk_name %s index %d",
|
||||
clk_name, index);
|
||||
cam_soc_util_clk_put(clk);
|
||||
*clk_rate = 0;
|
||||
return rc;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Option clocks are assumed to be available to single Device here.
|
||||
* Hence use INIT_RATE instead of NO_SET_RATE.
|
||||
*/
|
||||
*clk_rate = (*clk_rate == 0) ? (int32_t)INIT_RATE : *clk_rate;
|
||||
soc_info->optional_clk_rate[index] =
|
||||
(soc_info->optional_clk_rate[index] == 0) ?
|
||||
(int32_t)INIT_RATE : soc_info->optional_clk_rate[index];
|
||||
|
||||
CAM_DBG(CAM_UTIL, "clk_name %s index %d clk_rate %d",
|
||||
clk_name, *clk_index, *clk_rate);
|
||||
clk_name, *clk_index, soc_info->optional_clk_rate[index]);
|
||||
|
||||
rc = of_property_read_u32_index(of_node, "shared-clks-option",
|
||||
index, &shared_clk_val);
|
||||
if (rc) {
|
||||
CAM_DBG(CAM_UTIL, "Not shared clk %s index %d",
|
||||
clk_name, index);
|
||||
} else if (shared_clk_val > 1) {
|
||||
CAM_WARN(CAM_UTIL, "Invalid shared clk val %d", shared_clk_val);
|
||||
} else {
|
||||
CAM_DBG(CAM_UTIL,
|
||||
"Dev %s shared clk %s index %d, clk id %d, shared_clk_val %d",
|
||||
soc_info->dev_name, clk_name, index,
|
||||
soc_info->optional_clk_id[index], shared_clk_val);
|
||||
|
||||
if (shared_clk_val) {
|
||||
CAM_SET_BIT(soc_info->optional_shared_clk_mask, index);
|
||||
|
||||
/* Create a wrapper entry if this is a shared clock */
|
||||
CAM_DBG(CAM_UTIL,
|
||||
"Dev %s, clk %s, id %d register wrapper entry for shared clk",
|
||||
soc_info->dev_name,
|
||||
soc_info->optional_clk_name[index],
|
||||
soc_info->optional_clk_id[index]);
|
||||
|
||||
rc = cam_soc_util_clk_wrapper_register_entry(
|
||||
soc_info->optional_clk_id[index],
|
||||
soc_info->optional_clk[index], soc_info,
|
||||
soc_info->optional_clk_name[index]);
|
||||
if (rc) {
|
||||
CAM_ERR(CAM_UTIL,
|
||||
"Failed in registering shared clk Dev %s id %d",
|
||||
soc_info->dev_name,
|
||||
soc_info->optional_clk_id[index]);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
clk_put(soc_info->optional_clk[index]);
|
||||
soc_info->optional_clk_rate[index] = 0;
|
||||
soc_info->optional_clk[index] = NULL;
|
||||
*clk_index = -1;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int cam_soc_util_clk_enable(struct clk *clk, const char *clk_name,
|
||||
int32_t clk_rate, unsigned long *applied_clock_rate)
|
||||
int cam_soc_util_clk_enable(struct cam_hw_soc_info *soc_info,
|
||||
bool optional_clk, int32_t clk_idx, int32_t apply_level,
|
||||
unsigned long *applied_clock_rate)
|
||||
{
|
||||
int rc = 0;
|
||||
struct clk *clk;
|
||||
const char *clk_name;
|
||||
int32_t clk_rate;
|
||||
uint32_t shared_clk_mask;
|
||||
uint32_t clk_id;
|
||||
|
||||
if (!clk || !clk_name)
|
||||
if (!soc_info || (clk_idx < 0) || (apply_level >= CAM_MAX_VOTE)) {
|
||||
CAM_ERR(CAM_UTIL, "Invalid param %d %d", clk_idx, apply_level);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = cam_soc_util_set_clk_rate(clk, clk_name, clk_rate,
|
||||
if (optional_clk) {
|
||||
clk = soc_info->optional_clk[clk_idx];
|
||||
clk_name = soc_info->optional_clk_name[clk_idx];
|
||||
clk_rate = (apply_level == -1) ?
|
||||
0 : soc_info->optional_clk_rate[clk_idx];
|
||||
shared_clk_mask = soc_info->optional_shared_clk_mask;
|
||||
clk_id = soc_info->optional_clk_id[clk_idx];
|
||||
} else {
|
||||
clk = soc_info->clk[clk_idx];
|
||||
clk_name = soc_info->clk_name[clk_idx];
|
||||
clk_rate = (apply_level == -1) ?
|
||||
0 : soc_info->clk_rate[apply_level][clk_idx];
|
||||
shared_clk_mask = soc_info->shared_clk_mask;
|
||||
clk_id = soc_info->clk_id[clk_idx];
|
||||
}
|
||||
|
||||
rc = cam_soc_util_set_clk_rate(soc_info, clk, clk_name, clk_rate,
|
||||
CAM_IS_BIT_SET(shared_clk_mask, clk_idx), clk_id,
|
||||
applied_clock_rate);
|
||||
if (rc)
|
||||
return rc;
|
||||
@@ -612,14 +983,42 @@ int cam_soc_util_clk_enable(struct clk *clk, const char *clk_name,
|
||||
return rc;
|
||||
}
|
||||
|
||||
int cam_soc_util_clk_disable(struct clk *clk, const char *clk_name)
|
||||
int cam_soc_util_clk_disable(struct cam_hw_soc_info *soc_info,
|
||||
bool optional_clk, int32_t clk_idx)
|
||||
{
|
||||
if (!clk || !clk_name)
|
||||
|
||||
struct clk *clk;
|
||||
const char *clk_name;
|
||||
uint32_t shared_clk_mask;
|
||||
uint32_t clk_id;
|
||||
|
||||
if (!soc_info || (clk_idx < 0)) {
|
||||
CAM_ERR(CAM_UTIL, "Invalid param %d", clk_idx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (optional_clk) {
|
||||
clk = soc_info->optional_clk[clk_idx];
|
||||
clk_name = soc_info->optional_clk_name[clk_idx];
|
||||
shared_clk_mask = soc_info->optional_shared_clk_mask;
|
||||
clk_id = soc_info->optional_clk_id[clk_idx];
|
||||
} else {
|
||||
clk = soc_info->clk[clk_idx];
|
||||
clk_name = soc_info->clk_name[clk_idx];
|
||||
shared_clk_mask = soc_info->shared_clk_mask;
|
||||
clk_id = soc_info->clk_id[clk_idx];
|
||||
}
|
||||
|
||||
CAM_DBG(CAM_UTIL, "disable %s", clk_name);
|
||||
clk_disable_unprepare(clk);
|
||||
|
||||
if (CAM_IS_BIT_SET(shared_clk_mask, clk_idx)) {
|
||||
CAM_DBG(CAM_UTIL,
|
||||
"Dev %s clk %s Disabling Shared clk, set 0 rate",
|
||||
soc_info->dev_name, clk_name);
|
||||
cam_soc_util_clk_wrapper_set_clk_rate(clk_id, soc_info, clk, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -657,9 +1056,7 @@ int cam_soc_util_clk_enable_default(struct cam_hw_soc_info *soc_info,
|
||||
cam_cx_ipeak_update_vote_cx_ipeak(soc_info, apply_level);
|
||||
|
||||
for (i = 0; i < soc_info->num_clk; i++) {
|
||||
rc = cam_soc_util_clk_enable(soc_info->clk[i],
|
||||
soc_info->clk_name[i],
|
||||
soc_info->clk_rate[apply_level][i],
|
||||
rc = cam_soc_util_clk_enable(soc_info, false, i, apply_level,
|
||||
&applied_clk_rate);
|
||||
if (rc)
|
||||
goto clk_disable;
|
||||
@@ -683,8 +1080,7 @@ clk_disable:
|
||||
if (soc_info->cam_cx_ipeak_enable)
|
||||
cam_cx_ipeak_update_vote_cx_ipeak(soc_info, 0);
|
||||
for (i--; i >= 0; i--) {
|
||||
cam_soc_util_clk_disable(soc_info->clk[i],
|
||||
soc_info->clk_name[i]);
|
||||
cam_soc_util_clk_disable(soc_info, false, i);
|
||||
}
|
||||
|
||||
return rc;
|
||||
@@ -710,8 +1106,7 @@ void cam_soc_util_clk_disable_default(struct cam_hw_soc_info *soc_info)
|
||||
if (soc_info->cam_cx_ipeak_enable)
|
||||
cam_cx_ipeak_unvote_cx_ipeak(soc_info);
|
||||
for (i = soc_info->num_clk - 1; i >= 0; i--)
|
||||
cam_soc_util_clk_disable(soc_info->clk[i],
|
||||
soc_info->clk_name[i]);
|
||||
cam_soc_util_clk_disable(soc_info, false, i);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -736,6 +1131,8 @@ static int cam_soc_util_get_dt_clk_info(struct cam_hw_soc_info *soc_info)
|
||||
const char *clk_control_debugfs = NULL;
|
||||
const char *clk_cntl_lvl_string = NULL;
|
||||
enum cam_vote_level level;
|
||||
int shared_clk_cnt;
|
||||
struct of_phandle_args clk_args = {0};
|
||||
|
||||
if (!soc_info || !soc_info->dev)
|
||||
return -EINVAL;
|
||||
@@ -856,8 +1253,54 @@ static int cam_soc_util_get_dt_clk_info(struct cam_hw_soc_info *soc_info)
|
||||
soc_info->src_clk_idx = i;
|
||||
CAM_DBG(CAM_UTIL, "src clock = %s, index = %d",
|
||||
src_clk_str, i);
|
||||
break;
|
||||
}
|
||||
|
||||
rc = of_parse_phandle_with_args(of_node, "clocks",
|
||||
"#clock-cells", i, &clk_args);
|
||||
if (rc) {
|
||||
CAM_ERR(CAM_CPAS,
|
||||
"failed to clock info rc=%d", rc);
|
||||
rc = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
soc_info->clk_id[i] = clk_args.args[0];
|
||||
of_node_put(clk_args.np);
|
||||
|
||||
CAM_DBG(CAM_UTIL, "Dev %s clk %s id %d",
|
||||
soc_info->dev_name, soc_info->clk_name[i],
|
||||
soc_info->clk_id[i]);
|
||||
}
|
||||
|
||||
soc_info->shared_clk_mask = 0;
|
||||
shared_clk_cnt = of_property_count_u32_elems(of_node, "shared-clks");
|
||||
if (shared_clk_cnt <= 0) {
|
||||
CAM_DBG(CAM_UTIL, "Dev %s, no shared clks", soc_info->dev_name);
|
||||
} else if (shared_clk_cnt != count) {
|
||||
CAM_ERR(CAM_UTIL, "Dev %s, incorrect shared clock count %d %d",
|
||||
soc_info->dev_name, shared_clk_cnt, count);
|
||||
rc = -EINVAL;
|
||||
goto end;
|
||||
} else {
|
||||
uint32_t shared_clk_val;
|
||||
|
||||
for (i = 0; i < shared_clk_cnt; i++) {
|
||||
rc = of_property_read_u32_index(of_node,
|
||||
"shared-clks", i, &shared_clk_val);
|
||||
if (rc || (shared_clk_val > 1)) {
|
||||
CAM_ERR(CAM_UTIL,
|
||||
"Incorrect shared clk info at %d, val=%d, count=%d",
|
||||
i, shared_clk_val, shared_clk_cnt);
|
||||
rc = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (shared_clk_val)
|
||||
CAM_SET_BIT(soc_info->shared_clk_mask, i);
|
||||
}
|
||||
|
||||
CAM_DBG(CAM_UTIL, "Dev %s shared clk mask 0x%x",
|
||||
soc_info->dev_name, soc_info->shared_clk_mask);
|
||||
}
|
||||
|
||||
/* scalable clk info parsing */
|
||||
@@ -952,9 +1395,11 @@ int cam_soc_util_set_clk_rate_level(struct cam_hw_soc_info *soc_info,
|
||||
soc_info->clk_name[i],
|
||||
soc_info->clk_rate[apply_level][i]);
|
||||
|
||||
rc = cam_soc_util_set_clk_rate(soc_info->clk[i],
|
||||
rc = cam_soc_util_set_clk_rate(soc_info, soc_info->clk[i],
|
||||
soc_info->clk_name[i],
|
||||
soc_info->clk_rate[apply_level][i],
|
||||
CAM_IS_BIT_SET(soc_info->shared_clk_mask, i),
|
||||
soc_info->clk_id[i],
|
||||
&applied_clk_rate);
|
||||
if (rc < 0) {
|
||||
CAM_DBG(CAM_UTIL,
|
||||
@@ -1633,6 +2078,26 @@ int cam_soc_util_request_platform_resource(
|
||||
rc = -ENOENT;
|
||||
goto put_clk;
|
||||
}
|
||||
|
||||
/* Create a wrapper entry if this is a shared clock */
|
||||
if (CAM_IS_BIT_SET(soc_info->shared_clk_mask, i)) {
|
||||
CAM_DBG(CAM_UTIL,
|
||||
"Dev %s, clk %s, id %d register wrapper entry for shared clk",
|
||||
soc_info->dev_name, soc_info->clk_name[i],
|
||||
soc_info->clk_id[i]);
|
||||
rc = cam_soc_util_clk_wrapper_register_entry(
|
||||
soc_info->clk_id[i], soc_info->clk[i], soc_info,
|
||||
soc_info->clk_name[i]);
|
||||
if (rc) {
|
||||
CAM_ERR(CAM_UTIL,
|
||||
"Failed in registering shared clk Dev %s id %d",
|
||||
soc_info->dev_name,
|
||||
soc_info->clk_id[i]);
|
||||
clk_put(soc_info->clk[i]);
|
||||
soc_info->clk[i] = NULL;
|
||||
goto put_clk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rc = cam_soc_util_request_pinctrl(soc_info);
|
||||
@@ -1655,6 +2120,10 @@ put_clk:
|
||||
i = soc_info->num_clk;
|
||||
for (i = i - 1; i >= 0; i--) {
|
||||
if (soc_info->clk[i]) {
|
||||
if (CAM_IS_BIT_SET(soc_info->shared_clk_mask, i))
|
||||
cam_soc_util_clk_wrapper_unregister_entry(
|
||||
soc_info->clk_id[i], soc_info);
|
||||
|
||||
clk_put(soc_info->clk[i]);
|
||||
soc_info->clk[i] = NULL;
|
||||
}
|
||||
@@ -1702,6 +2171,10 @@ int cam_soc_util_release_platform_resource(struct cam_hw_soc_info *soc_info)
|
||||
}
|
||||
|
||||
for (i = soc_info->num_clk - 1; i >= 0; i--) {
|
||||
if (CAM_IS_BIT_SET(soc_info->shared_clk_mask, i))
|
||||
cam_soc_util_clk_wrapper_unregister_entry(
|
||||
soc_info->clk_id[i], soc_info);
|
||||
|
||||
clk_put(soc_info->clk[i]);
|
||||
soc_info->clk[i] = NULL;
|
||||
}
|
||||
|
Reference in New Issue
Block a user