diff --git a/MAINTAINERS b/MAINTAINERS index af6c0842aeda..ba60c0000b70 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13494,6 +13494,10 @@ AMLOGIC driver for thermal M: Tao Zeng F: drivers/amlogic/thermal/* +M: Tao Zeng +F: drivers/amlogic/ddr_tool/* +F: drivers/amlogic/ddr_tool/ddr_band_port_desc.c + AMLOGIC driver for cpufreq M: Tao Zeng F: drivers/amlogic/cpufreq/* @@ -14014,12 +14018,6 @@ AMLOGIC AXG ADD CLKMSR INTERFACE M: wang xing F: include/linux/amlogic/clk_measure.h -AMLOGIC AXG ADD DDR WINDOW DRIVER -M: jiaxing.ye -F: driver/amlogic/ddr_window/Makefile -F: driver/amlogic/ddr_window/ddr_window.c -F: driver/amlogic/ddr_window/Kconfig - AMLOGIC AUDIO INFO M: wang xing F: drivers/amlogic/audioinfo/ diff --git a/arch/arm/boot/dts/amlogic/meson8b.dtsi b/arch/arm/boot/dts/amlogic/meson8b.dtsi index 6bb0183d3f80..320c4986f625 100644 --- a/arch/arm/boot/dts/amlogic/meson8b.dtsi +++ b/arch/arm/boot/dts/amlogic/meson8b.dtsi @@ -950,4 +950,13 @@ dwc2_b { }; }; /* end of soc*/ + + ddr_bandwidth { + compatible = "amlogic, ddr-bandwidth"; + status = "okay"; + reg = <0x0 0xc8006000 0x0 0x100 + 0x0 0xc8000400 0x0 0x100>; + interrupts = <0 52 1>; + interrupt-names = "ddr_bandwidth"; + }; }; /* end of / */ diff --git a/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi b/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi index f1c60e1a5efc..51157cf17724 100644 --- a/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi @@ -786,6 +786,15 @@ query_licence_cmd = <0x82000050>; status = "disabled"; }; + + ddr_bandwidth { + compatible = "amlogic, ddr-bandwidth"; + status = "okay"; + reg = <0x0 0xff638000 0x0 0x100 + 0x0 0xff637000 0x0 0x100>; + interrupts = <0 52 1>; + interrupt-names = "ddr_bandwidth"; + }; };/* end of / */ &pinctrl_aobus { diff --git a/arch/arm64/boot/dts/amlogic/mesong12a.dtsi b/arch/arm64/boot/dts/amlogic/mesong12a.dtsi index 50e122648926..1cbce22c4b4a 100644 --- a/arch/arm64/boot/dts/amlogic/mesong12a.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesong12a.dtsi @@ -1605,6 +1605,15 @@ <731000 100>; status = "okay"; }; + + ddr_bandwidth { + compatible = "amlogic, ddr-bandwidth"; + status = "okay"; + reg = <0x0 0xff638000 0x0 0x100 + 0x0 0xff638c00 0x0 0x100>; + interrupts = <0 52 1>; + interrupt-names = "ddr_bandwidth"; + }; };/* end of / */ &pinctrl_aobus { diff --git a/arch/arm64/boot/dts/amlogic/mesongxl.dtsi b/arch/arm64/boot/dts/amlogic/mesongxl.dtsi index 8593f9443114..079ba183f5fb 100644 --- a/arch/arm64/boot/dts/amlogic/mesongxl.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesongxl.dtsi @@ -1304,6 +1304,14 @@ clock-names = "clkin0","clkin1","clkin2","clkin3"; status = "disabled"; }; + ddr_bandwidth { + compatible = "amlogic, ddr-bandwidth"; + status = "okay"; + reg = <0x0 0xc8838000 0x0 0x100 + 0x0 0xc8837000 0x0 0x100>; + interrupts = <0 52 1>; + interrupt-names = "ddr_bandwidth"; + }; }; &gpu{ diff --git a/arch/arm64/boot/dts/amlogic/mesongxm.dtsi b/arch/arm64/boot/dts/amlogic/mesongxm.dtsi index eab2a40a79eb..627b956a1069 100644 --- a/arch/arm64/boot/dts/amlogic/mesongxm.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesongxm.dtsi @@ -1406,5 +1406,13 @@ clock-names = "clkin0","clkin1","clkin2","clkin3"; status = "disabled"; }; + ddr_bandwidth { + compatible = "amlogic, ddr-bandwidth"; + status = "okay"; + reg = <0x0 0xc8838000 0x0 0x100 + 0x0 0xc8837000 0x0 0x100>; + interrupts = <0 52 1>; + interrupt-names = "ddr_bandwidth"; + }; }; diff --git a/arch/arm64/boot/dts/amlogic/mesontxlx.dtsi b/arch/arm64/boot/dts/amlogic/mesontxlx.dtsi index 482289f82661..5fe22b9e14c6 100644 --- a/arch/arm64/boot/dts/amlogic/mesontxlx.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesontxlx.dtsi @@ -870,6 +870,14 @@ }; };/* end of hiubus*/ }; /* end of soc*/ + ddr_bandwidth { + compatible = "amlogic, ddr-bandwidth"; + status = "okay"; + reg = <0x0 0xff638000 0x0 0x100 + 0x0 0xff637000 0x0 0x100>; + interrupts = <0 52 1>; + interrupt-names = "ddr_bandwidth"; + }; }; /* end of / */ &pinctrl_aobus { diff --git a/arch/arm64/configs/meson64_defconfig b/arch/arm64/configs/meson64_defconfig index d3a170017a82..ab18d7e27c23 100644 --- a/arch/arm64/configs/meson64_defconfig +++ b/arch/arm64/configs/meson64_defconfig @@ -342,7 +342,8 @@ CONFIG_AMLOGIC_PCIE=y CONFIG_AMLOGIC_IRBLASTER=y CONFIG_AMLOGIC_IIO=y CONFIG_AMLOGIC_SARADC=y -CONFIG_AMLOGIC_DDR_WINDOW_TOOL=m +CONFIG_AMLOGIC_DDR_TOOL=y +CONFIG_AMLOGIC_DDR_BANDWIDTH=y CONFIG_AMLOGIC_TEE=y CONFIG_AMLOGIC_GPIO_IRQ=y CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" diff --git a/drivers/amlogic/Kconfig b/drivers/amlogic/Kconfig index be057ed0038c..329b208e4b9e 100644 --- a/drivers/amlogic/Kconfig +++ b/drivers/amlogic/Kconfig @@ -112,7 +112,7 @@ source "drivers/amlogic/irblaster/Kconfig" source "drivers/amlogic/iio/Kconfig" -source "drivers/amlogic/ddr_window/Kconfig" +source "drivers/amlogic/ddr_tool/Kconfig" source "drivers/amlogic/drm/Kconfig" diff --git a/drivers/amlogic/Makefile b/drivers/amlogic/Makefile index 2805351076dd..77bea31e32f5 100644 --- a/drivers/amlogic/Makefile +++ b/drivers/amlogic/Makefile @@ -103,7 +103,7 @@ obj-$(CONFIG_AMLOGIC_IRBLASTER) += irblaster/ obj-$(CONFIG_AMLOGIC_IIO) += iio/ -obj-$(CONFIG_AMLOGIC_DDR_WINDOW_TOOL) += ddr_window/ +obj-$(CONFIG_AMLOGIC_DDR_TOOL) += ddr_tool/ obj-$(CONFIG_DRM_MESON) += drm/ diff --git a/drivers/amlogic/ddr_tool/Kconfig b/drivers/amlogic/ddr_tool/Kconfig new file mode 100644 index 000000000000..b6ffb87fe287 --- /dev/null +++ b/drivers/amlogic/ddr_tool/Kconfig @@ -0,0 +1,28 @@ +config AMLOGIC_DDR_TOOL + bool "Meson cpu ddr tool" + depends on AMLOGIC_DRIVER + default n + help + This config add some debug tools for DDR, including test ddr + bandwidth, ddr window and other configs. + For more information. + Please see folllowing configs + +config AMLOGIC_DDR_WINDOW_TOOL + bool "Meson ddr window tool" + default n + depends on AMLOGIC_DDR_TOOL + help + This config enables test for ddr window. + This driver acts as a kernel agent which set different ddr window + register value from user space, user space can generate large ddr + loading to test each value are stable. + +config AMLOGIC_DDR_BANDWIDTH + bool "Meson ddr bandwidth" + default n + depends on AMLOGIC_DDR_TOOL + help + This config enables ddr bandwidth measure. + If open it, this driver will export interface to get ddr total + bandwidth, it can be used for ddr DVFS/devfreq system. diff --git a/drivers/amlogic/ddr_tool/Makefile b/drivers/amlogic/ddr_tool/Makefile new file mode 100644 index 000000000000..a3a52b2033b9 --- /dev/null +++ b/drivers/amlogic/ddr_tool/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_AMLOGIC_DDR_WINDOW_TOOL) += ddr_window.o +obj-$(CONFIG_AMLOGIC_DDR_BANDWIDTH) += ddr_bandwidth.o ddr_band_op_gxl.o \ + ddr_band_op_gx.o ddr_band_op_g12.o \ + ddr_band_port_desc.o + diff --git a/drivers/amlogic/ddr_tool/ddr_band_op_g12.c b/drivers/amlogic/ddr_tool/ddr_band_op_g12.c new file mode 100644 index 000000000000..1fb00001766e --- /dev/null +++ b/drivers/amlogic/ddr_tool/ddr_band_op_g12.c @@ -0,0 +1,194 @@ +/* + * drivers/amlogic/ddr_tool/ddr_band_op_g12.c + * + * Copyright (C) 2017 Amlogic, Inc. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void g12_dmc_port_config(struct ddr_bandwidth *db, int channel, int port) +{ + unsigned int val; + unsigned int rp[MAX_CHANNEL] = {DMC_MON_G12_CTRL1, DMC_MON_G12_CTRL3, + DMC_MON_G12_CTRL5, DMC_MON_G12_CTRL7}; + unsigned int rs[MAX_CHANNEL] = {DMC_MON_G12_CTRL2, DMC_MON_G12_CTRL4, + DMC_MON_G12_CTRL6, DMC_MON_G12_CTRL8}; + int subport = -1; + + if (port >= PORT_MAJOR) + subport = port - PORT_MAJOR; + + if (subport < 0) { + val = readl(db->ddr_reg + rp[channel]); + val &= ~(0xffffff << 0); + val |= (1 << port); + writel(val, db->ddr_reg + rp[channel]); + val = 0xffff; + writel(val, db->ddr_reg + rs[channel]); + } else { + val = (0x1 << 23); /* select device */ + writel(val, db->ddr_reg + rp[channel]); + val = readl(db->ddr_reg + rs[channel]); + val &= ~(0xffff); + val |= (1 << subport); + writel(val, db->ddr_reg + rs[channel]); + } +} + +static unsigned long g12_get_ddr_freq_quick(struct ddr_bandwidth *db) +{ + unsigned int val; + unsigned int n, m, od1; + unsigned int od_div = 0xfff; + unsigned long freq = 0; + + val = readl(db->pll_reg); + val = val & 0xfffff; + switch ((val >> 16) & 7) { + case 0: + od_div = 2; + break; + + case 1: + od_div = 3; + break; + + case 2: + od_div = 4; + break; + + case 3: + od_div = 6; + break; + + case 4: + od_div = 8; + break; + + default: + break; + } + + m = val & 0x1ff; + n = ((val >> 10) & 0x1f); + od1 = (((val >> 19) & 0x1)) == 1 ? 2 : 1; + if (n) + freq = 2 * (((DEFAULT_XTAL_FREQ * m) / n) >> od1) / od_div; + + return freq; +} + +static void g12_dmc_bandwidth_enable(struct ddr_bandwidth *db) +{ + unsigned int val; + + /* enable all channel */ + val = (0x01 << 31) | /* enable bit */ + (0x01 << 20) | /* use timer */ + (0x0f << 0); + writel(val, db->ddr_reg + DMC_MON_G12_CTRL0); +} + +static void g12_dmc_bandwidth_init(struct ddr_bandwidth *db) +{ + unsigned int i; + + /* set timer trigger clock_cnt */ + writel(db->clock_count, db->ddr_reg + DMC_MON_G12_TIMER); + g12_dmc_bandwidth_enable(db); + + for (i = 0; i < db->channels; i++) + g12_dmc_port_config(db, i, db->port[i]); +} + +static int g12_handle_irq(struct ddr_bandwidth *db, struct ddr_grant *dg) +{ + unsigned int reg, i, val; + int ret = -1; + + val = readl(db->ddr_reg + DMC_MON_G12_CTRL0); + if (val & DMC_QOS_IRQ) { + /* + * get total bytes by each channel, each cycle 16 bytes; + */ + dg->all_grant = readl(db->ddr_reg + DMC_MON_G12_ALL_GRANT_CNT); + dg->all_req = readl(db->ddr_reg + DMC_MON_G12_ALL_REQ_CNT); + dg->all_grant *= 16; + dg->all_req *= 16; + for (i = 0; i < db->channels; i++) { + reg = DMC_MON_G12_ONE_GRANT_CNT + (i << 2); + dg->channel_grant[i] = readl(db->ddr_reg + reg) * 16; + } + /* clear irq flags */ + writel(val, db->ddr_reg + DMC_MON_G12_CTRL0); + g12_dmc_bandwidth_enable(db); + + ret = 0; + } + return ret; +} + +#if DDR_BANDWIDTH_DEBUG +static int g12_dump_reg(struct ddr_bandwidth *db, char *buf) +{ + int s = 0, i; + unsigned int r; + + for (i = 0; i < 9; i++) { + r = readl(db->ddr_reg + (DMC_MON_G12_CTRL0 + (i << 2))); + s += sprintf(buf + s, "DMC_MON_CTRL%d: %08x\n", i, r); + } + r = readl(db->ddr_reg + DMC_MON_G12_ALL_REQ_CNT); + s += sprintf(buf + s, "DMC_MON_ALL_REQ_CNT: %08x\n", r); + r = readl(db->ddr_reg + DMC_MON_G12_ALL_GRANT_CNT); + s += sprintf(buf + s, "DMC_MON_ALL_GRANT_CNT:%08x\n", r); + r = readl(db->ddr_reg + DMC_MON_G12_ONE_GRANT_CNT); + s += sprintf(buf + s, "DMC_MON_ONE_GRANT_CNT:%08x\n", r); + r = readl(db->ddr_reg + DMC_MON_G12_SEC_GRANT_CNT); + s += sprintf(buf + s, "DMC_MON_SEC_GRANT_CNT:%08x\n", r); + r = readl(db->ddr_reg + DMC_MON_G12_THD_GRANT_CNT); + s += sprintf(buf + s, "DMC_MON_THD_GRANT_CNT:%08x\n", r); + r = readl(db->ddr_reg + DMC_MON_G12_FOR_GRANT_CNT); + s += sprintf(buf + s, "DMC_MON_FOR_GRANT_CNT:%08x\n", r); + r = readl(db->ddr_reg + DMC_MON_G12_TIMER); + s += sprintf(buf + s, "DMC_MON_TIMER: %08x\n", r); + + return s; +} +#endif + +struct ddr_bandwidth_ops g12_ddr_bw_ops = { + .init = g12_dmc_bandwidth_init, + .config_port = g12_dmc_port_config, + .get_freq = g12_get_ddr_freq_quick, + .handle_irq = g12_handle_irq, + .bandwidth_enable = g12_dmc_bandwidth_enable, +#if DDR_BANDWIDTH_DEBUG + .dump_reg = g12_dump_reg, +#endif +}; diff --git a/drivers/amlogic/ddr_tool/ddr_band_op_gx.c b/drivers/amlogic/ddr_tool/ddr_band_op_gx.c new file mode 100644 index 000000000000..1f206cc778d7 --- /dev/null +++ b/drivers/amlogic/ddr_tool/ddr_band_op_gx.c @@ -0,0 +1,146 @@ +/* + * drivers/amlogic/ddr_tool/ddr_band_op_gx.c + * + * Copyright (C) 2017 Amlogic, Inc. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static void gx_dmc_port_config(struct ddr_bandwidth *db, int channel, int port) +{ + unsigned int val; + int subport = -1; + + if (port >= PORT_MAJOR) + subport = port - PORT_MAJOR; + + val = readl(db->ddr_reg + DMC_MON_CTRL2); + if (subport < 0) { + val &= ~(0xfffff << 0); + val |= ((port << 16) | 0xffff); + } else { + val &= ~(0xffff); + if (db->cpu_type == MESON_CPU_MAJOR_ID_M8B) + val |= (0xf << 16) | (1 << subport); + else + val |= (0x7 << 16) | (1 << subport); + } + writel(val, db->ddr_reg + DMC_MON_CTRL2); +} + + +static unsigned long gx_get_ddr_freq_quick(struct ddr_bandwidth *db) +{ + unsigned int val; + unsigned int od, n, m, od1; + unsigned long freq = 0; + + val = readl(db->pll_reg); + od = (val >> 16) & 0x03; + n = (val >> 9) & 0x1f; + m = (val >> 0) & 0x1ff; + od1 = 0; + if ((db->cpu_type >= MESON_CPU_MAJOR_ID_GXBB) && + (db->cpu_type < MESON_CPU_MAJOR_ID_GXL)) { + od1 = (val >> 14) & 0x03; + } + freq = (DEFAULT_XTAL_FREQ * m / (n * (1 + od))) >> od1; + return freq; +} + +static void gx_dmc_bandwidth_enable(struct ddr_bandwidth *db) +{ + /* + * for old chips, only 1 port can be selected + */ + writel(0x8010ffff, db->ddr_reg + DMC_MON_CTRL2); +} + +static void gx_dmc_bandwidth_init(struct ddr_bandwidth *db) +{ + /* set timer trigger clock_cnt */ + writel(db->clock_count, db->ddr_reg + DMC_MON_CTRL3); + gx_dmc_bandwidth_enable(db); + + gx_dmc_port_config(db, 0, db->port[0]); +} + +static int gx_handle_irq(struct ddr_bandwidth *db, struct ddr_grant *dg) +{ + unsigned int reg, val; + int ret = -1; + + val = readl(db->ddr_reg + DMC_MON_CTRL2); + if (val & DMC_QOS_IRQ) { + /* + * get total bytes by each channel, each cycle 16 bytes; + */ + dg->all_grant = readl(db->ddr_reg + DMC_MON_ALL_GRANT_CNT) * 16; + dg->all_req = readl(db->ddr_reg + DMC_MON_ALL_REQ_CNT) * 16; + reg = DMC_MON_ONE_GRANT_CNT; + dg->channel_grant[0] = readl(db->ddr_reg + reg) * 16; + /* clear irq flags */ + writel(val, db->ddr_reg + DMC_MON_CTRL2); + gx_dmc_bandwidth_enable(db); + ret = 0; + } + return ret; +} + +#if DDR_BANDWIDTH_DEBUG +static int gx_dump_reg(struct ddr_bandwidth *db, char *buf) +{ + int s = 0; + unsigned int r; + + r = readl(db->ddr_reg + DMC_MON_CTRL2); + s += sprintf(buf + s, "DMC_MON_CTRL2: %08x\n", r); + r = readl(db->ddr_reg + DMC_MON_CTRL3); + s += sprintf(buf + s, "DMC_MON_CTRL3: %08x\n", r); + r = readl(db->ddr_reg + DMC_MON_ALL_REQ_CNT); + s += sprintf(buf + s, "DMC_MON_ALL_REQ_CNT: %08x\n", r); + r = readl(db->ddr_reg + DMC_MON_ALL_GRANT_CNT); + s += sprintf(buf + s, "DMC_MON_ALL_GRANT_CNT:%08x\n", r); + r = readl(db->ddr_reg + DMC_MON_ONE_GRANT_CNT); + s += sprintf(buf + s, "DMC_MON_ONE_GRANT_CNT:%08x\n", r); + + return s; +} +#endif + +struct ddr_bandwidth_ops gx_ddr_bw_ops = { + .init = gx_dmc_bandwidth_init, + .config_port = gx_dmc_port_config, + .get_freq = gx_get_ddr_freq_quick, + .handle_irq = gx_handle_irq, + .bandwidth_enable = gx_dmc_bandwidth_enable, +#if DDR_BANDWIDTH_DEBUG + .dump_reg = gx_dump_reg, +#endif +}; diff --git a/drivers/amlogic/ddr_tool/ddr_band_op_gxl.c b/drivers/amlogic/ddr_tool/ddr_band_op_gxl.c new file mode 100644 index 000000000000..01dea7c1c547 --- /dev/null +++ b/drivers/amlogic/ddr_tool/ddr_band_op_gxl.c @@ -0,0 +1,164 @@ +/* + * drivers/amlogic/ddr_tool/ddr_band_op_gxl.c + * + * Copyright (C) 2017 Amlogic, Inc. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void gxl_dmc_port_config(struct ddr_bandwidth *db, int channel, int port) +{ + unsigned int val; + unsigned int port_reg[MAX_CHANNEL] = {DMC_MON_CTRL1, DMC_MON_CTRL4, + DMC_MON_CTRL5, DMC_MON_CTRL6}; + int subport = -1; + + if (port >= PORT_MAJOR) + subport = port - PORT_MAJOR; + + val = readl(db->ddr_reg + port_reg[channel]); + if (subport < 0) { + val &= ~(0xffff << 16); + val |= ((1 << (16 + port)) | 0xffff); + } else { + val &= ~(0xffffffff); + val |= (1 << 23) | (1 << subport); + } + writel(val, db->ddr_reg + port_reg[channel]); +} + +static unsigned long gxl_get_ddr_freq_quick(struct ddr_bandwidth *db) +{ + unsigned int val; + unsigned int od, n, m, od1; + unsigned long freq = 0; + + val = readl(db->pll_reg); + od = (val >> 2) & 0x03; + m = (val >> 4) & 0x1ff; + n = (val >> 16) & 0x1f; + od1 = (val >> 0) & 0x03; + freq = (DEFAULT_XTAL_FREQ * m / (n * (1 + od))) >> od1; + return freq; +} + +static void gxl_dmc_bandwidth_enable(struct ddr_bandwidth *db) +{ + unsigned int val; + + /* enable all channel */ + val = (0x01 << 31) | /* enable bit */ + (0x01 << 20) | /* use timer */ + (0x0f << 0); + writel(val, db->ddr_reg + DMC_MON_CTRL2); +} + +static void gxl_dmc_bandwidth_init(struct ddr_bandwidth *db) +{ + unsigned int i; + + /* set timer trigger clock_cnt */ + writel(db->clock_count, db->ddr_reg + DMC_MON_CTRL3); + gxl_dmc_bandwidth_enable(db); + + for (i = 0; i < db->channels; i++) + gxl_dmc_port_config(db, i, db->port[i]); +} + + +static int gxl_handle_irq(struct ddr_bandwidth *db, struct ddr_grant *dg) +{ + unsigned int reg, i, val; + int ret = -1; + + val = readl(db->ddr_reg + DMC_MON_CTRL2); + if (val & DMC_QOS_IRQ) { + /* + * get total bytes by each channel, each cycle 16 bytes; + */ + dg->all_grant = readl(db->ddr_reg + DMC_MON_ALL_GRANT_CNT) * 16; + dg->all_req = readl(db->ddr_reg + DMC_MON_ALL_REQ_CNT) * 16; + for (i = 0; i < db->channels; i++) { + reg = DMC_MON_ONE_GRANT_CNT + (i << 2); + dg->channel_grant[i] = readl(db->ddr_reg + reg) * 16; + } + /* clear irq flags */ + writel(val, db->ddr_reg + DMC_MON_CTRL2); + gxl_dmc_bandwidth_enable(db); + ret = 0; + } + return ret; +} + +#if DDR_BANDWIDTH_DEBUG +static int gxl_dump_reg(struct ddr_bandwidth *db, char *buf) +{ + int s = 0; + unsigned int r; + + + r = readl(db->ddr_reg + DMC_MON_CTRL1); + s += sprintf(buf + s, "DMC_MON_CTRL1: %08x\n", r); + r = readl(db->ddr_reg + DMC_MON_CTRL2); + s += sprintf(buf + s, "DMC_MON_CTRL2: %08x\n", r); + r = readl(db->ddr_reg + DMC_MON_CTRL3); + s += sprintf(buf + s, "DMC_MON_CTRL3: %08x\n", r); + r = readl(db->ddr_reg + DMC_MON_CTRL4); + s += sprintf(buf + s, "DMC_MON_CTRL4: %08x\n", r); + r = readl(db->ddr_reg + DMC_MON_CTRL5); + s += sprintf(buf + s, "DMC_MON_CTRL5: %08x\n", r); + r = readl(db->ddr_reg + DMC_MON_CTRL6); + s += sprintf(buf + s, "DMC_MON_CTRL6: %08x\n", r); + + r = readl(db->ddr_reg + DMC_MON_ALL_REQ_CNT); + s += sprintf(buf + s, "DMC_MON_ALL_REQ_CNT: %08x\n", r); + r = readl(db->ddr_reg + DMC_MON_ALL_GRANT_CNT); + s += sprintf(buf + s, "DMC_MON_ALL_GRANT_CNT:%08x\n", r); + r = readl(db->ddr_reg + DMC_MON_ONE_GRANT_CNT); + s += sprintf(buf + s, "DMC_MON_ONE_GRANT_CNT:%08x\n", r); + r = readl(db->ddr_reg + DMC_MON_SEC_GRANT_CNT); + s += sprintf(buf + s, "DMC_MON_SEC_GRANT_CNT:%08x\n", r); + r = readl(db->ddr_reg + DMC_MON_THD_GRANT_CNT); + s += sprintf(buf + s, "DMC_MON_THD_GRANT_CNT:%08x\n", r); + r = readl(db->ddr_reg + DMC_MON_FOR_GRANT_CNT); + s += sprintf(buf + s, "DMC_MON_FOR_GRANT_CNT:%08x\n", r); + + return s; +} +#endif + +struct ddr_bandwidth_ops gxl_ddr_bw_ops = { + .init = gxl_dmc_bandwidth_init, + .config_port = gxl_dmc_port_config, + .get_freq = gxl_get_ddr_freq_quick, + .handle_irq = gxl_handle_irq, + .bandwidth_enable = gxl_dmc_bandwidth_enable, +#if DDR_BANDWIDTH_DEBUG + .dump_reg = gxl_dump_reg, +#endif +}; diff --git a/drivers/amlogic/ddr_tool/ddr_band_port_desc.c b/drivers/amlogic/ddr_tool/ddr_band_port_desc.c new file mode 100644 index 000000000000..8dd9ea508462 --- /dev/null +++ b/drivers/amlogic/ddr_tool/ddr_band_port_desc.c @@ -0,0 +1,463 @@ +/* + * drivers/amlogic/ddr_tool/ddr_band_port_desc.c + * + * Copyright (C) 2017 Amlogic, Inc. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct ddr_port_desc ddr_port_desc_m8b[] __initdata = { + { .port_id = 0, .port_name = "ARM" }, + { .port_id = 1, .port_name = "MALI0" }, + { .port_id = 2, .port_name = "MALI1" }, + { .port_id = 3, .port_name = "HEVC" }, + { .port_id = 8, .port_name = "VPU1" }, + { .port_id = 9, .port_name = "VPU2" }, + { .port_id = 10, .port_name = "VPU3" }, + { .port_id = 11, .port_name = "VDEC" }, + { .port_id = 12, .port_name = "HCODEC" }, + { .port_id = 14, .port_name = "AUDIO" }, + /* start of each device */ + { .port_id = 33, .port_name = "NAND" }, + { .port_id = 34, .port_name = "BLKMV" }, + { .port_id = 35, .port_name = "ARB0" }, + { .port_id = 36, .port_name = "USB0" }, + { .port_id = 37, .port_name = "USB1" }, + { .port_id = 38, .port_name = "GE2D SOURCE1" }, + { .port_id = 39, .port_name = "GE2D SOURCE2" }, + { .port_id = 40, .port_name = "GE2D DEST" }, + { .port_id = 41, .port_name = "PASER" }, + { .port_id = 42, .port_name = "SANA" }, + { .port_id = 43, .port_name = "SDIO2" }, + { .port_id = 44, .port_name = "SPICC" }, + { .port_id = 45, .port_name = "ETHERNET" } +}; + +static struct ddr_port_desc ddr_port_desc_gxbb[] __initdata = { + { .port_id = 0, .port_name = "ARM" }, + { .port_id = 1, .port_name = "MALI0" }, + { .port_id = 2, .port_name = "MALI1" }, + { .port_id = 3, .port_name = "HDCP" }, + { .port_id = 4, .port_name = "HEVC" }, + { .port_id = 8, .port_name = "VPU READ1" }, + { .port_id = 9, .port_name = "VPU READ2" }, + { .port_id = 10, .port_name = "VPU READ3" }, + { .port_id = 11, .port_name = "VPU WRITE1" }, + { .port_id = 12, .port_name = "VPU WRITE2" }, + { .port_id = 13, .port_name = "VDEC" }, + { .port_id = 14, .port_name = "HCODEC" }, + { .port_id = 15, .port_name = "GE2D" }, + /* start of each device */ + { .port_id = 32, .port_name = "SD_EMMC_A" }, + { .port_id = 33, .port_name = "USB0" }, + { .port_id = 34, .port_name = "BLKMV" }, + { .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 OUT" }, + { .port_id = 39, .port_name = "AUDIO IN" }, + { .port_id = 40, .port_name = "AIU" }, + { .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 = "SPICC" }, + { .port_id = 45, .port_name = "ETHERNET" }, + { .port_id = 46, .port_name = "SANA" } +}; + +static struct ddr_port_desc ddr_port_desc_gxl[] __initdata = { + { .port_id = 0, .port_name = "ARM" }, + { .port_id = 1, .port_name = "MALI0" }, + { .port_id = 2, .port_name = "MALI1" }, + { .port_id = 3, .port_name = "HDCP" }, + { .port_id = 4, .port_name = "HEVC" }, + { .port_id = 5, .port_name = "TEST" }, + { .port_id = 6, .port_name = "USB3.0" }, + { .port_id = 8, .port_name = "VPU READ1" }, + { .port_id = 9, .port_name = "VPU READ2" }, + { .port_id = 10, .port_name = "VPU READ3" }, + { .port_id = 11, .port_name = "VPU WRITE1" }, + { .port_id = 12, .port_name = "VPU WRITE2" }, + { .port_id = 13, .port_name = "VDEC" }, + { .port_id = 14, .port_name = "HCODEC" }, + { .port_id = 15, .port_name = "GE2D" }, + /* start of each device */ + { .port_id = 32, .port_name = "SD_EMMC_A" }, + { .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 OUT" }, + { .port_id = 39, .port_name = "AUDIO IN" }, + { .port_id = 40, .port_name = "AIU" }, + { .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 = "SPICC" }, + { .port_id = 45, .port_name = "ETHERNET" }, + { .port_id = 46, .port_name = "SANA" } +}; + +static struct ddr_port_desc ddr_port_desc_gxm[] __initdata = { + { .port_id = 0, .port_name = "ARM" }, + { .port_id = 1, .port_name = "MALI" }, + { .port_id = 2, .port_name = "H265ENC" }, + { .port_id = 3, .port_name = "HDCP" }, + { .port_id = 4, .port_name = "HEVC" }, + { .port_id = 5, .port_name = "TEST" }, + { .port_id = 6, .port_name = "USB3.0" }, + { .port_id = 8, .port_name = "VPU READ1" }, + { .port_id = 9, .port_name = "VPU READ2" }, + { .port_id = 10, .port_name = "VPU READ3" }, + { .port_id = 11, .port_name = "VPU WRITE1" }, + { .port_id = 12, .port_name = "VPU WRITE2" }, + { .port_id = 13, .port_name = "VDEC" }, + { .port_id = 14, .port_name = "HCODEC" }, + { .port_id = 15, .port_name = "GE2D" }, + /* start of each device */ + { .port_id = 32, .port_name = "SD_EMMC_A" }, + { .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 OUT" }, + { .port_id = 39, .port_name = "AUDIO IN" }, + { .port_id = 40, .port_name = "AIU" }, + { .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 = "SPICC" }, + { .port_id = 45, .port_name = "ETHERNET" }, + { .port_id = 46, .port_name = "SANA" } +}; + +static struct ddr_port_desc ddr_port_desc_gxlx[] __initdata = { + { .port_id = 0, .port_name = "ARM" }, + { .port_id = 1, .port_name = "MALI0" }, + { .port_id = 2, .port_name = "MALI1" }, + { .port_id = 3, .port_name = "HDCP" }, + { .port_id = 4, .port_name = "HEVC" }, + { .port_id = 5, .port_name = "H265ENC/TEST" }, + { .port_id = 6, .port_name = "USB3.0" }, + { .port_id = 8, .port_name = "VPU READ1" }, + { .port_id = 9, .port_name = "VPU READ2" }, + { .port_id = 10, .port_name = "VPU READ3" }, + { .port_id = 11, .port_name = "VPU WRITE1" }, + { .port_id = 12, .port_name = "VPU WRITE2" }, + { .port_id = 13, .port_name = "VDEC" }, + { .port_id = 14, .port_name = "HCODEC" }, + { .port_id = 15, .port_name = "GE2D" }, + /* start of each device */ + { .port_id = 32, .port_name = "SD_EMMC_A" }, + { .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 OUT" }, + { .port_id = 39, .port_name = "AUDIO IN" }, + { .port_id = 40, .port_name = "AIU" }, + { .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 = "SPICC" }, + { .port_id = 45, .port_name = "ETHERNET" }, + { .port_id = 46, .port_name = "SANA" } +}; + +static struct ddr_port_desc ddr_port_desc_g12a[] __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 = 8, .port_name = "HEVC BACK" }, + { .port_id = 9, .port_name = "H265ENC" }, + { .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 ddr_port_desc_g12b[] __initdata = { + { .port_id = 0, .port_name = "ARM-ADDR-E" }, + { .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 = 8, .port_name = "HEVC BACK" }, + { .port_id = 9, .port_name = "H265ENC" }, + { .port_id = 10, .port_name = "NNA" }, + { .port_id = 11, .port_name = "GDC" }, + { .port_id = 12, .port_name = "MIPI ISP" }, + { .port_id = 13, .port_name = "ARM-ADDR-F" }, + { .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 = 40, .port_name = "SD_EMMC_A" }, + { .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 ddr_port_desc_axg[] __initdata = { + { .port_id = 0, .port_name = "ARM" }, + { .port_id = 1, .port_name = "PCIE-A" }, + { .port_id = 2, .port_name = "PCIE-B" }, + { .port_id = 3, .port_name = "GE2D" }, + { .port_id = 4, .port_name = "AUDIO" }, + { .port_id = 5, .port_name = "TEST" }, + { .port_id = 6, .port_name = "USB3.0" }, + { .port_id = 8, .port_name = "VPU READ0" }, + /* start of each device */ + { .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 = 42, .port_name = "AO CPU" }, + { .port_id = 43, .port_name = "SD_EMMC_C" }, + { .port_id = 44, .port_name = "SPICC0" }, + { .port_id = 45, .port_name = "ETHERNET" }, + { .port_id = 46, .port_name = "SPICC1" } +}; + +static struct ddr_port_desc ddr_port_desc_txl[] __initdata = { + { .port_id = 0, .port_name = "ARM" }, + { .port_id = 1, .port_name = "MALI0" }, + { .port_id = 2, .port_name = "MALI1" }, + { .port_id = 3, .port_name = "HDCP" }, + { .port_id = 4, .port_name = "HEVC" }, + { .port_id = 5, .port_name = "TEST" }, + { .port_id = 6, .port_name = "USB3.0" }, + { .port_id = 8, .port_name = "VPU READ1" }, + { .port_id = 9, .port_name = "VPU READ2" }, + { .port_id = 10, .port_name = "VPU READ3" }, + { .port_id = 11, .port_name = "VPU WRITE1" }, + { .port_id = 12, .port_name = "VPU WRITE2" }, + { .port_id = 13, .port_name = "VDEC" }, + { .port_id = 14, .port_name = "HCODEC" }, + { .port_id = 15, .port_name = "GE2D" }, + /* start of each device */ + { .port_id = 32, .port_name = "SD_EMMC_A" }, + { .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 OUT" }, + { .port_id = 39, .port_name = "AUDIO IN" }, + { .port_id = 40, .port_name = "AIU" }, + { .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 = "SPICC" }, + { .port_id = 45, .port_name = "ETHERNET" }, + { .port_id = 46, .port_name = "SANA" }, + { .port_id = 47, .port_name = "DEMOD" } +}; + +static struct ddr_port_desc ddr_port_desc_txlx[] __initdata = { + { .port_id = 0, .port_name = "ARM" }, + { .port_id = 1, .port_name = "MALI0" }, + { .port_id = 2, .port_name = "MALI1" }, + { .port_id = 3, .port_name = "HDCP" }, + { .port_id = 4, .port_name = "HEVC" }, + { .port_id = 5, .port_name = "TEST" }, + { .port_id = 6, .port_name = "USB3.0" }, + { .port_id = 8, .port_name = "VPU READ1" }, + { .port_id = 9, .port_name = "VPU READ2" }, + { .port_id = 10, .port_name = "VPU READ3" }, + { .port_id = 11, .port_name = "VPU WRITE1" }, + { .port_id = 12, .port_name = "VPU WRITE2" }, + { .port_id = 13, .port_name = "VDEC" }, + { .port_id = 14, .port_name = "HCODEC" }, + { .port_id = 15, .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 OUT" }, + { .port_id = 39, .port_name = "AUDIO IN" }, + { .port_id = 40, .port_name = "AIU" }, + { .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 = "SPICC" }, + { .port_id = 45, .port_name = "ETHERNET" }, + { .port_id = 46, .port_name = "SANA" }, + { .port_id = 47, .port_name = "DEMOD" } +}; + +static struct ddr_port_desc ddr_port_desc_txhd[] __initdata = { + { .port_id = 0, .port_name = "ARM" }, + { .port_id = 1, .port_name = "MALI0" }, + { .port_id = 2, .port_name = "MALI1" }, + { .port_id = 4, .port_name = "HEVC" }, + { .port_id = 5, .port_name = "TEST" }, + { .port_id = 6, .port_name = "USB3.0" }, + { .port_id = 8, .port_name = "VPU READ1" }, + { .port_id = 9, .port_name = "VPU READ2" }, + { .port_id = 10, .port_name = "VPU READ3" }, + { .port_id = 11, .port_name = "VPU WRITE1" }, + { .port_id = 12, .port_name = "VPU WRITE2" }, + { .port_id = 13, .port_name = "VDEC" }, + { .port_id = 14, .port_name = "HCODEC" }, + { .port_id = 15, .port_name = "GE2D" }, + /* start of each device */ + { .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 OUT" }, + { .port_id = 39, .port_name = "AUDIO IN" }, + { .port_id = 40, .port_name = "AIU" }, + { .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 = "SPICC" }, + { .port_id = 45, .port_name = "ETHERNET" }, + { .port_id = 46, .port_name = "SANA" }, + { .port_id = 47, .port_name = "DEMOD" } +}; + +int __init ddr_find_port_desc(int cpu_type, struct ddr_port_desc **desc) +{ + int desc_size = -EINVAL; + + switch (cpu_type) { + case MESON_CPU_MAJOR_ID_M8B: + *desc = ddr_port_desc_m8b; + desc_size = ARRAY_SIZE(ddr_port_desc_m8b); + break; + + case MESON_CPU_MAJOR_ID_GXBB: + *desc = ddr_port_desc_gxbb; + desc_size = ARRAY_SIZE(ddr_port_desc_gxbb); + break; + + case MESON_CPU_MAJOR_ID_GXTVBB: + /* Not used or need implement? */ + break; + + case MESON_CPU_MAJOR_ID_GXL: + *desc = ddr_port_desc_gxl; + desc_size = ARRAY_SIZE(ddr_port_desc_gxl); + break; + + case MESON_CPU_MAJOR_ID_GXM: + *desc = ddr_port_desc_gxm; + desc_size = ARRAY_SIZE(ddr_port_desc_gxm); + break; + + case MESON_CPU_MAJOR_ID_TXL: + *desc = ddr_port_desc_txl; + desc_size = ARRAY_SIZE(ddr_port_desc_txl); + break; + + case MESON_CPU_MAJOR_ID_TXLX: + *desc = ddr_port_desc_txlx; + desc_size = ARRAY_SIZE(ddr_port_desc_txlx); + break; + + case MESON_CPU_MAJOR_ID_AXG: + *desc = ddr_port_desc_axg; + desc_size = ARRAY_SIZE(ddr_port_desc_axg); + break; + + case MESON_CPU_MAJOR_ID_GXLX: + *desc = ddr_port_desc_gxlx; + desc_size = ARRAY_SIZE(ddr_port_desc_gxlx); + break; + + case MESON_CPU_MAJOR_ID_TXHD: + *desc = ddr_port_desc_txhd; + desc_size = ARRAY_SIZE(ddr_port_desc_txhd); + break; + + case MESON_CPU_MAJOR_ID_G12A: + *desc = ddr_port_desc_g12a; + desc_size = ARRAY_SIZE(ddr_port_desc_g12a); + break; + + case MESON_CPU_MAJOR_ID_G12B: + *desc = ddr_port_desc_g12b; + desc_size = ARRAY_SIZE(ddr_port_desc_g12b); + break; + + default: + break; + } + + return desc_size; +} diff --git a/drivers/amlogic/ddr_tool/ddr_bandwidth.c b/drivers/amlogic/ddr_tool/ddr_bandwidth.c new file mode 100644 index 000000000000..8b8f9d7ca352 --- /dev/null +++ b/drivers/amlogic/ddr_tool/ddr_bandwidth.c @@ -0,0 +1,396 @@ +/* + * drivers/amlogic/ddr_tool/ddr_bandwidth.c + * + * Copyright (C) 2017 Amlogic, Inc. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct ddr_bandwidth *aml_db; + +static void cal_ddr_usage(struct ddr_bandwidth *db, struct ddr_grant *dg) +{ + u64 mul; /* avoid overflow */ + unsigned long i, cnt, freq = 0; + + if (db->ops && db->ops->get_freq) + freq = db->ops->get_freq(db); + mul = (dg->all_grant * 10000ULL) / 16; /* scale up to keep precision*/ + cnt = db->clock_count; + do_div(mul, cnt); + db->total_usage = mul; + if (freq) { + /* calculate in KB */ + mul = (dg->all_grant * freq) / 1024; + do_div(mul, cnt); + db->total_bandwidth = mul; + for (i = 0; i < db->channels; i++) { + mul = (dg->channel_grant[i] * freq) / 1024; + do_div(mul, cnt); + db->bandwidth[i] = mul; + } + } +} + +static irqreturn_t dmc_irq_handler(int irq, void *dev_instance) +{ + struct ddr_bandwidth *db; + struct ddr_grant dg = {0}; + + db = (struct ddr_bandwidth *)dev_instance; + if (db->ops && db->ops->handle_irq) { + if (!db->ops->handle_irq(db, &dg)) + cal_ddr_usage(db, &dg); + } + return IRQ_HANDLED; +} + +unsigned int aml_get_ddr_usage(void) +{ + unsigned int ret = 0; + + if (aml_db) + ret = aml_db->total_usage; + + return ret; +} +EXPORT_SYMBOL(aml_get_ddr_usage); + +static char *find_port_name(int id) +{ + int i; + + if (!aml_db->real_ports || !aml_db->port_desc) + return NULL; + + for (i = 0; i < aml_db->real_ports; i++) { + if (aml_db->port_desc[i].port_id == id) + return aml_db->port_desc[i].port_name; + } + return NULL; +} + +static ssize_t ddr_channel_show(struct class *cla, + struct class_attribute *attr, char *buf) +{ + int size = 0, i; + + for (i = 0; i < aml_db->channels; i++) + size += sprintf(buf + size, "ch %d:%3d, %s\n", + i, aml_db->port[i], + find_port_name(aml_db->port[i])); + + return size; +} + +static ssize_t ddr_channel_store(struct class *cla, + struct class_attribute *attr, const char *buf, size_t count) +{ + int ch = 0, port = 0; + + if (sscanf(buf, "%d:%d", &ch, &port) < 2) { + pr_info("invalid input:%s\n", buf); + return count; + } + + if (ch >= MAX_CHANNEL || + (ch && aml_db->cpu_type < MESON_CPU_MAJOR_ID_GXTVBB) || + port > MAX_PORTS) { + pr_info("invalid channel %d or port %d\n", ch, port); + return count; + } + + if (aml_db->ops && aml_db->ops->config_port) { + aml_db->ops->config_port(aml_db, ch, port); + aml_db->port[ch] = port; + } + + return count; +} + +static ssize_t clock_count_show(struct class *cla, + struct class_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", aml_db->clock_count); +} + +static ssize_t clock_count_store(struct class *cla, + struct class_attribute *attr, const char *buf, size_t count) +{ + long val = 0; + + if (kstrtoul(buf, 10, &val)) { + pr_info("invalid input:%s\n", buf); + return 0; + } + aml_db->clock_count = val; + if (aml_db->ops && aml_db->ops->init) + aml_db->ops->init(aml_db); + + return count; +} + +static ssize_t bandwidth_show(struct class *cla, + struct class_attribute *attr, char *buf) +{ + size_t s = 0, i; + int percent, rem; +#define BANDWIDTH_PREFIX "Total bandwidth:%8d KB/s, usage:%2d.%02d%%\n" + + percent = aml_db->total_usage / 100; + rem = aml_db->total_usage % 100; + s += sprintf(buf + s, BANDWIDTH_PREFIX, + aml_db->total_bandwidth, percent, rem); + + for (i = 0; i < aml_db->channels; i++) { + s += sprintf(buf + s, "Channel %ld, bandwidth:%8d KB/s\n", + i, aml_db->bandwidth[i]); + } + return s; +} + +static ssize_t freq_show(struct class *cla, + struct class_attribute *attr, char *buf) +{ + unsigned long clk = 0; + + if (aml_db->ops && aml_db->ops->get_freq) + clk = aml_db->ops->get_freq(aml_db); + return sprintf(buf, "%ld MHz\n", clk / 1000000); +} + +#if DDR_BANDWIDTH_DEBUG +static ssize_t dump_reg_show(struct class *cla, + struct class_attribute *attr, char *buf) +{ + int s = 0; + + if (aml_db->ops && aml_db->ops->dump_reg) + return aml_db->ops->dump_reg(aml_db, buf); + + return s; +} +#endif + +static ssize_t cpu_type_show(struct class *cla, + struct class_attribute *attr, char *buf) +{ + int cpu_type; + + cpu_type = aml_db->cpu_type; + return sprintf(buf, "%x\n", cpu_type); +} + +static ssize_t name_of_ports_show(struct class *cla, + struct class_attribute *attr, char *buf) +{ + int i, s = 0; + + if (!aml_db->real_ports || !aml_db->port_desc) + return -EINVAL; + + for (i = 0; i < aml_db->real_ports; i++) { + s += sprintf(buf + s, "%2d, %s\n", + aml_db->port_desc[i].port_id, + aml_db->port_desc[i].port_name); + } + + return s; +} + +static struct class_attribute aml_ddr_tool_attr[] = { + __ATTR(port, 0664, ddr_channel_show, ddr_channel_store), + __ATTR(irq_clock, 0664, clock_count_show, clock_count_store), + __ATTR_RO(bandwidth), + __ATTR_RO(freq), + __ATTR_RO(cpu_type), + __ATTR_RO(name_of_ports), +#if DDR_BANDWIDTH_DEBUG + __ATTR_RO(dump_reg), +#endif + __ATTR_NULL +}; + +static struct class aml_ddr_class = { + .name = "aml_ddr", + .class_attrs = aml_ddr_tool_attr, +}; + +static int ddr_bandwidth_probe(struct platform_device *pdev) +{ + int r = 0; +#ifdef CONFIG_OF + struct device_node *node = pdev->dev.of_node; + const char *irq_name = NULL; + /*struct pinctrl *p;*/ + struct resource *res; + resource_size_t *base; +#endif + struct ddr_port_desc *desc = NULL; + int pcnt; + + aml_db = kzalloc(sizeof(struct ddr_bandwidth), GFP_KERNEL); + if (!aml_db) + return -ENOMEM; + + aml_db->cpu_type = get_meson_cpu_version(0); + pr_info("chip type:0x%x\n", aml_db->cpu_type); + if (aml_db->cpu_type < MESON_CPU_MAJOR_ID_M8B) { + pr_info("unsupport chip type:%d\n", aml_db->cpu_type); + goto inval; + } + + /* set channel */ + if (aml_db->cpu_type < MESON_CPU_MAJOR_ID_GXTVBB) + aml_db->channels = 1; + else + aml_db->channels = 4; + + /* find and configure port description */ + pcnt = ddr_find_port_desc(aml_db->cpu_type, &desc); + if (pcnt < 0) + pr_err("can't find port descriptor,cpu:%d\n", aml_db->cpu_type); + else { + aml_db->port_desc = kcalloc(pcnt, sizeof(*desc), GFP_KERNEL); + if (!aml_db->port_desc) + goto inval; + pr_info("port count:%d, desc:%p\n", pcnt, aml_db->port_desc); + memcpy(aml_db->port_desc, desc, sizeof(*desc) * pcnt); + aml_db->real_ports = pcnt; + } + +#ifdef CONFIG_OF + /* resource 0 for ddr register base */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) { + base = ioremap(res->start, res->end - res->start); + aml_db->ddr_reg = (void *)base; + } else { + pr_err("can't get ddr reg base\n"); + goto inval; + } + + /* resource 1 for pll register base */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res) { + base = ioremap(res->start, res->end - res->start); + aml_db->pll_reg = (void *)base; + } else { + pr_err("can't get ddr reg base\n"); + goto inval; + } + + aml_db->irq_num = of_irq_get(node, 0); + if (of_get_property(node, "interrupt-names", NULL)) { + r = of_property_read_string(node, "interrupt-names", &irq_name); + if (!r) { + r = request_irq(aml_db->irq_num, dmc_irq_handler, + IRQF_SHARED, irq_name, (void *)aml_db); + } + } + if (r < 0) { + pr_info("request irq failed:%d\n", aml_db->irq_num); + goto inval; + } +#endif + aml_db->clock_count = DEFAULT_CLK_CNT; + if (aml_db->cpu_type <= MESON_CPU_MAJOR_ID_GXTVBB) + aml_db->ops = &gx_ddr_bw_ops; + else if ((aml_db->cpu_type <= MESON_CPU_MAJOR_ID_TXHD) && + (aml_db->cpu_type >= MESON_CPU_MAJOR_ID_GXL)) + aml_db->ops = &gxl_ddr_bw_ops; + else if (aml_db->cpu_type >= MESON_CPU_MAJOR_ID_G12A) + aml_db->ops = &g12_ddr_bw_ops; + else { + pr_err("%s, can't find ops for cpu type:%d\n", + __func__, aml_db->cpu_type); + goto inval; + } + + if (aml_db->ops->init) + aml_db->ops->init(aml_db); + r = class_register(&aml_ddr_class); + if (r) + pr_info("%s, class regist failed\n", __func__); + return 0; +inval: + kfree(aml_db->port_desc); + kfree(aml_db); + aml_db = NULL; + + return -EINVAL; +} + +static int ddr_bandwidth_remove(struct platform_device *pdev) +{ + if (aml_db) { + class_destroy(&aml_ddr_class); + free_irq(aml_db->irq_num, aml_db); + kfree(aml_db->port_desc); + kfree(aml_db); + iounmap(aml_db->ddr_reg); + iounmap(aml_db->pll_reg); + aml_db = NULL; + } + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id aml_ddr_bandwidth_dt_match[] = { + { + .compatible = "amlogic, ddr-bandwidth", + } +}; +#endif + +static struct platform_driver ddr_bandwidth_driver = { + .driver = { + .name = "amlogic, ddr-bandwidth", + .owner = THIS_MODULE, + #ifdef CONFIG_OF + .of_match_table = aml_ddr_bandwidth_dt_match, + #endif + }, + .probe = ddr_bandwidth_probe, + .remove = ddr_bandwidth_remove, +}; + +static int __init ddr_bandwidth_init(void) +{ + return platform_driver_register(&ddr_bandwidth_driver); +} + +static void __exit ddr_bandwidth_exit(void) +{ + platform_driver_unregister(&ddr_bandwidth_driver); +} + +subsys_initcall(ddr_bandwidth_init); +module_exit(ddr_bandwidth_exit); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/amlogic/ddr_window/ddr_window.c b/drivers/amlogic/ddr_tool/ddr_window.c similarity index 99% rename from drivers/amlogic/ddr_window/ddr_window.c rename to drivers/amlogic/ddr_tool/ddr_window.c index f6f62324b183..39f6db59e84e 100644 --- a/drivers/amlogic/ddr_window/ddr_window.c +++ b/drivers/amlogic/ddr_tool/ddr_window.c @@ -1,5 +1,5 @@ /* - * drivers/amlogic/ddr_window/ddr_window.c + * drivers/amlogic/ddr_tool/ddr_window.c * * Copyright (C) 2017 Amlogic, Inc. All rights reserved. * diff --git a/drivers/amlogic/ddr_window/Kconfig b/drivers/amlogic/ddr_window/Kconfig deleted file mode 100644 index 62723358dc84..000000000000 --- a/drivers/amlogic/ddr_window/Kconfig +++ /dev/null @@ -1,5 +0,0 @@ -config AMLOGIC_DDR_WINDOW_TOOL - tristate "Meson cpu ddr window test tool" - default n - help - This add ddr window test tools diff --git a/drivers/amlogic/ddr_window/Makefile b/drivers/amlogic/ddr_window/Makefile deleted file mode 100644 index f428bcb5e495..000000000000 --- a/drivers/amlogic/ddr_window/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_AMLOGIC_DDR_WINDOW_TOOL) += ddr_window.o diff --git a/include/linux/amlogic/aml_ddr_bandwidth.h b/include/linux/amlogic/aml_ddr_bandwidth.h new file mode 100644 index 000000000000..e9efe9a33d36 --- /dev/null +++ b/include/linux/amlogic/aml_ddr_bandwidth.h @@ -0,0 +1,125 @@ +/* + * include/linux/amlogic/aml_ddr_bandwidth.h + * + * Copyright (C) 2017 Amlogic, Inc. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + */ + +#ifndef __AML_DDR_BANDWIDTH_H__ +#define __AML_DDR_BANDWIDTH_H__ + + +#define DEFAULT_CLK_CNT 12000000 +#define DEFAULT_XTAL_FREQ 24000000 + +#define DMC_QOS_IRQ (1 << 30) +#define MAX_CHANNEL 4 +#define MAX_PORTS 256 +#define MAX_NAME 15 +#define PORT_MAJOR 32 + +/* + * register offset for chips before g12 + */ +#define DMC_MON_CTRL1 (0x25 << 2) +#define DMC_MON_CTRL2 (0x26 << 2) +#define DMC_MON_CTRL3 (0x27 << 2) +#define DMC_MON_ALL_REQ_CNT (0x28 << 2) +#define DMC_MON_ALL_GRANT_CNT (0x29 << 2) +#define DMC_MON_ONE_GRANT_CNT (0x2a << 2) +#define DMC_MON_SEC_GRANT_CNT (0x2b << 2) +#define DMC_MON_THD_GRANT_CNT (0x2c << 2) +#define DMC_MON_FOR_GRANT_CNT (0x2d << 2) + +#define DMC_MON_CTRL4 (0x18 << 2) +#define DMC_MON_CTRL5 (0x19 << 2) +#define DMC_MON_CTRL6 (0x1a << 2) + +/* + * register offset for g12a + */ +#define DMC_MON_G12_CTRL0 (0x20 << 2) +#define DMC_MON_G12_CTRL1 (0x21 << 2) +#define DMC_MON_G12_CTRL2 (0x22 << 2) +#define DMC_MON_G12_CTRL3 (0x23 << 2) +#define DMC_MON_G12_CTRL4 (0x24 << 2) +#define DMC_MON_G12_CTRL5 (0x25 << 2) +#define DMC_MON_G12_CTRL6 (0x26 << 2) +#define DMC_MON_G12_CTRL7 (0x27 << 2) +#define DMC_MON_G12_CTRL8 (0x28 << 2) + +#define DMC_MON_G12_ALL_REQ_CNT (0x29 << 2) +#define DMC_MON_G12_ALL_GRANT_CNT (0x2a << 2) +#define DMC_MON_G12_ONE_GRANT_CNT (0x2b << 2) +#define DMC_MON_G12_SEC_GRANT_CNT (0x2c << 2) +#define DMC_MON_G12_THD_GRANT_CNT (0x2d << 2) +#define DMC_MON_G12_FOR_GRANT_CNT (0x2e << 2) +#define DMC_MON_G12_TIMER (0x2f << 2) + +/* data structure */ +#define DDR_BANDWIDTH_DEBUG 1 + +struct ddr_bandwidth; + +struct ddr_grant { + unsigned int all_grant, all_req; + unsigned int channel_grant[MAX_CHANNEL]; +}; + +struct ddr_bandwidth_ops { + void (*init)(struct ddr_bandwidth *db); + void (*config_port)(struct ddr_bandwidth *db, int channel, int port); + int (*handle_irq)(struct ddr_bandwidth *db, struct ddr_grant *dg); + void (*bandwidth_enable)(struct ddr_bandwidth *db); + unsigned long (*get_freq)(struct ddr_bandwidth *db); +#if DDR_BANDWIDTH_DEBUG + int (*dump_reg)(struct ddr_bandwidth *db, char *buf); +#endif +}; + +struct ddr_port_desc { + char port_name[MAX_NAME]; + unsigned char port_id; +}; + +struct ddr_bandwidth { + void __iomem *ddr_reg; + void __iomem *pll_reg; + struct class *class; + unsigned short cpu_type; + unsigned short real_ports; + unsigned int irq_num; + unsigned int clock_count; + unsigned int channels; + unsigned int port[MAX_CHANNEL]; + unsigned int bandwidth[MAX_CHANNEL]; + unsigned int total_usage; + unsigned int total_bandwidth; + struct ddr_port_desc *port_desc; + struct ddr_bandwidth_ops *ops; +}; + +#ifdef CONFIG_AMLOGIC_DDR_BANDWIDTH +extern unsigned int aml_get_ddr_usage(void); +extern struct ddr_bandwidth_ops g12_ddr_bw_ops; +extern struct ddr_bandwidth_ops gx_ddr_bw_ops; +extern struct ddr_bandwidth_ops gxl_ddr_bw_ops; +extern int ddr_find_port_desc(int cpu_type, struct ddr_port_desc **desc); +#else +static inline unsigned int aml_get_ddr_usage(void) +{ + return 0; +} +#endif + +#endif /* __AML_DDR_BANDWIDTH_H__ */