blob: 6db5a9796606bfb23b61e6a9abd14e680bd9ae48 [file] [log] [blame]
/* Copyright (c) 2017, 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 "cam_csiphy_soc.h"
#include "cam_csiphy_core.h"
#include "include/cam_csiphy_1_0_hwreg.h"
#define BYTES_PER_REGISTER 4
#define NUM_REGISTER_PER_LINE 4
#define REG_OFFSET(__start, __i) ((__start) + ((__i) * BYTES_PER_REGISTER))
static int cam_io_phy_dump(void __iomem *base_addr,
uint32_t start_offset, int size)
{
char line_str[128];
char *p_str;
int i;
uint32_t data;
CAM_INFO(CAM_CSIPHY, "addr=%pK offset=0x%x size=%d",
base_addr, start_offset, size);
if (!base_addr || (size <= 0))
return -EINVAL;
line_str[0] = '\0';
p_str = line_str;
for (i = 0; i < size; i++) {
if (i % NUM_REGISTER_PER_LINE == 0) {
snprintf(p_str, 12, "0x%08x: ",
REG_OFFSET(start_offset, i));
p_str += 11;
}
data = readl_relaxed(base_addr + REG_OFFSET(start_offset, i));
snprintf(p_str, 9, "%08x ", data);
p_str += 8;
if ((i + 1) % NUM_REGISTER_PER_LINE == 0) {
CAM_ERR(CAM_CSIPHY, "%s", line_str);
line_str[0] = '\0';
p_str = line_str;
}
}
if (line_str[0] != '\0')
CAM_ERR(CAM_CSIPHY, "%s", line_str);
return 0;
}
int32_t cam_csiphy_mem_dmp(struct cam_hw_soc_info *soc_info)
{
int32_t rc = 0;
resource_size_t size = 0;
void __iomem *addr = NULL;
if (!soc_info) {
rc = -EINVAL;
CAM_ERR(CAM_CSIPHY, "invalid input %d", rc);
return rc;
}
addr = soc_info->reg_map[0].mem_base;
size = resource_size(soc_info->mem_block[0]);
rc = cam_io_phy_dump(addr, 0, (size >> 2));
if (rc < 0) {
CAM_ERR(CAM_CSIPHY, "generating dump failed %d", rc);
return rc;
}
return rc;
}
int32_t cam_csiphy_enable_hw(struct csiphy_device *csiphy_dev)
{
int32_t rc = 0;
struct cam_hw_soc_info *soc_info;
soc_info = &csiphy_dev->soc_info;
if (csiphy_dev->ref_count++) {
CAM_ERR(CAM_CSIPHY, "csiphy refcount = %d",
csiphy_dev->ref_count);
return rc;
}
rc = cam_soc_util_enable_platform_resource(soc_info, true,
CAM_SVS_VOTE, ENABLE_IRQ);
if (rc < 0) {
CAM_ERR(CAM_CSIPHY, "failed to enable platform resources %d",
rc);
return rc;
}
rc = cam_soc_util_set_clk_rate(
soc_info->clk[csiphy_dev->csiphy_clk_index],
soc_info->clk_name[csiphy_dev->csiphy_clk_index],
soc_info->clk_rate[0][csiphy_dev->csiphy_clk_index]);
if (rc < 0) {
CAM_ERR(CAM_CSIPHY, "csiphy_clk_set_rate failed rc: %d", rc);
goto csiphy_disable_platform_resource;
}
cam_csiphy_reset(csiphy_dev);
return rc;
csiphy_disable_platform_resource:
cam_soc_util_disable_platform_resource(soc_info, true, true);
return rc;
}
int32_t cam_csiphy_disable_hw(struct csiphy_device *csiphy_dev)
{
struct cam_hw_soc_info *soc_info;
if (!csiphy_dev || !csiphy_dev->ref_count) {
CAM_ERR(CAM_CSIPHY, "csiphy dev NULL / ref_count ZERO");
return 0;
}
soc_info = &csiphy_dev->soc_info;
if (--csiphy_dev->ref_count) {
CAM_ERR(CAM_CSIPHY, "csiphy refcount = %d",
csiphy_dev->ref_count);
return 0;
}
cam_csiphy_reset(csiphy_dev);
cam_soc_util_disable_platform_resource(soc_info, true, true);
return 0;
}
int32_t cam_csiphy_parse_dt_info(struct platform_device *pdev,
struct csiphy_device *csiphy_dev)
{
int32_t rc = 0, i = 0;
uint32_t clk_cnt = 0;
char *csi_3p_clk_name = "csi_phy_3p_clk";
char *csi_3p_clk_src_name = "csiphy_3p_clk_src";
struct cam_hw_soc_info *soc_info;
csiphy_dev->is_csiphy_3phase_hw = 0;
soc_info = &csiphy_dev->soc_info;
rc = cam_soc_util_get_dt_properties(soc_info);
if (rc < 0) {
CAM_ERR(CAM_CSIPHY, "parsing common soc dt(rc %d)", rc);
return rc;
}
csiphy_dev->is_csiphy_3phase_hw = 0;
if (of_device_is_compatible(csiphy_dev->v4l2_dev_str.pdev->dev.of_node,
"qcom,csiphy-v1.0")) {
csiphy_dev->ctrl_reg->csiphy_2ph_reg = csiphy_2ph_v1_0_reg;
csiphy_dev->ctrl_reg->csiphy_2ph_combo_mode_reg =
csiphy_2ph_v1_0_combo_mode_reg;
csiphy_dev->ctrl_reg->csiphy_3ph_reg = csiphy_3ph_v1_0_reg;
csiphy_dev->ctrl_reg->csiphy_2ph_3ph_mode_reg =
csiphy_3ph_v1_0_combo_mode_reg;
csiphy_dev->ctrl_reg->csiphy_irq_reg = csiphy_irq_reg_1_0;
csiphy_dev->ctrl_reg->csiphy_common_reg = csiphy_common_reg_1_0;
csiphy_dev->ctrl_reg->csiphy_reset_reg = csiphy_reset_reg_1_0;
csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v1_0;
csiphy_dev->hw_version = CSIPHY_VERSION_V10;
csiphy_dev->is_csiphy_3phase_hw = CSI_3PHASE_HW;
csiphy_dev->clk_lane = 0;
} else {
CAM_ERR(CAM_CSIPHY, "invalid hw version : 0x%x",
csiphy_dev->hw_version);
rc = -EINVAL;
return rc;
}
if (soc_info->num_clk > CSIPHY_NUM_CLK_MAX) {
CAM_ERR(CAM_CSIPHY, "invalid clk count=%d, max is %d",
soc_info->num_clk, CSIPHY_NUM_CLK_MAX);
return -EINVAL;
}
for (i = 0; i < soc_info->num_clk; i++) {
if (!strcmp(soc_info->clk_name[i],
csi_3p_clk_src_name)) {
csiphy_dev->csiphy_3p_clk_info[0].clk_name =
soc_info->clk_name[i];
csiphy_dev->csiphy_3p_clk_info[0].clk_rate =
soc_info->clk_rate[0][i];
csiphy_dev->csiphy_3p_clk[0] =
soc_info->clk[i];
continue;
} else if (!strcmp(soc_info->clk_name[i],
csi_3p_clk_name)) {
csiphy_dev->csiphy_3p_clk_info[1].clk_name =
soc_info->clk_name[i];
csiphy_dev->csiphy_3p_clk_info[1].clk_rate =
soc_info->clk_rate[0][i];
csiphy_dev->csiphy_3p_clk[1] =
soc_info->clk[i];
continue;
}
if (!strcmp(soc_info->clk_name[i],
"csiphy_timer_src_clk")) {
csiphy_dev->csiphy_max_clk =
soc_info->clk_rate[0][clk_cnt];
csiphy_dev->csiphy_clk_index = clk_cnt;
}
CAM_DBG(CAM_CSIPHY, "clk_rate[%d] = %d", clk_cnt,
soc_info->clk_rate[0][clk_cnt]);
clk_cnt++;
}
rc = cam_soc_util_request_platform_resource(&csiphy_dev->soc_info,
cam_csiphy_irq, csiphy_dev);
return rc;
}
int32_t cam_csiphy_soc_release(struct csiphy_device *csiphy_dev)
{
if (!csiphy_dev) {
CAM_ERR(CAM_CSIPHY, "csiphy dev NULL");
return 0;
}
cam_soc_util_release_platform_resource(&csiphy_dev->soc_info);
return 0;
}