From: Tang Yun ping Date: Thu, 9 Apr 2015 03:36:59 +0000 (+0800) Subject: RK3368 DDR: add support 3368 ddr change freq function X-Git-Tag: firefly_0821_release~4158^2~225 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=860a4ab3430d0c83cb56c82582f0ee0c871f7daf;p=firefly-linux-kernel-4.4.55.git RK3368 DDR: add support 3368 ddr change freq function note:need using new trustimage and uboot for support ddr change freq Signed-off-by: Tang Yun ping --- diff --git a/arch/arm/mach-rockchip/ddr_freq.c b/arch/arm/mach-rockchip/ddr_freq.c index b5b31e02cd79..66b6e2afe108 100644 --- a/arch/arm/mach-rockchip/ddr_freq.c +++ b/arch/arm/mach-rockchip/ddr_freq.c @@ -179,19 +179,25 @@ static void ddrfreq_mode(bool auto_self_refresh, unsigned long target_rate, char } if (target_rate != dvfs_clk_get_last_set_rate(ddr.clk_dvfs_node)) { - freq_limit_en = dvfs_clk_get_limit(clk_cpu_dvfs_node, &min_rate, &max_rate); + if (clk_cpu_dvfs_node) { + freq_limit_en = dvfs_clk_get_limit(clk_cpu_dvfs_node, + &min_rate, + &max_rate); - dvfs_clk_enable_limit(clk_cpu_dvfs_node, 600000000, -1); + dvfs_clk_enable_limit(clk_cpu_dvfs_node, 600000000, -1); + } if (dvfs_clk_set_rate(ddr.clk_dvfs_node, target_rate) == 0) { target_rate = dvfs_clk_get_rate(ddr.clk_dvfs_node); auto_freq_update_index(target_rate); dprintk(DEBUG_DDR, "change freq to %lu MHz when %s\n", target_rate / MHZ, name); } - - if (freq_limit_en) { - dvfs_clk_enable_limit(clk_cpu_dvfs_node, min_rate, max_rate); - } else { - dvfs_clk_disable_limit(clk_cpu_dvfs_node); + if (clk_cpu_dvfs_node) { + if (freq_limit_en) { + dvfs_clk_enable_limit(clk_cpu_dvfs_node, + min_rate, max_rate); + } else { + dvfs_clk_disable_limit(clk_cpu_dvfs_node); + } } } } @@ -931,10 +937,6 @@ static int ddrfreq_init(void) #endif clk_cpu_dvfs_node = clk_get_dvfs_node("clk_core"); - if (!clk_cpu_dvfs_node){ - return -EINVAL; - } - memset(&ddr, 0x00, sizeof(ddr)); ddr.clk_dvfs_node = clk_get_dvfs_node("clk_ddr"); if (!ddr.clk_dvfs_node){ diff --git a/arch/arm64/boot/dts/rk3368.dtsi b/arch/arm64/boot/dts/rk3368.dtsi index ce2cd4969821..a1dc9900d058 100755 --- a/arch/arm64/boot/dts/rk3368.dtsi +++ b/arch/arm64/boot/dts/rk3368.dtsi @@ -151,6 +151,11 @@ <0x0 0xffb72000 0 0x1000>; }; + ddrpctl: syscon@ff610000 { + compatible = "rockchip,rk3368-ddrpctl", "syscon"; + reg = <0x0 0xff610000 0x0 0x400>; + }; + pmu: syscon@ff730000 { compatible = "rockchip,rk3368-pmu", "rockchip,pmu", "syscon"; reg = <0x0 0xff730000 0x0 0x1000>; @@ -177,6 +182,11 @@ reg = <0x0 0xff770000 0x0 0x1000>; }; + msch: syscon@ffac0000 { + compatible = "rockchip,rk3368-msch", "rockchip,msch", "syscon"; + reg = <0x0 0xffac0000 0x0 0x3000>; + }; + arm-pmu { compatible = "arm,armv8-pmuv3"; interrupts = , @@ -566,6 +576,14 @@ mboxes = <&mbox 0 &mbox 1>; }; + ddr { + compatible = "rockchip,rk3368-ddr"; + status = "okay"; + rockchip,ddrpctl = <&ddrpctl>; + rockchip,grf = <&grf>; + rockchip,msch = <&msch>; + }; + rockchip_clocks_init: clocks-init{ compatible = "rockchip,clocks-init"; rockchip,clocks-init-parent = diff --git a/arch/arm64/configs/rockchip_defconfig b/arch/arm64/configs/rockchip_defconfig index b82f50f022e0..6b582f7f7329 100644 --- a/arch/arm64/configs/rockchip_defconfig +++ b/arch/arm64/configs/rockchip_defconfig @@ -570,6 +570,8 @@ CONFIG_ROCKCHIP_MAILBOX=y CONFIG_SCPI_PROTOCOL=y CONFIG_ROCKCHIP_IOMMU=y CONFIG_ROCKCHIP_IOVMM=y +CONFIG_PM_DEVFREQ=y +CONFIG_ROCKCHIP_RK3368_DDR_FREQ=y CONFIG_IIO=y CONFIG_ROCKCHIP_ADC=y CONFIG_PWM=y diff --git a/arch/arm64/mach-rockchip/Makefile b/arch/arm64/mach-rockchip/Makefile index 40f10e984282..a07df9bf13cc 100644 --- a/arch/arm64/mach-rockchip/Makefile +++ b/arch/arm64/mach-rockchip/Makefile @@ -3,11 +3,13 @@ obj-y += ../../arm/mach-rockchip/cpu.o obj-y += ../../arm/mach-rockchip/efuse.o obj-y += ../../arm/mach-rockchip/pvtm.o obj-y += ../../arm/mach-rockchip/rk_system_status.o +obj-y += ../../arm/mach-rockchip/ddr_freq.o obj-$(CONFIG_PM) += ../../arm/mach-rockchip/rockchip_pm.o obj-$(CONFIG_RK_LAST_LOG) += ../../arm/mach-rockchip/last_log.o obj-$(CONFIG_DVFS) += ../../arm/mach-rockchip/dvfs.o obj-$(CONFIG_BLOCK_RKNAND) += ../../arm/mach-rockchip/rknandbase.o obj-$(CONFIG_RK_PL330_DMA_TEST) += ../../arm/mach-rockchip/dma_memcpy_test.o obj-$(CONFIG_RK_VCODEC) += ../../arm/mach-rockchip/vcodec_service.o +obj-$(CONFIG_DDR_TEST) += ../../arm/mach-rockchip/ddr_test.o obj-$(CONFIG_RK_PM_TESTS) += ../../arm/mach-rockchip/rk_pm_tests/ obj-$(CONFIG_ROCK_CHIP_SOC_CAMERA) += ../../arm/mach-rockchip/rk_camera.o diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 0f079be13305..eac49013836f 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -65,6 +65,14 @@ config DEVFREQ_GOV_USERSPACE comment "DEVFREQ Drivers" +config ROCKCHIP_RK3368_DDR_FREQ + bool "ROCKCHIP RK3368 DDR FREQ Driver" + depends on ARCH_ROCKCHIP + help + This adds the rockchip ddr change freq driver for rk3368 it + used MCU to change ddr freq,and mast enadle rockchip mailbox + and scpi. + config ARM_EXYNOS4_BUS_DEVFREQ bool "ARM Exynos4210/4212/4412 Memory Bus DEVFREQ Driver" depends on CPU_EXYNOS4210 || CPU_EXYNOS4212 || CPU_EXYNOS4412 diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 8c464234f7e7..cc3254b9406f 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -4,5 +4,6 @@ obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE) += governor_performance.o obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o +obj-$(CONFIG_ROCKCHIP_RK3368_DDR_FREQ) += ddr_rk3368.o # DEVFREQ Drivers obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos4_bus.o diff --git a/drivers/devfreq/ddr_rk3368.c b/drivers/devfreq/ddr_rk3368.c new file mode 100644 index 000000000000..4c6614c8f162 --- /dev/null +++ b/drivers/devfreq/ddr_rk3368.c @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2015, Fuzhou Rockchip Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GRF_DDRC0_CON0 0x600 +#define GRF_SOC_STATUS5 0x494 +#define DDR_PCTL_TOGCNT_1U 0xc0 + +enum ddr_bandwidth_id { + ddrbw_wr_num = 0, + ddrbw_rd_num, + ddrbw_act_num, + ddrbw_time_num, + ddrbw_eff, + ddrbw_id_end +}; + +struct rockchip_ddr { + struct regmap *ddrpctl_regs; + struct regmap *msch_regs; + struct regmap *grf_regs; +}; + +static struct rockchip_ddr *ddr_data = NULL; + +static int _ddr_change_freq(u32 n_mhz) +{ + u32 ret; + + printk(KERN_DEBUG pr_fmt("In func %s,freq=%dMHz\n"), __func__, n_mhz); + if (scpi_ddr_set_clk_rate(n_mhz)) + pr_info("set ddr freq timeout\n"); + ret = scpi_ddr_get_clk_rate(); + printk(KERN_DEBUG pr_fmt("Func %s out,freq=%dMHz\n"), __func__, ret); + return ret; +} + +static long _ddr_round_rate(u32 n_mhz) +{ + return (n_mhz / 12) * 12; +} + +static int _ddr_recalc_rate(void) +{ + return (1000000 * scpi_ddr_get_clk_rate()); +} + +static void _ddr_set_auto_self_refresh(bool en) +{ + if (scpi_ddr_set_auto_self_refresh(en)) + printk(KERN_DEBUG pr_fmt("ddr set auto selfrefresh error\n")); +} + +static void ddr_monitor_start(void) +{ + u32 i; + + /* cpum, gpu probe */ + for (i = 1; i < 3; i++) { + regmap_write(ddr_data->msch_regs, 0x1000 + (0x400 * i) + 0x8, + 0x8); + regmap_write(ddr_data->msch_regs, 0x1000 + (0x400 * i) + 0xc, + 0x1); + regmap_write(ddr_data->msch_regs, 0x1000 + (0x400 * i) + 0x138, + 0x6); + regmap_write(ddr_data->msch_regs, 0x1000 + (0x400 * i) + 0x14c, + 0x10); + regmap_write(ddr_data->msch_regs, 0x1000 + (0x400 * i) + 0x160, + 0x8); + regmap_write(ddr_data->msch_regs, 0x1000 + (0x400 * i) + 0x174, + 0x10); + } + /* video, vio0, vio1 probe */ + for (i = 0; i < 3; i++) { + regmap_write(ddr_data->msch_regs, 0x2000 + (0x400 * i) + 0x8, + 0x8); + regmap_write(ddr_data->msch_regs, 0x2000 + (0x400 * i) + 0xc, + 0x1); + regmap_write(ddr_data->msch_regs, 0x2000 + (0x400 * i) + 0x138, + 0x6); + regmap_write(ddr_data->msch_regs, 0x2000 + (0x400 * i) + 0x14c, + 0x10); + regmap_write(ddr_data->msch_regs, 0x2000 + (0x400 * i) + 0x160, + 0x8); + regmap_write(ddr_data->msch_regs, 0x2000 + (0x400 * i) + 0x174, + 0x10); + } + /* dfi eff start */ + regmap_write(ddr_data->grf_regs, GRF_DDRC0_CON0, + ((0x3 << 5) << 16) | 0x3 << 5); + /*flash data */ + wmb(); + /* trigger statistic */ + for (i = 1; i < 3; i++) + regmap_write(ddr_data->msch_regs, 0x1000 + (0x400 * i) + 0x28, + 0x1); + for (i = 0; i < 3; i++) + regmap_write(ddr_data->msch_regs, 0x2000 + (0x400 * i) + 0x28, + 0x1); +} + +static void ddr_monitor_stop(void) +{ + /* dfi eff stop */ + regmap_write(ddr_data->grf_regs, GRF_DDRC0_CON0, + ((0x3 << 5) << 16) | 0x0 << 5); +} + +static void _ddr_bandwidth_get(struct ddr_bw_info *ddr_bw_ch0, + struct ddr_bw_info *ddr_bw_ch1) +{ + u32 ddr_bw_val[2][ddrbw_id_end], ddr_freq, dfi_freq; + u64 temp64; + int i, j; + u32 tmp32; + + if (!ddr_data) + return; + + ddr_monitor_stop(); + /* read dfi eff */ + for (j = 0; j < 2; j++) { + for (i = 0; i < ddrbw_eff; i++) { + regmap_read(ddr_data->grf_regs, + GRF_SOC_STATUS5 + 4 * i + j * 16, + &ddr_bw_val[j][i]); + } + } + if (!ddr_bw_val[0][ddrbw_time_num]) + goto end; + if (ddr_bw_ch0) { + regmap_read(ddr_data->ddrpctl_regs, DDR_PCTL_TOGCNT_1U, + &ddr_freq); + ddr_freq *= 2; + dfi_freq = ddr_freq / 2; + /* dfi eff */ + temp64 = ((u64) ddr_bw_val[0][0] + ddr_bw_val[0][1] + + ddr_bw_val[1][0] + ddr_bw_val[1][1]) * 2 * 100; + do_div(temp64, ddr_bw_val[0][ddrbw_time_num]); + ddr_bw_val[0][ddrbw_eff] = temp64; + ddr_bw_ch0->ddr_percent = temp64; + ddr_bw_ch0->ddr_time = + ddr_bw_val[0][ddrbw_time_num] / (dfi_freq * 1000); + /*unit:MB/s */ + ddr_bw_ch0->ddr_wr = (((u64) + (ddr_bw_val[0][ddrbw_wr_num] + + ddr_bw_val[1][ddrbw_wr_num]) * 8 * 4) * + dfi_freq) / ddr_bw_val[0][ddrbw_time_num]; + ddr_bw_ch0->ddr_rd = (((u64) + (ddr_bw_val[0][ddrbw_rd_num] + + ddr_bw_val[1][ddrbw_rd_num]) * 8 * 4) * + dfi_freq) / ddr_bw_val[0][ddrbw_time_num]; + ddr_bw_ch0->ddr_act = ddr_bw_val[0][ddrbw_act_num]; + ddr_bw_ch0->ddr_total = ddr_freq * 2 * 4; + /* noc unit:bype */ + regmap_read(ddr_data->msch_regs, 0x1400 + 0x178, &tmp32); + regmap_read(ddr_data->msch_regs, 0x1400 + 0x164, + &ddr_bw_ch0->cpum); + ddr_bw_ch0->cpum += (tmp32 << 16); + regmap_read(ddr_data->msch_regs, 0x1800 + 0x178, &tmp32); + regmap_read(ddr_data->msch_regs, 0x1800 + 0x164, + &ddr_bw_ch0->gpu); + ddr_bw_ch0->gpu += (tmp32 << 16); + ddr_bw_ch0->peri = 0; + regmap_read(ddr_data->msch_regs, 0x2000 + 0x178, &tmp32); + regmap_read(ddr_data->msch_regs, 0x2000 + 0x164, + &ddr_bw_ch0->video); + ddr_bw_ch0->video += (tmp32 << 16); + regmap_read(ddr_data->msch_regs, 0x2400 + 0x178, &tmp32); + regmap_read(ddr_data->msch_regs, 0x2400 + 0x164, + &ddr_bw_ch0->vio0); + ddr_bw_ch0->vio0 += (tmp32 << 16); + regmap_read(ddr_data->msch_regs, 0x2800 + 0x178, &tmp32); + regmap_read(ddr_data->msch_regs, 0x2800 + 0x164, + &ddr_bw_ch0->vio1); + ddr_bw_ch0->vio1 += (tmp32 << 16); + ddr_bw_ch0->vio2 = 0; + + /* B/s => MB/s */ + ddr_bw_ch0->cpum = + (u64) ddr_bw_ch0->cpum * dfi_freq / + ddr_bw_val[0][ddrbw_time_num]; + ddr_bw_ch0->gpu = + (u64) ddr_bw_ch0->gpu * dfi_freq / + ddr_bw_val[0][ddrbw_time_num]; + ddr_bw_ch0->peri = + (u64) ddr_bw_ch0->peri * dfi_freq / + ddr_bw_val[0][ddrbw_time_num]; + ddr_bw_ch0->video = + (u64) ddr_bw_ch0->video * dfi_freq / + ddr_bw_val[0][ddrbw_time_num]; + ddr_bw_ch0->vio0 = + (u64) ddr_bw_ch0->vio0 * dfi_freq / + ddr_bw_val[0][ddrbw_time_num]; + ddr_bw_ch0->vio1 = + (u64) ddr_bw_ch0->vio1 * dfi_freq / + ddr_bw_val[0][ddrbw_time_num]; + ddr_bw_ch0->vio2 = + (u64) ddr_bw_ch0->vio2 * dfi_freq / + ddr_bw_val[0][ddrbw_time_num]; + } +end: + ddr_monitor_start(); +} + +static void ddr_init(u32 dram_speed_bin, u32 freq) +{ + printk(KERN_DEBUG pr_fmt("In Func:%s,dram_speed_bin:%d,freq:%d\n"), + __func__, dram_speed_bin, freq); + if (scpi_ddr_init(dram_speed_bin, freq)) + pr_info("ddr init error\n"); + else + printk(KERN_DEBUG pr_fmt("%s out\n"), __func__); +} + +static int __init rockchip_ddr_probe(struct platform_device *pdev) +{ + struct device_node *np; + + np = pdev->dev.of_node; + ddr_data = + devm_kzalloc(&pdev->dev, sizeof(struct rockchip_ddr), GFP_KERNEL); + if (!ddr_data) { + dev_err(&pdev->dev, "no memory for state\n"); + return -ENOMEM; + } + /* ddrpctl */ + ddr_data->ddrpctl_regs = + syscon_regmap_lookup_by_phandle(np, "rockchip,ddrpctl"); + if (IS_ERR(ddr_data->ddrpctl_regs)) { + dev_err(&pdev->dev, "%s: could not find ddrpctl dt node\n", + __func__); + return -ENXIO; + } + + /* grf */ + ddr_data->grf_regs = + syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(ddr_data->grf_regs)) { + dev_err(&pdev->dev, "%s: could not find grf dt node\n", + __func__); + return -ENXIO; + } + /* msch */ + ddr_data->msch_regs = + syscon_regmap_lookup_by_phandle(np, "rockchip,msch"); + if (IS_ERR(ddr_data->msch_regs)) { + dev_err(&pdev->dev, "%s: could not find msch dt node\n", + __func__); + return -ENXIO; + } + + platform_set_drvdata(pdev, ddr_data); + ddr_change_freq = _ddr_change_freq; + ddr_round_rate = _ddr_round_rate; + ddr_set_auto_self_refresh = _ddr_set_auto_self_refresh; + ddr_bandwidth_get = _ddr_bandwidth_get; + ddr_recalc_rate = _ddr_recalc_rate; + ddr_init(DDR3_DEFAULT, 0); + pr_info("%s: success\n", __func__); + return 0; +} + +static const struct of_device_id rockchip_ddr_of_match[] __refdata = { + {.compatible = "rockchip,rk3368-ddr", .data = NULL,}, + {}, +}; + +static struct platform_driver rockchip_ddr_driver = { + .driver = { + .name = "rockchip_ddr", + .of_match_table = rockchip_ddr_of_match, + }, +}; + +static int __init rockchip_ddr_init(void) +{ + pr_info("rockchip_ddr_init\n"); + return platform_driver_probe(&rockchip_ddr_driver, rockchip_ddr_probe); +} + +device_initcall(rockchip_ddr_init); diff --git a/drivers/mailbox/rockchip_mailbox.c b/drivers/mailbox/rockchip_mailbox.c index 751903894b93..ca8cb4d1aeb3 100644 --- a/drivers/mailbox/rockchip_mailbox.c +++ b/drivers/mailbox/rockchip_mailbox.c @@ -271,8 +271,9 @@ static struct platform_driver rockchip_mbox_driver = { .of_match_table = of_match_ptr(rockchip_mbox_of_match), }, }; -module_platform_driver(rockchip_mbox_driver); -MODULE_AUTHOR("Addy Ke "); -MODULE_DESCRIPTION("Rockchip Mailbox Driver"); -MODULE_LICENSE("GPL v2"); +static int __init rockchip_mbox_init(void) +{ + return platform_driver_register(&rockchip_mbox_driver); +} +subsys_initcall(rockchip_mbox_init); diff --git a/drivers/mailbox/scpi_protocol.c b/drivers/mailbox/scpi_protocol.c index f9f1df7bae47..78885289123f 100644 --- a/drivers/mailbox/scpi_protocol.c +++ b/drivers/mailbox/scpi_protocol.c @@ -567,15 +567,16 @@ exit: return ret; } -static struct platform_driver mobx_scpi_driver = { +static struct platform_driver mbox_scpi_driver = { .probe = mobx_scpi_probe, .driver = { .name = "mbox-scpi", .of_match_table = of_match_ptr(mobx_scpi_of_match), }, }; -module_platform_driver(mobx_scpi_driver); -MODULE_AUTHOR("Addy Ke "); -MODULE_DESCRIPTION("Rockchip SCPI Driver"); -MODULE_LICENSE("GPL v2"); +static int __init rockchip_mbox_scpi_init(void) +{ + return platform_driver_register(&mbox_scpi_driver); +} +subsys_initcall(rockchip_mbox_scpi_init);