mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 03:40:35 +09:00
ddr: bring up bandwidth support for sm1 [1/1]
PD#SWPL-6865 Problem: ddr bandwidth measure is not supported on sm1 Solution: 1. Add port description and support for it 2. Record max/average bandwidth support; 3. Support up to 60 continue bandwidth sample 4. Fix bit mismatch of dmc monitor for sm1/tl1. Verify: sm1 Change-Id: I0b42db8214099b9cd6d1c3f00174dc65eebfc030 Signed-off-by: Tao Zeng <tao.zeng@amlogic.com> Signed-off-by: Luan Yuan <luan.yuan@amlogic.com>
This commit is contained in:
@@ -1389,7 +1389,7 @@
|
||||
dmc_monitor {
|
||||
compatible = "amlogic, dmc_monitor";
|
||||
status = "okay";
|
||||
reg_base = <0xff638800>;
|
||||
reg_base = <0xff639000>;
|
||||
interrupts = <GIC_SPI 51 IRQ_TYPE_EDGE_RISING>;
|
||||
};
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ static const unsigned int bandwidth_cable[] = {
|
||||
static void cal_ddr_usage(struct ddr_bandwidth *db, struct ddr_grant *dg)
|
||||
{
|
||||
u64 mul; /* avoid overflow */
|
||||
unsigned long i, cnt, freq = 0;
|
||||
unsigned long i, cnt, freq = 0, flags;
|
||||
|
||||
if (db->mode == MODE_AUTODETECT) { /* ignore mali bandwidth */
|
||||
static int count;
|
||||
@@ -77,22 +77,43 @@ static void cal_ddr_usage(struct ddr_bandwidth *db, struct ddr_grant *dg)
|
||||
mul /= 16;
|
||||
cnt = db->clock_count;
|
||||
do_div(mul, cnt);
|
||||
db->total_usage = mul;
|
||||
db->cur_sample.total_usage = mul;
|
||||
if (freq) {
|
||||
/* calculate in KB */
|
||||
mul = dg->all_grant;
|
||||
mul *= freq;
|
||||
mul /= 1024;
|
||||
do_div(mul, cnt);
|
||||
db->total_bandwidth = mul;
|
||||
db->cur_sample.total_bandwidth = mul;
|
||||
db->cur_sample.tick = sched_clock();
|
||||
for (i = 0; i < db->channels; i++) {
|
||||
mul = dg->channel_grant[i];
|
||||
mul *= freq;
|
||||
mul /= 1024;
|
||||
do_div(mul, cnt);
|
||||
db->bandwidth[i] = mul;
|
||||
db->cur_sample.bandwidth[i] = mul;
|
||||
}
|
||||
}
|
||||
|
||||
if (db->stat_flag) /* stop update usage stat if flag set */
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&aml_db->lock, flags);
|
||||
/* update max sample */
|
||||
if (db->cur_sample.total_bandwidth > db->max_sample.total_bandwidth) {
|
||||
memcpy(&db->max_sample, &db->cur_sample,
|
||||
sizeof(struct ddr_bandwidth_sample));
|
||||
}
|
||||
/* update usage statistics */
|
||||
db->usage_stat[db->cur_sample.total_usage / 1000]++;
|
||||
|
||||
/* collect for average bandwidth calculate */
|
||||
db->avg.avg_bandwidth += db->cur_sample.total_bandwidth;
|
||||
db->avg.avg_usage += db->cur_sample.total_usage;
|
||||
for (i = 0; i < db->channels; i++)
|
||||
db->avg.avg_port[i] += db->cur_sample.bandwidth[i];
|
||||
db->avg.sample_count++;
|
||||
spin_unlock_irqrestore(&aml_db->lock, flags);
|
||||
}
|
||||
|
||||
static irqreturn_t dmc_irq_handler(int irq, void *dev_instance)
|
||||
@@ -113,7 +134,7 @@ unsigned int aml_get_ddr_usage(void)
|
||||
unsigned int ret = 0;
|
||||
|
||||
if (aml_db)
|
||||
ret = aml_db->total_usage;
|
||||
ret = aml_db->cur_sample.total_usage;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -268,8 +289,8 @@ static ssize_t mode_store(struct class *cla,
|
||||
aml_db->ops->init(aml_db);
|
||||
} else if ((aml_db->mode != MODE_DISABLE) && (val == MODE_DISABLE)) {
|
||||
free_irq(aml_db->irq_num, (void *)aml_db);
|
||||
aml_db->total_usage = 0;
|
||||
aml_db->total_bandwidth = 0;
|
||||
aml_db->cur_sample.total_usage = 0;
|
||||
aml_db->cur_sample.total_bandwidth = 0;
|
||||
aml_db->busy = 0;
|
||||
}
|
||||
aml_db->mode = val;
|
||||
@@ -301,24 +322,116 @@ static ssize_t clock_count_store(struct class *cla,
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t usage_stat_store(struct class *cla,
|
||||
struct class_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long flags;
|
||||
int d = -1;
|
||||
|
||||
if (kstrtoint(buf, 10, &d))
|
||||
return count;
|
||||
|
||||
aml_db->stat_flag = d;
|
||||
if (d)
|
||||
return count;
|
||||
|
||||
/* clear flag and start statistics */
|
||||
spin_lock_irqsave(&aml_db->lock, flags);
|
||||
memset(&aml_db->max_sample, 0, sizeof(struct ddr_bandwidth_sample));
|
||||
memset(aml_db->usage_stat, 0, 10 * sizeof(int));
|
||||
memset(&aml_db->avg, 0, sizeof(struct ddr_avg_bandwidth));
|
||||
spin_unlock_irqrestore(&aml_db->lock, flags);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t usage_stat_show(struct class *cla,
|
||||
struct class_attribute *attr, char *buf)
|
||||
{
|
||||
size_t s = 0;
|
||||
int percent, rem, i;
|
||||
unsigned long long tick;
|
||||
unsigned long total_count = 0;
|
||||
struct ddr_avg_bandwidth tmp;
|
||||
#define MAX_PREFIX "MAX bandwidth: %8d KB/s, usage: %2d.%02d%%"
|
||||
#define AVG_PREFIX "AVG bandwidth: %8lld KB/s, usage: %2d.%02d%%"
|
||||
|
||||
if (aml_db->mode != MODE_ENABLE)
|
||||
return sprintf(buf, "set mode to enable(1) first.\n");
|
||||
|
||||
/* show for max bandwidth */
|
||||
percent = aml_db->max_sample.total_usage / 100;
|
||||
rem = aml_db->max_sample.total_usage % 100;
|
||||
tick = aml_db->max_sample.tick;
|
||||
do_div(tick, 1000);
|
||||
s += sprintf(buf + s, MAX_PREFIX", tick:%lld us\n",
|
||||
aml_db->max_sample.total_bandwidth,
|
||||
percent, rem, tick);
|
||||
for (i = 0; i < aml_db->channels; i++) {
|
||||
s += sprintf(buf + s, "ch:%d port:%16llx: %8d KB/s\n",
|
||||
i, aml_db->port[i],
|
||||
aml_db->max_sample.bandwidth[i]);
|
||||
}
|
||||
|
||||
/* show for average bandwidth */
|
||||
if (aml_db->avg.sample_count) {
|
||||
memcpy(&tmp, &aml_db->avg, sizeof(tmp));
|
||||
do_div(tmp.avg_bandwidth, tmp.sample_count);
|
||||
do_div(tmp.avg_usage, tmp.sample_count);
|
||||
for (i = 0; i < aml_db->channels; i++)
|
||||
do_div(tmp.avg_port[i], tmp.sample_count);
|
||||
|
||||
rem = do_div(tmp.avg_usage, 100);
|
||||
percent = tmp.avg_usage,
|
||||
s += sprintf(buf + s, AVG_PREFIX", samples:%d\n",
|
||||
tmp.avg_bandwidth,
|
||||
percent, rem, tmp.sample_count);
|
||||
for (i = 0; i < aml_db->channels; i++) {
|
||||
s += sprintf(buf + s, "ch:%d port:%16llx: %8lld KB/s\n",
|
||||
i, aml_db->port[i],
|
||||
tmp.avg_port[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* show for usage statistics */
|
||||
for (i = 0; i < 10; i++)
|
||||
total_count += aml_db->usage_stat[i];
|
||||
|
||||
s += sprintf(buf + s, "\nusage statistics:\n");
|
||||
s += sprintf(buf + s, "range, count, proportion\n");
|
||||
for (i = 0; i < 10; i++) {
|
||||
percent = aml_db->usage_stat[i] * 10000 / total_count;
|
||||
rem = percent % 100;
|
||||
percent = percent / 100;
|
||||
s += sprintf(buf + s, "%2d%% ~ %3d%%: %8d, %3d.%02d%%\n",
|
||||
i * 10, (i + 1) * 10,
|
||||
aml_db->usage_stat[i], percent, rem);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static ssize_t bandwidth_show(struct class *cla,
|
||||
struct class_attribute *attr, char *buf)
|
||||
{
|
||||
size_t s = 0;
|
||||
int percent, rem, i;
|
||||
unsigned long long tick;
|
||||
#define BANDWIDTH_PREFIX "Total bandwidth: %8d KB/s, usage: %2d.%02d%%\n"
|
||||
|
||||
if (aml_db->mode != MODE_ENABLE)
|
||||
return sprintf(buf, "set mode to enable(1) first.\n");
|
||||
|
||||
percent = aml_db->total_usage / 100;
|
||||
rem = aml_db->total_usage % 100;
|
||||
percent = aml_db->cur_sample.total_usage / 100;
|
||||
rem = aml_db->cur_sample.total_usage % 100;
|
||||
tick = aml_db->cur_sample.tick;
|
||||
do_div(tick, 1000);
|
||||
s += sprintf(buf + s, BANDWIDTH_PREFIX,
|
||||
aml_db->total_bandwidth, percent, rem);
|
||||
aml_db->cur_sample.total_bandwidth,
|
||||
percent, rem);
|
||||
|
||||
for (i = 0; i < aml_db->channels; i++) {
|
||||
s += sprintf(buf + s, "ch:%d port bit:%16llx: %8d KB/s\n",
|
||||
i, aml_db->port[i], aml_db->bandwidth[i]);
|
||||
s += sprintf(buf + s, "ch:%d port:%16llx: %8d KB/s\n",
|
||||
i, aml_db->port[i],
|
||||
aml_db->cur_sample.bandwidth[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -466,6 +579,7 @@ static struct class_attribute aml_ddr_tool_attr[] = {
|
||||
__ATTR(urgent, 0664, urgent_show, urgent_store),
|
||||
__ATTR(threshold, 0664, threshold_show, threshold_store),
|
||||
__ATTR(mode, 0664, mode_show, mode_store),
|
||||
__ATTR(usage_stat, 0664, usage_stat_show, usage_stat_store),
|
||||
__ATTR_RO(busy),
|
||||
__ATTR_RO(bandwidth),
|
||||
__ATTR_RO(freq),
|
||||
@@ -597,6 +711,7 @@ static int __ref ddr_bandwidth_probe(struct platform_device *pdev)
|
||||
|
||||
aml_db->irq_num = of_irq_get(node, 0);
|
||||
#endif
|
||||
spin_lock_init(&aml_db->lock);
|
||||
aml_db->clock_count = DEFAULT_CLK_CNT;
|
||||
aml_db->mode = MODE_DISABLE;
|
||||
aml_db->threshold = DEFAULT_THRESHOLD * 16 *
|
||||
|
||||
@@ -421,6 +421,44 @@ static struct ddr_port_desc ddr_port_desc_txhd[] __initdata = {
|
||||
{ .port_id = 47, .port_name = "DEMOD" }
|
||||
};
|
||||
|
||||
static struct ddr_port_desc ddr_port_desc_sm1[] __initdata = {
|
||||
{ .port_id = 0, .port_name = "ARM" },
|
||||
{ .port_id = 1, .port_name = "MALI" },
|
||||
{ .port_id = 2, .port_name = "PCIE" },
|
||||
{ .port_id = 3, .port_name = "HDCP" },
|
||||
{ .port_id = 4, .port_name = "HEVC FRONT" },
|
||||
{ .port_id = 5, .port_name = "TEST" },
|
||||
{ .port_id = 6, .port_name = "USB3.0" },
|
||||
{ .port_id = 7, .port_name = "DEVICE" },
|
||||
{ .port_id = 8, .port_name = "HEVC BACK" },
|
||||
{ .port_id = 9, .port_name = "H265ENC" },
|
||||
{ .port_id = 10, .port_name = "NNA" },
|
||||
{ .port_id = 16, .port_name = "VPU READ1" },
|
||||
{ .port_id = 17, .port_name = "VPU READ2" },
|
||||
{ .port_id = 18, .port_name = "VPU READ3" },
|
||||
{ .port_id = 19, .port_name = "VPU WRITE1" },
|
||||
{ .port_id = 20, .port_name = "VPU WRITE2" },
|
||||
{ .port_id = 21, .port_name = "VDEC" },
|
||||
{ .port_id = 22, .port_name = "HCODEC" },
|
||||
{ .port_id = 23, .port_name = "GE2D" },
|
||||
/* start of each device */
|
||||
{ .port_id = 32, .port_name = "SPICC1" },
|
||||
{ .port_id = 33, .port_name = "USB0" },
|
||||
{ .port_id = 34, .port_name = "DMA" },
|
||||
{ .port_id = 35, .port_name = "ARB0" },
|
||||
{ .port_id = 36, .port_name = "SD_EMMC_B" },
|
||||
{ .port_id = 37, .port_name = "USB1" },
|
||||
{ .port_id = 38, .port_name = "AUDIO" },
|
||||
{ .port_id = 39, .port_name = "AIFIFO" },
|
||||
{ .port_id = 41, .port_name = "PASER" },
|
||||
{ .port_id = 42, .port_name = "AO CPU" },
|
||||
{ .port_id = 43, .port_name = "SD_EMMC_C" },
|
||||
{ .port_id = 44, .port_name = "SPICC2" },
|
||||
{ .port_id = 45, .port_name = "ETHERNET" },
|
||||
{ .port_id = 46, .port_name = "SANA" }
|
||||
};
|
||||
|
||||
|
||||
static struct ddr_port_desc *chip_ddr_port;
|
||||
static unsigned char chip_ddr_port_num;
|
||||
|
||||
@@ -489,6 +527,11 @@ int __init ddr_find_port_desc(int cpu_type, struct ddr_port_desc **desc)
|
||||
desc_size = ARRAY_SIZE(ddr_port_desc_g12b);
|
||||
break;
|
||||
|
||||
case MESON_CPU_MAJOR_ID_SM1:
|
||||
*desc = ddr_port_desc_sm1;
|
||||
desc_size = ARRAY_SIZE(ddr_port_desc_sm1);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -83,12 +83,24 @@ static void check_violation(struct dmc_monitor *mon)
|
||||
struct page *page;
|
||||
unsigned long *p;
|
||||
char id_str[4];
|
||||
char off1 = 21, off2 = 10;
|
||||
char off1, off2;
|
||||
|
||||
if (mon->chip == MESON_CPU_MAJOR_ID_G12B) {
|
||||
switch (mon->chip) {
|
||||
case MESON_CPU_MAJOR_ID_G12B:
|
||||
/* bit fix for G12B */
|
||||
off1 = 24;
|
||||
off2 = 13;
|
||||
break;
|
||||
case MESON_CPU_MAJOR_ID_SM1:
|
||||
case MESON_CPU_MAJOR_ID_TL1:
|
||||
/* bit fix for SM1/TL1 */
|
||||
off1 = 22;
|
||||
off2 = 11;
|
||||
break;
|
||||
default: /* G12A */
|
||||
off1 = 21;
|
||||
off2 = 10;
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 1; i < 4; i += 2) {
|
||||
|
||||
@@ -109,6 +109,30 @@ unsigned int get_all_dev_mask(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int get_other_dev_mask(void)
|
||||
{
|
||||
unsigned int ret = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PORT_MAJOR; i++) {
|
||||
if (dmc_mon->port[i].port_id >= PORT_MAJOR)
|
||||
break;
|
||||
|
||||
/*
|
||||
* we don't want id with arm mali and device
|
||||
* because these devices can access all ddr range
|
||||
* and generate value-less report
|
||||
*/
|
||||
if (strstr(dmc_mon->port[i].port_name, "ARM") ||
|
||||
strstr(dmc_mon->port[i].port_name, "MALI") ||
|
||||
strstr(dmc_mon->port[i].port_name, "DEVICE"))
|
||||
continue;
|
||||
|
||||
ret |= (1 << dmc_mon->port[i].port_id);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static size_t dump_reg(char *buf)
|
||||
{
|
||||
size_t sz = 0, i;
|
||||
@@ -220,6 +244,8 @@ static ssize_t dev_store(struct class *cla,
|
||||
}
|
||||
if (!strncmp(buf, "all", 3))
|
||||
dmc_mon->device = get_all_dev_mask();
|
||||
else if (!strncmp(buf, "other", 5))
|
||||
dmc_mon->device = get_other_dev_mask();
|
||||
else {
|
||||
i = dev_name_to_id(buf);
|
||||
if (i < 0) {
|
||||
|
||||
@@ -114,6 +114,7 @@
|
||||
struct ddr_bandwidth;
|
||||
|
||||
struct ddr_grant {
|
||||
unsigned long long tick;
|
||||
unsigned int all_grant, all_req;
|
||||
unsigned int channel_grant[MAX_CHANNEL];
|
||||
};
|
||||
@@ -129,19 +130,36 @@ struct ddr_bandwidth_ops {
|
||||
#endif
|
||||
};
|
||||
|
||||
struct ddr_bandwidth_sample {
|
||||
unsigned long long tick;
|
||||
unsigned int total_usage;
|
||||
unsigned int total_bandwidth;
|
||||
unsigned int bandwidth[MAX_CHANNEL];
|
||||
};
|
||||
|
||||
struct ddr_avg_bandwidth {
|
||||
unsigned long long avg_bandwidth;
|
||||
unsigned long long avg_usage;
|
||||
unsigned long long avg_port[MAX_CHANNEL];
|
||||
unsigned int sample_count;
|
||||
};
|
||||
|
||||
struct ddr_bandwidth {
|
||||
unsigned short cpu_type;
|
||||
unsigned short real_ports;
|
||||
char busy;
|
||||
char mode;
|
||||
int mali_port[2];
|
||||
int stat_flag;
|
||||
unsigned int threshold;
|
||||
unsigned int irq_num;
|
||||
unsigned int clock_count;
|
||||
unsigned int channels;
|
||||
unsigned int bandwidth[MAX_CHANNEL];
|
||||
unsigned int total_usage;
|
||||
unsigned int total_bandwidth;
|
||||
unsigned int usage_stat[10];
|
||||
spinlock_t lock;
|
||||
struct ddr_bandwidth_sample cur_sample;
|
||||
struct ddr_bandwidth_sample max_sample;
|
||||
struct ddr_avg_bandwidth avg;
|
||||
u64 port[MAX_CHANNEL];
|
||||
void __iomem *ddr_reg;
|
||||
void __iomem *pll_reg;
|
||||
|
||||
Reference in New Issue
Block a user