tegrakernel/kernel/nvidia/drivers/platform/tegra/mc/isomgr-pre_t19x.c

467 lines
11 KiB
C

/*
* arch/arm/mach-tegra/isomgr-pre_t19x.c
*
* Copyright (c) 2018, NVIDIA CORPORATION. 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 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.
*
*/
#define pr_fmt(fmt) "%s(): " fmt, __func__
#ifdef CONFIG_COMMON_CLK
#include <linux/platform/tegra/bwmgr_mc.h>
#else
#include <linux/platform/tegra/mc.h>
#include <linux/clk.h>
#include <linux/platform/tegra/clock.h>
#endif
#include <linux/platform/tegra/emc_bwmgr.h>
#include <linux/platform/tegra/isomgr.h>
#include <linux/io.h>
#include <soc/tegra/chip-id.h>
/* This file contains platform specific isomgr
* code for all chips prior to T194.
*/
#define ISOMGR_DEBUG 1
#if ISOMGR_DEBUG
#define SANITY_CHECK_AVAIL_BW() { \
int t = 0; \
int idx = 0; \
for (idx = 0; idx < TEGRA_ISO_CLIENT_COUNT; idx++) { \
if (isomgr_clients[idx].real_bw >= \
isomgr_clients[idx].margin_bw) \
t += isomgr_clients[idx].real_bw; \
else \
t += isomgr_clients[idx].margin_bw; \
} \
if (t + isomgr.avail_bw != isomgr.max_iso_bw) { \
pr_err("bw mismatch, line=%d\n", __LINE__); \
pr_err("t+isomgr.avail_bw=%d, isomgr.max_iso_bw=%d\n", \
t + isomgr.avail_bw, isomgr.max_iso_bw); \
return false; \
} \
}
#else
#define SANITY_CHECK_AVAIL_BW()
#endif
static int iso_bw_percentage = 100;
static struct isoclient_info tegra_null_isoclients[] = {
/* This must be last entry*/
{
.client = TEGRA_ISO_CLIENT_COUNT,
.name = NULL,
},
};
static struct isoclient_info tegra11x_isoclients[] = {
{
.client = TEGRA_ISO_CLIENT_DISP_0,
.name = "disp_0",
.dev_name = "tegradc.0",
.emc_clk_name = "emc",
},
{
.client = TEGRA_ISO_CLIENT_DISP_1,
.name = "disp_1",
.dev_name = "tegradc.1",
.emc_clk_name = "emc",
},
{
.client = TEGRA_ISO_CLIENT_VI_0,
.name = "vi_0",
.dev_name = "vi",
.emc_clk_name = "emc",
},
/* This must be last entry*/
{
.client = TEGRA_ISO_CLIENT_COUNT,
.name = NULL,
},
};
static struct isoclient_info tegra14x_isoclients[] = {
{
.client = TEGRA_ISO_CLIENT_DISP_0,
.name = "disp_0",
.dev_name = "tegradc.0",
.emc_clk_name = "emc",
},
{
.client = TEGRA_ISO_CLIENT_DISP_1,
.name = "disp_1",
.dev_name = "tegradc.1",
.emc_clk_name = "emc",
},
{
.client = TEGRA_ISO_CLIENT_VI_0,
.name = "vi_0",
.dev_name = "vi",
.emc_clk_name = "emc",
},
{
.client = TEGRA_ISO_CLIENT_BBC_0,
.name = "bbc_0",
.dev_name = "tegra_bb.0",
.emc_clk_name = "emc_bw",
},
/* This must be last entry*/
{
.client = TEGRA_ISO_CLIENT_COUNT,
.name = NULL,
},
};
static struct isoclient_info tegra12x_isoclients[] = {
{
.client = TEGRA_ISO_CLIENT_DISP_0,
.name = "disp_0",
.dev_name = "tegradc.0",
.emc_clk_name = "emc",
},
{
.client = TEGRA_ISO_CLIENT_DISP_1,
.name = "disp_1",
.dev_name = "tegradc.1",
.emc_clk_name = "emc",
},
{
.client = TEGRA_ISO_CLIENT_VI_0,
.name = "vi_0",
.dev_name = "tegra_vi",
.emc_clk_name = "emc",
},
{
.client = TEGRA_ISO_CLIENT_ISP_A,
.name = "isp_a",
.dev_name = "tegra_isp.0",
.emc_clk_name = "emc",
},
{
.client = TEGRA_ISO_CLIENT_ISP_B,
.name = "isp_b",
.dev_name = "tegra_isp.1",
.emc_clk_name = "emc",
},
{
.client = TEGRA_ISO_CLIENT_TEGRA_CAMERA,
.name = "tegra_camera",
.dev_name = "tegra_camera_ctrl",
.emc_clk_name = "iso.emc",
},
/* This must be last entry*/
{
.client = TEGRA_ISO_CLIENT_COUNT,
.name = NULL,
},
};
static struct isoclient_info tegra21x_isoclients[] = {
{
.client = TEGRA_ISO_CLIENT_DISP_0,
.name = "disp_0",
.dev_name = "tegradc.0",
.emc_clk_name = "emc",
.bwmgr_id = TEGRA_BWMGR_CLIENT_DISP0,
},
{
.client = TEGRA_ISO_CLIENT_DISP_1,
.name = "disp_1",
.dev_name = "tegradc.1",
.emc_clk_name = "emc",
.bwmgr_id = TEGRA_BWMGR_CLIENT_DISP1,
},
{
.client = TEGRA_ISO_CLIENT_VI_0,
.name = "vi_0",
.dev_name = "tegra_vi",
.emc_clk_name = "emc",
.bwmgr_id = TEGRA_BWMGR_CLIENT_VI,
},
{
.client = TEGRA_ISO_CLIENT_ISP_A,
.name = "isp_a",
.dev_name = "tegra_isp.0",
.emc_clk_name = "emc",
.bwmgr_id = TEGRA_BWMGR_CLIENT_ISPA,
},
{
.client = TEGRA_ISO_CLIENT_ISP_B,
.name = "isp_b",
.dev_name = "tegra_isp.1",
.emc_clk_name = "emc",
.bwmgr_id = TEGRA_BWMGR_CLIENT_ISPB,
},
{
.client = TEGRA_ISO_CLIENT_TEGRA_CAMERA,
.name = "tegra_camera",
.dev_name = "tegra_camera_ctrl",
.emc_clk_name = "iso.emc",
.bwmgr_id = TEGRA_BWMGR_CLIENT_CAMERA,
},
/* This must be last entry*/
{
.client = TEGRA_ISO_CLIENT_COUNT,
.name = NULL,
},
};
static struct isoclient_info tegra18x_isoclients[] = {
{
.client = TEGRA_ISO_CLIENT_DISP_0,
.name = "disp_0",
.bwmgr_id = TEGRA_BWMGR_CLIENT_DISP0,
},
{
.client = TEGRA_ISO_CLIENT_DISP_1,
.name = "disp_1",
.bwmgr_id = TEGRA_BWMGR_CLIENT_DISP1,
},
{
.client = TEGRA_ISO_CLIENT_DISP_2,
.name = "disp_2",
.bwmgr_id = TEGRA_BWMGR_CLIENT_DISP2,
},
{
.client = TEGRA_ISO_CLIENT_ISP_A,
.name = "isp_a",
.bwmgr_id = TEGRA_BWMGR_CLIENT_ISPA,
},
{
.client = TEGRA_ISO_CLIENT_TEGRA_CAMERA,
.name = "camera",
.bwmgr_id = TEGRA_BWMGR_CLIENT_CAMERA,
},
{
.client = TEGRA_ISO_CLIENT_APE_ADMA,
.name = "ape_adma",
.bwmgr_id = TEGRA_BWMGR_CLIENT_APE_ADMA,
},
{
.client = TEGRA_ISO_CLIENT_EQOS,
.name = "eqos",
.bwmgr_id = TEGRA_BWMGR_CLIENT_EQOS,
},
/* This must be last entry*/
{
.client = TEGRA_ISO_CLIENT_COUNT,
.name = NULL,
},
};
static struct isoclient_info *get_iso_client_info(int *length)
{
enum tegra_chipid cid;
struct isoclient_info *cinfo;
int i, len;
cid = tegra_get_chip_id();
switch (cid) {
case TEGRA114:
cinfo = tegra11x_isoclients;
len = ARRAY_SIZE(tegra11x_isoclients);
iso_bw_percentage = 50;
for (i = 0; i < TEGRA_ISO_CLIENT_COUNT; i++)
isomgr_clients[i].limit_bw_percentage = 100;
break;
case TEGRA148:
cinfo = tegra14x_isoclients;
len = ARRAY_SIZE(tegra14x_isoclients);
iso_bw_percentage = 50;
for (i = 0; i < TEGRA_ISO_CLIENT_COUNT; i++)
isomgr_clients[i].limit_bw_percentage = 100;
break;
case TEGRA124:
case TEGRA132:
cinfo = tegra12x_isoclients;
len = ARRAY_SIZE(tegra12x_isoclients);
iso_bw_percentage = 50;
for (i = 0; i < TEGRA_ISO_CLIENT_COUNT; i++)
isomgr_clients[i].limit_bw_percentage = 100;
break;
case TEGRA210:
cinfo = tegra21x_isoclients;
iso_bw_percentage = 45; /* Hack: Should be determined based on
* DRAM type
*/
len = ARRAY_SIZE(tegra21x_isoclients);
for (i = 0; i < TEGRA_ISO_CLIENT_COUNT; i++)
isomgr_clients[i].limit_bw_percentage = 100;
break;
case TEGRA186:
cinfo = tegra18x_isoclients;
len = ARRAY_SIZE(tegra18x_isoclients);
for (i = 0; i < TEGRA_ISO_CLIENT_COUNT; i++) {
if (i == TEGRA_ISO_CLIENT_TEGRA_CAMERA)
isomgr_clients[i].limit_bw_percentage = 10;
else
isomgr_clients[i].limit_bw_percentage = 100;
}
break;
default:
cinfo = tegra_null_isoclients;
len = 0;
break;
}
if (length)
*length = len;
return cinfo;
}
static void pre_t19x_iso_plat_init(void)
{
unsigned int max_emc_clk;
unsigned int max_emc_bw;
if (!isomgr.max_iso_bw) {
#ifdef CONFIG_COMMON_CLK
max_emc_clk = tegra_bwmgr_get_max_emc_rate() / 1000;
max_emc_bw = bwmgr_freq_to_bw(max_emc_clk);
isomgr.max_iso_bw = max_emc_bw *
bwmgr_iso_bw_percentage_max() / 100;
#else
/* With DVFS disabled, bus children cannot get real
* max emc freq supported Only the root parent EMC
* node is set to max possible rate
*/
max_emc_clk = clk_round_rate(clk_get_parent(isomgr.emc_clk),
ULONG_MAX) / 1000;
max_emc_bw = tegra_emc_freq_req_to_bw(max_emc_clk);
/* ISO clients can use iso_bw_percentage of max emc bw. */
isomgr.max_iso_bw = max_emc_bw * iso_bw_percentage / 100;
#endif
pr_info("iso emc max clk=%dKHz\n", max_emc_clk);
pr_info("max_iso_bw=%dKB\n", isomgr.max_iso_bw);
isomgr.avail_bw = isomgr.max_iso_bw;
}
}
static void pre_t19x_iso_plat_unregister(struct isomgr_client *cp)
{
if (cp->real_bw > cp->margin_bw)
isomgr.avail_bw += cp->real_bw;
else
isomgr.avail_bw += cp->margin_bw;
}
static bool pre_t19x_iso_plat_reserve(struct isomgr_client *cp, u32 bw,
enum tegra_iso_client client)
{
u64 bw_check;
u32 max_emc_bw;
if (bw <= cp->margin_bw)
goto bw_limit_check;
if (bw > cp->dedi_bw &&
bw > isomgr.avail_bw + cp->real_bw - isomgr.sleep_bw)
return false;
bw_limit_check:
/* During reserve, check if BW request is within limit_bw_percentage%
* of max emc bw. Using max emc bw to find if the request is possible
* when we raise the freq to max possible value. If not, the reserve
* call will fail
*/
#ifdef CONFIG_COMMON_CLK
max_emc_bw = bwmgr_freq_to_bw(tegra_bwmgr_get_max_emc_rate() / 1000);
#else
max_emc_bw = tegra_emc_freq_req_to_bw(
clk_round_rate(clk_get_parent(isomgr.emc_clk), ULONG_MAX)
/ 1000);
#endif
bw_check = ((u64)max_emc_bw * (u64)(cp->limit_bw_percentage) / 100);
if (bw > bw_check)
return false;
else
return true;
}
static bool pre_t19x_iso_plat_realize(struct isomgr_client *cp)
{
s32 delta_bw = 0;
if (cp->margin_bw < cp->real_bw)
isomgr.avail_bw += cp->real_bw - cp->margin_bw;
cp->real_bw = 0;
cp->realize = true;
if (unlikely(isomgr.avail_bw > isomgr.max_iso_bw)) {
pr_err("isomgr: iso_plat_realize: avail_bw > max_iso_bw\n");
return false;
}
if (cp->rsvd_bw <= cp->margin_bw) {
if (unlikely(cp->sleep_bw)) {
pr_err
("isomgr_realize: rsvd_bw < margin_bw, sleep_bw = 1\n");
return false;
}
cp->real_bw = cp->rsvd_bw; /* reservation has been realized */
cp->real_mf = cp->rsvd_mf; /* minimum frequency realized */
} else if (cp->rsvd_bw <= isomgr.avail_bw + cp->margin_bw) {
delta_bw = cp->rsvd_bw - cp->margin_bw;
isomgr.avail_bw -= delta_bw;
cp->real_bw = cp->rsvd_bw; /* reservation has been realized */
cp->real_mf = cp->rsvd_mf; /* minimum frequency realized */
if (cp->sleep_bw) {
isomgr.sleep_bw -= delta_bw;
cp->sleep_bw -= delta_bw;
if (unlikely(cp->sleep_bw)) {
pr_err
("isomgr:rsvd_bw < margin_bw, sleep_bw = 1\n");
return false;
}
}
if (unlikely(isomgr.avail_bw < 0)) {
pr_err("isomgr: iso_plat_realize: avail_bw < 0\n");
return false;
}
SANITY_CHECK_AVAIL_BW();
} else {
return false;
}
return true;
}
static bool pre_t19x_iso_plat_register(u32 dedi_bw, enum tegra_iso_client client)
{
if (unlikely(dedi_bw > isomgr.max_iso_bw - isomgr.dedi_bw)) {
pr_err("iso bandwidth %uKB is not available, client %s\n",
dedi_bw, cname[client]);
return false;
}
return true;
}
static struct isomgr_ops isomgr_ops_pre_t19x = {
.isomgr_plat_init = pre_t19x_iso_plat_init,
.isomgr_plat_register = pre_t19x_iso_plat_register,
.isomgr_plat_unregister = pre_t19x_iso_plat_unregister,
.isomgr_plat_reserve = pre_t19x_iso_plat_reserve,
.isomgr_plat_realize = pre_t19x_iso_plat_realize,
};
struct isomgr_ops *pre_t19x_isomgr_init(void)
{
isoclient_info = get_iso_client_info(&isoclients);
return &isomgr_ops_pre_t19x;
}