ddr: bring up bandwidth support for sm1 [1/1]
authorTao Zeng <tao.zeng@amlogic.com>
Wed, 10 Apr 2019 03:59:24 +0000 (11:59 +0800)
committerNick Xie <nick@khadas.com>
Mon, 5 Aug 2019 06:06:07 +0000 (14:06 +0800)
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>
arch/arm/boot/dts/amlogic/mesontl1.dtsi
drivers/amlogic/ddr_tool/ddr_bandwidth.c
drivers/amlogic/ddr_tool/ddr_port_desc.c
drivers/amlogic/ddr_tool/dmc_g12.c
drivers/amlogic/ddr_tool/dmc_monitor.c
include/linux/amlogic/aml_ddr_bandwidth.h

index cbd8744..287df2f 100644 (file)
        dmc_monitor {
                compatible = "amlogic, dmc_monitor";
                status = "okay";
-               reg_base = <0xff638800>;
+               reg_base = <0xff639000>;
                interrupts = <GIC_SPI 51 IRQ_TYPE_EDGE_RISING>;
        };
 
index 513ed83..2fa3b7e 100644 (file)
@@ -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 *
index bce96aa..a893e28 100644 (file)
@@ -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;
        }
index cc36a23..fc169c5 100644 (file)
@@ -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) {
index 17086aa..d57ce49 100644 (file)
@@ -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) {
index bde095b..abe2a7f 100644 (file)
 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;