13d0407baSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 23d0407baSopenharmony_ci/* 33d0407baSopenharmony_ci * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd 43d0407baSopenharmony_ci * Author: Lin Huang <hl@rock-chips.com> 53d0407baSopenharmony_ci */ 63d0407baSopenharmony_ci 73d0407baSopenharmony_ci#include <linux/clk.h> 83d0407baSopenharmony_ci#include <linux/devfreq-event.h> 93d0407baSopenharmony_ci#include <linux/kernel.h> 103d0407baSopenharmony_ci#include <linux/err.h> 113d0407baSopenharmony_ci#include <linux/init.h> 123d0407baSopenharmony_ci#include <linux/io.h> 133d0407baSopenharmony_ci#include <linux/mfd/syscon.h> 143d0407baSopenharmony_ci#include <linux/module.h> 153d0407baSopenharmony_ci#include <linux/platform_device.h> 163d0407baSopenharmony_ci#include <linux/regmap.h> 173d0407baSopenharmony_ci#include <linux/slab.h> 183d0407baSopenharmony_ci#include <linux/list.h> 193d0407baSopenharmony_ci#include <linux/of.h> 203d0407baSopenharmony_ci 213d0407baSopenharmony_ci#include <soc/rockchip/rk3399_grf.h> 223d0407baSopenharmony_ci 233d0407baSopenharmony_ci#define PX30_PMUGRF_OS_REG2 0x208 243d0407baSopenharmony_ci#define PX30_PMUGRF_OS_REG3 0x20c 253d0407baSopenharmony_ci 263d0407baSopenharmony_ci#define RK3128_GRF_SOC_CON0 0x140 273d0407baSopenharmony_ci#define RK3128_GRF_OS_REG1 0x1cc 283d0407baSopenharmony_ci#define RK3128_GRF_DFI_WRNUM 0x220 293d0407baSopenharmony_ci#define RK3128_GRF_DFI_RDNUM 0x224 303d0407baSopenharmony_ci#define RK3128_GRF_DFI_TIMERVAL 0x22c 313d0407baSopenharmony_ci#define RK3128_DDR_MONITOR_EN ((1 << (16 + 6)) + (1 << 6)) 323d0407baSopenharmony_ci#define RK3128_DDR_MONITOR_DISB ((1 << (16 + 6)) + (0 << 6)) 333d0407baSopenharmony_ci 343d0407baSopenharmony_ci#define RK3288_PMU_SYS_REG2 0x9c 353d0407baSopenharmony_ci#define RK3288_GRF_SOC_CON4 0x254 363d0407baSopenharmony_ci#define RK3288_GRF_SOC_STATUS(n) (0x280 + (n)*4) 373d0407baSopenharmony_ci#define RK3288_DFI_EN (0x30003 << 14) 383d0407baSopenharmony_ci#define RK3288_DFI_DIS (0x30000 << 14) 393d0407baSopenharmony_ci#define RK3288_LPDDR_SEL (0x10001 << 13) 403d0407baSopenharmony_ci#define RK3288_DDR3_SEL (0x10000 << 13) 413d0407baSopenharmony_ci 423d0407baSopenharmony_ci#define RK3328_GRF_OS_REG2 0x5d0 433d0407baSopenharmony_ci 443d0407baSopenharmony_ci#define RK3368_GRF_DDRC0_CON0 0x600 453d0407baSopenharmony_ci#define RK3368_GRF_SOC_STATUS5 0x494 463d0407baSopenharmony_ci#define RK3368_GRF_SOC_STATUS6 0x498 473d0407baSopenharmony_ci#define RK3368_GRF_SOC_STATUS8 0x4a0 483d0407baSopenharmony_ci#define RK3368_GRF_SOC_STATUS9 0x4a4 493d0407baSopenharmony_ci#define RK3368_GRF_SOC_STATUS10 0x4a8 503d0407baSopenharmony_ci#define RK3368_DFI_EN (0x30003 << 5) 513d0407baSopenharmony_ci#define RK3368_DFI_DIS (0x30000 << 5) 523d0407baSopenharmony_ci 533d0407baSopenharmony_ci#define MAX_DMC_NUM_CH 2 543d0407baSopenharmony_ci#define READ_DRAMTYPE_INFO(n) (((n) >> 13) & 0x7) 553d0407baSopenharmony_ci#define READ_CH_INFO(n) (((n) >> 28) & 0x3) 563d0407baSopenharmony_ci#define READ_DRAMTYPE_INFO_V3(n, m) ((((n) >> 13) & 0x7) | ((((m) >> 12) & 0x3) << 3)) 573d0407baSopenharmony_ci#define READ_SYSREG_VERSION(m) (((m) >> 28) & 0xf) 583d0407baSopenharmony_ci/* DDRMON_CTRL */ 593d0407baSopenharmony_ci#define DDRMON_CTRL 0x04 603d0407baSopenharmony_ci#define CLR_DDRMON_CTRL (0x3f0000 << 0) 613d0407baSopenharmony_ci#define DDR4_EN (0x10001 << 5) 623d0407baSopenharmony_ci#define LPDDR4_EN (0x10001 << 4) 633d0407baSopenharmony_ci#define HARDWARE_EN (0x10001 << 3) 643d0407baSopenharmony_ci#define LPDDR2_3_EN (0x10001 << 2) 653d0407baSopenharmony_ci#define SOFTWARE_EN (0x10001 << 1) 663d0407baSopenharmony_ci#define SOFTWARE_DIS (0x10000 << 1) 673d0407baSopenharmony_ci#define TIME_CNT_EN (0x10001 << 0) 683d0407baSopenharmony_ci 693d0407baSopenharmony_ci#define DDRMON_CH0_COUNT_NUM 0x28 703d0407baSopenharmony_ci#define DDRMON_CH0_DFI_ACCESS_NUM 0x2c 713d0407baSopenharmony_ci#define DDRMON_CH1_COUNT_NUM 0x3c 723d0407baSopenharmony_ci#define DDRMON_CH1_DFI_ACCESS_NUM 0x40 733d0407baSopenharmony_ci 743d0407baSopenharmony_ci/* pmu grf */ 753d0407baSopenharmony_ci#define PMUGRF_OS_REG2 0x308 763d0407baSopenharmony_ci 773d0407baSopenharmony_cienum { DDR4 = 0, DDR3 = 3, LPDDR2 = 5, LPDDR3 = 6, LPDDR4 = 7, LPDDR4X = 8, UNUSED = 0xFF }; 783d0407baSopenharmony_ci 793d0407baSopenharmony_cistruct dmc_usage { 803d0407baSopenharmony_ci u32 access; 813d0407baSopenharmony_ci u32 total; 823d0407baSopenharmony_ci}; 833d0407baSopenharmony_ci 843d0407baSopenharmony_ci/* 853d0407baSopenharmony_ci * The dfi controller can monitor DDR load. It has an upper and lower threshold 863d0407baSopenharmony_ci * for the operating points. Whenever the usage leaves these bounds an event is 873d0407baSopenharmony_ci * generated to indicate the DDR frequency should be changed. 883d0407baSopenharmony_ci */ 893d0407baSopenharmony_cistruct rockchip_dfi { 903d0407baSopenharmony_ci struct devfreq_event_dev *edev; 913d0407baSopenharmony_ci struct devfreq_event_desc *desc; 923d0407baSopenharmony_ci struct dmc_usage ch_usage[MAX_DMC_NUM_CH]; 933d0407baSopenharmony_ci struct device *dev; 943d0407baSopenharmony_ci void __iomem *regs; 953d0407baSopenharmony_ci struct regmap *regmap_pmu; 963d0407baSopenharmony_ci struct regmap *regmap_grf; 973d0407baSopenharmony_ci struct regmap *regmap_pmugrf; 983d0407baSopenharmony_ci struct clk *clk; 993d0407baSopenharmony_ci u32 dram_type; 1003d0407baSopenharmony_ci /* 1013d0407baSopenharmony_ci * available mask, 1: available, 0: not available 1023d0407baSopenharmony_ci * each bit represent a channel 1033d0407baSopenharmony_ci */ 1043d0407baSopenharmony_ci u32 ch_msk; 1053d0407baSopenharmony_ci}; 1063d0407baSopenharmony_ci 1073d0407baSopenharmony_cistatic void rk3128_dfi_start_hardware_counter(struct devfreq_event_dev *edev) 1083d0407baSopenharmony_ci{ 1093d0407baSopenharmony_ci struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); 1103d0407baSopenharmony_ci 1113d0407baSopenharmony_ci regmap_write(info->regmap_grf, RK3128_GRF_SOC_CON0, RK3128_DDR_MONITOR_EN); 1123d0407baSopenharmony_ci} 1133d0407baSopenharmony_ci 1143d0407baSopenharmony_cistatic void rk3128_dfi_stop_hardware_counter(struct devfreq_event_dev *edev) 1153d0407baSopenharmony_ci{ 1163d0407baSopenharmony_ci struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); 1173d0407baSopenharmony_ci 1183d0407baSopenharmony_ci regmap_write(info->regmap_grf, RK3128_GRF_SOC_CON0, RK3128_DDR_MONITOR_DISB); 1193d0407baSopenharmony_ci} 1203d0407baSopenharmony_ci 1213d0407baSopenharmony_cistatic int rk3128_dfi_disable(struct devfreq_event_dev *edev) 1223d0407baSopenharmony_ci{ 1233d0407baSopenharmony_ci rk3128_dfi_stop_hardware_counter(edev); 1243d0407baSopenharmony_ci 1253d0407baSopenharmony_ci return 0; 1263d0407baSopenharmony_ci} 1273d0407baSopenharmony_ci 1283d0407baSopenharmony_cistatic int rk3128_dfi_enable(struct devfreq_event_dev *edev) 1293d0407baSopenharmony_ci{ 1303d0407baSopenharmony_ci rk3128_dfi_start_hardware_counter(edev); 1313d0407baSopenharmony_ci 1323d0407baSopenharmony_ci return 0; 1333d0407baSopenharmony_ci} 1343d0407baSopenharmony_ci 1353d0407baSopenharmony_cistatic int rk3128_dfi_set_event(struct devfreq_event_dev *edev) 1363d0407baSopenharmony_ci{ 1373d0407baSopenharmony_ci return 0; 1383d0407baSopenharmony_ci} 1393d0407baSopenharmony_ci 1403d0407baSopenharmony_cistatic int rk3128_dfi_get_event(struct devfreq_event_dev *edev, struct devfreq_event_data *edata) 1413d0407baSopenharmony_ci{ 1423d0407baSopenharmony_ci struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); 1433d0407baSopenharmony_ci unsigned long flags; 1443d0407baSopenharmony_ci u32 dfi_wr, dfi_rd, dfi_timer; 1453d0407baSopenharmony_ci 1463d0407baSopenharmony_ci local_irq_save(flags); 1473d0407baSopenharmony_ci 1483d0407baSopenharmony_ci rk3128_dfi_stop_hardware_counter(edev); 1493d0407baSopenharmony_ci 1503d0407baSopenharmony_ci regmap_read(info->regmap_grf, RK3128_GRF_DFI_WRNUM, &dfi_wr); 1513d0407baSopenharmony_ci regmap_read(info->regmap_grf, RK3128_GRF_DFI_RDNUM, &dfi_rd); 1523d0407baSopenharmony_ci regmap_read(info->regmap_grf, RK3128_GRF_DFI_TIMERVAL, &dfi_timer); 1533d0407baSopenharmony_ci 1543d0407baSopenharmony_ci edata->load_count = (dfi_wr + dfi_rd) * 0x4; 1553d0407baSopenharmony_ci edata->total_count = dfi_timer; 1563d0407baSopenharmony_ci 1573d0407baSopenharmony_ci rk3128_dfi_start_hardware_counter(edev); 1583d0407baSopenharmony_ci 1593d0407baSopenharmony_ci local_irq_restore(flags); 1603d0407baSopenharmony_ci 1613d0407baSopenharmony_ci return 0; 1623d0407baSopenharmony_ci} 1633d0407baSopenharmony_ci 1643d0407baSopenharmony_cistatic const struct devfreq_event_ops rk3128_dfi_ops = { 1653d0407baSopenharmony_ci .disable = rk3128_dfi_disable, 1663d0407baSopenharmony_ci .enable = rk3128_dfi_enable, 1673d0407baSopenharmony_ci .get_event = rk3128_dfi_get_event, 1683d0407baSopenharmony_ci .set_event = rk3128_dfi_set_event, 1693d0407baSopenharmony_ci}; 1703d0407baSopenharmony_ci 1713d0407baSopenharmony_cistatic void rk3288_dfi_start_hardware_counter(struct devfreq_event_dev *edev) 1723d0407baSopenharmony_ci{ 1733d0407baSopenharmony_ci struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); 1743d0407baSopenharmony_ci 1753d0407baSopenharmony_ci regmap_write(info->regmap_grf, RK3288_GRF_SOC_CON4, RK3288_DFI_EN); 1763d0407baSopenharmony_ci} 1773d0407baSopenharmony_ci 1783d0407baSopenharmony_cistatic void rk3288_dfi_stop_hardware_counter(struct devfreq_event_dev *edev) 1793d0407baSopenharmony_ci{ 1803d0407baSopenharmony_ci struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); 1813d0407baSopenharmony_ci 1823d0407baSopenharmony_ci regmap_write(info->regmap_grf, RK3288_GRF_SOC_CON4, RK3288_DFI_DIS); 1833d0407baSopenharmony_ci} 1843d0407baSopenharmony_ci 1853d0407baSopenharmony_cistatic int rk3288_dfi_disable(struct devfreq_event_dev *edev) 1863d0407baSopenharmony_ci{ 1873d0407baSopenharmony_ci rk3288_dfi_stop_hardware_counter(edev); 1883d0407baSopenharmony_ci 1893d0407baSopenharmony_ci return 0; 1903d0407baSopenharmony_ci} 1913d0407baSopenharmony_ci 1923d0407baSopenharmony_cistatic int rk3288_dfi_enable(struct devfreq_event_dev *edev) 1933d0407baSopenharmony_ci{ 1943d0407baSopenharmony_ci rk3288_dfi_start_hardware_counter(edev); 1953d0407baSopenharmony_ci 1963d0407baSopenharmony_ci return 0; 1973d0407baSopenharmony_ci} 1983d0407baSopenharmony_ci 1993d0407baSopenharmony_cistatic int rk3288_dfi_set_event(struct devfreq_event_dev *edev) 2003d0407baSopenharmony_ci{ 2013d0407baSopenharmony_ci return 0; 2023d0407baSopenharmony_ci} 2033d0407baSopenharmony_ci 2043d0407baSopenharmony_cistatic int rk3288_dfi_get_busier_ch(struct devfreq_event_dev *edev) 2053d0407baSopenharmony_ci{ 2063d0407baSopenharmony_ci struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); 2073d0407baSopenharmony_ci u32 tmp, max = 0; 2083d0407baSopenharmony_ci u32 i, busier_ch = 0; 2093d0407baSopenharmony_ci u32 rd_count, wr_count, total_count; 2103d0407baSopenharmony_ci 2113d0407baSopenharmony_ci rk3288_dfi_stop_hardware_counter(edev); 2123d0407baSopenharmony_ci 2133d0407baSopenharmony_ci /* Find out which channel is busier */ 2143d0407baSopenharmony_ci for (i = 0; i < MAX_DMC_NUM_CH; i++) { 2153d0407baSopenharmony_ci if (!(info->ch_msk & BIT(i))) { 2163d0407baSopenharmony_ci continue; 2173d0407baSopenharmony_ci } 2183d0407baSopenharmony_ci regmap_read(info->regmap_grf, RK3288_GRF_SOC_STATUS(0xb + i * 0x4), &wr_count); 2193d0407baSopenharmony_ci regmap_read(info->regmap_grf, RK3288_GRF_SOC_STATUS(0xc + i * 0x4), &rd_count); 2203d0407baSopenharmony_ci regmap_read(info->regmap_grf, RK3288_GRF_SOC_STATUS(0xe + i * 0x4), &total_count); 2213d0407baSopenharmony_ci info->ch_usage[i].access = (wr_count + rd_count) * 0x4; 2223d0407baSopenharmony_ci info->ch_usage[i].total = total_count; 2233d0407baSopenharmony_ci tmp = info->ch_usage[i].access; 2243d0407baSopenharmony_ci if (tmp > max) { 2253d0407baSopenharmony_ci busier_ch = i; 2263d0407baSopenharmony_ci max = tmp; 2273d0407baSopenharmony_ci } 2283d0407baSopenharmony_ci } 2293d0407baSopenharmony_ci rk3288_dfi_start_hardware_counter(edev); 2303d0407baSopenharmony_ci 2313d0407baSopenharmony_ci return busier_ch; 2323d0407baSopenharmony_ci} 2333d0407baSopenharmony_ci 2343d0407baSopenharmony_cistatic int rk3288_dfi_get_event(struct devfreq_event_dev *edev, struct devfreq_event_data *edata) 2353d0407baSopenharmony_ci{ 2363d0407baSopenharmony_ci struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); 2373d0407baSopenharmony_ci int busier_ch; 2383d0407baSopenharmony_ci unsigned long flags; 2393d0407baSopenharmony_ci 2403d0407baSopenharmony_ci local_irq_save(flags); 2413d0407baSopenharmony_ci busier_ch = rk3288_dfi_get_busier_ch(edev); 2423d0407baSopenharmony_ci local_irq_restore(flags); 2433d0407baSopenharmony_ci 2443d0407baSopenharmony_ci edata->load_count = info->ch_usage[busier_ch].access; 2453d0407baSopenharmony_ci edata->total_count = info->ch_usage[busier_ch].total; 2463d0407baSopenharmony_ci 2473d0407baSopenharmony_ci return 0; 2483d0407baSopenharmony_ci} 2493d0407baSopenharmony_ci 2503d0407baSopenharmony_cistatic const struct devfreq_event_ops rk3288_dfi_ops = { 2513d0407baSopenharmony_ci .disable = rk3288_dfi_disable, 2523d0407baSopenharmony_ci .enable = rk3288_dfi_enable, 2533d0407baSopenharmony_ci .get_event = rk3288_dfi_get_event, 2543d0407baSopenharmony_ci .set_event = rk3288_dfi_set_event, 2553d0407baSopenharmony_ci}; 2563d0407baSopenharmony_ci 2573d0407baSopenharmony_cistatic void rk3368_dfi_start_hardware_counter(struct devfreq_event_dev *edev) 2583d0407baSopenharmony_ci{ 2593d0407baSopenharmony_ci struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); 2603d0407baSopenharmony_ci 2613d0407baSopenharmony_ci regmap_write(info->regmap_grf, RK3368_GRF_DDRC0_CON0, RK3368_DFI_EN); 2623d0407baSopenharmony_ci} 2633d0407baSopenharmony_ci 2643d0407baSopenharmony_cistatic void rk3368_dfi_stop_hardware_counter(struct devfreq_event_dev *edev) 2653d0407baSopenharmony_ci{ 2663d0407baSopenharmony_ci struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); 2673d0407baSopenharmony_ci 2683d0407baSopenharmony_ci regmap_write(info->regmap_grf, RK3368_GRF_DDRC0_CON0, RK3368_DFI_DIS); 2693d0407baSopenharmony_ci} 2703d0407baSopenharmony_ci 2713d0407baSopenharmony_cistatic int rk3368_dfi_disable(struct devfreq_event_dev *edev) 2723d0407baSopenharmony_ci{ 2733d0407baSopenharmony_ci rk3368_dfi_stop_hardware_counter(edev); 2743d0407baSopenharmony_ci 2753d0407baSopenharmony_ci return 0; 2763d0407baSopenharmony_ci} 2773d0407baSopenharmony_ci 2783d0407baSopenharmony_cistatic int rk3368_dfi_enable(struct devfreq_event_dev *edev) 2793d0407baSopenharmony_ci{ 2803d0407baSopenharmony_ci rk3368_dfi_start_hardware_counter(edev); 2813d0407baSopenharmony_ci 2823d0407baSopenharmony_ci return 0; 2833d0407baSopenharmony_ci} 2843d0407baSopenharmony_ci 2853d0407baSopenharmony_cistatic int rk3368_dfi_set_event(struct devfreq_event_dev *edev) 2863d0407baSopenharmony_ci{ 2873d0407baSopenharmony_ci return 0; 2883d0407baSopenharmony_ci} 2893d0407baSopenharmony_ci 2903d0407baSopenharmony_cistatic int rk3368_dfi_get_event(struct devfreq_event_dev *edev, struct devfreq_event_data *edata) 2913d0407baSopenharmony_ci{ 2923d0407baSopenharmony_ci struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); 2933d0407baSopenharmony_ci unsigned long flags; 2943d0407baSopenharmony_ci u32 dfi0_wr, dfi0_rd, dfi1_wr, dfi1_rd, dfi_timer; 2953d0407baSopenharmony_ci 2963d0407baSopenharmony_ci local_irq_save(flags); 2973d0407baSopenharmony_ci 2983d0407baSopenharmony_ci rk3368_dfi_stop_hardware_counter(edev); 2993d0407baSopenharmony_ci 3003d0407baSopenharmony_ci regmap_read(info->regmap_grf, RK3368_GRF_SOC_STATUS5, &dfi0_wr); 3013d0407baSopenharmony_ci regmap_read(info->regmap_grf, RK3368_GRF_SOC_STATUS6, &dfi0_rd); 3023d0407baSopenharmony_ci regmap_read(info->regmap_grf, RK3368_GRF_SOC_STATUS9, &dfi1_wr); 3033d0407baSopenharmony_ci regmap_read(info->regmap_grf, RK3368_GRF_SOC_STATUS10, &dfi1_rd); 3043d0407baSopenharmony_ci regmap_read(info->regmap_grf, RK3368_GRF_SOC_STATUS8, &dfi_timer); 3053d0407baSopenharmony_ci 3063d0407baSopenharmony_ci edata->load_count = (dfi0_wr + dfi0_rd + dfi1_wr + dfi1_rd) * 0x2; 3073d0407baSopenharmony_ci edata->total_count = dfi_timer; 3083d0407baSopenharmony_ci 3093d0407baSopenharmony_ci rk3368_dfi_start_hardware_counter(edev); 3103d0407baSopenharmony_ci 3113d0407baSopenharmony_ci local_irq_restore(flags); 3123d0407baSopenharmony_ci 3133d0407baSopenharmony_ci return 0; 3143d0407baSopenharmony_ci} 3153d0407baSopenharmony_ci 3163d0407baSopenharmony_cistatic const struct devfreq_event_ops rk3368_dfi_ops = { 3173d0407baSopenharmony_ci .disable = rk3368_dfi_disable, 3183d0407baSopenharmony_ci .enable = rk3368_dfi_enable, 3193d0407baSopenharmony_ci .get_event = rk3368_dfi_get_event, 3203d0407baSopenharmony_ci .set_event = rk3368_dfi_set_event, 3213d0407baSopenharmony_ci}; 3223d0407baSopenharmony_ci 3233d0407baSopenharmony_cistatic void rockchip_dfi_start_hardware_counter(struct devfreq_event_dev *edev) 3243d0407baSopenharmony_ci{ 3253d0407baSopenharmony_ci struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); 3263d0407baSopenharmony_ci void __iomem *dfi_regs = info->regs; 3273d0407baSopenharmony_ci 3283d0407baSopenharmony_ci /* clear DDRMON_CTRL setting */ 3293d0407baSopenharmony_ci writel_relaxed(CLR_DDRMON_CTRL, dfi_regs + DDRMON_CTRL); 3303d0407baSopenharmony_ci 3313d0407baSopenharmony_ci /* set ddr type to dfi */ 3323d0407baSopenharmony_ci if (info->dram_type == LPDDR3 || info->dram_type == LPDDR2) { 3333d0407baSopenharmony_ci writel_relaxed(LPDDR2_3_EN, dfi_regs + DDRMON_CTRL); 3343d0407baSopenharmony_ci } else if (info->dram_type == LPDDR4 || info->dram_type == LPDDR4X) { 3353d0407baSopenharmony_ci writel_relaxed(LPDDR4_EN, dfi_regs + DDRMON_CTRL); 3363d0407baSopenharmony_ci } else if (info->dram_type == DDR4) { 3373d0407baSopenharmony_ci writel_relaxed(DDR4_EN, dfi_regs + DDRMON_CTRL); 3383d0407baSopenharmony_ci } 3393d0407baSopenharmony_ci 3403d0407baSopenharmony_ci /* enable count, use software mode */ 3413d0407baSopenharmony_ci writel_relaxed(SOFTWARE_EN, dfi_regs + DDRMON_CTRL); 3423d0407baSopenharmony_ci} 3433d0407baSopenharmony_ci 3443d0407baSopenharmony_cistatic void rockchip_dfi_stop_hardware_counter(struct devfreq_event_dev *edev) 3453d0407baSopenharmony_ci{ 3463d0407baSopenharmony_ci struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); 3473d0407baSopenharmony_ci void __iomem *dfi_regs = info->regs; 3483d0407baSopenharmony_ci 3493d0407baSopenharmony_ci writel_relaxed(SOFTWARE_DIS, dfi_regs + DDRMON_CTRL); 3503d0407baSopenharmony_ci} 3513d0407baSopenharmony_ci 3523d0407baSopenharmony_cistatic int rockchip_dfi_get_busier_ch(struct devfreq_event_dev *edev) 3533d0407baSopenharmony_ci{ 3543d0407baSopenharmony_ci struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); 3553d0407baSopenharmony_ci u32 tmp, max = 0; 3563d0407baSopenharmony_ci u32 i, busier_ch = 0; 3573d0407baSopenharmony_ci void __iomem *dfi_regs = info->regs; 3583d0407baSopenharmony_ci 3593d0407baSopenharmony_ci rockchip_dfi_stop_hardware_counter(edev); 3603d0407baSopenharmony_ci 3613d0407baSopenharmony_ci /* Find out which channel is busier */ 3623d0407baSopenharmony_ci for (i = 0; i < MAX_DMC_NUM_CH; i++) { 3633d0407baSopenharmony_ci if (!(info->ch_msk & BIT(i))) { 3643d0407baSopenharmony_ci continue; 3653d0407baSopenharmony_ci } 3663d0407baSopenharmony_ci 3673d0407baSopenharmony_ci info->ch_usage[i].total = readl_relaxed(dfi_regs + DDRMON_CH0_COUNT_NUM + i * 0x14); 3683d0407baSopenharmony_ci 3693d0407baSopenharmony_ci /* LPDDR4 and LPDDR4X BL = 16,other DDR type BL = 8 */ 3703d0407baSopenharmony_ci tmp = readl_relaxed(dfi_regs + DDRMON_CH0_DFI_ACCESS_NUM + i * 0x14); 3713d0407baSopenharmony_ci if (info->dram_type == LPDDR4 || info->dram_type == LPDDR4X) { 3723d0407baSopenharmony_ci tmp *= 0x8; 3733d0407baSopenharmony_ci } else { 3743d0407baSopenharmony_ci tmp *= 0x4; 3753d0407baSopenharmony_ci } 3763d0407baSopenharmony_ci info->ch_usage[i].access = tmp; 3773d0407baSopenharmony_ci 3783d0407baSopenharmony_ci if (tmp > max) { 3793d0407baSopenharmony_ci busier_ch = i; 3803d0407baSopenharmony_ci max = tmp; 3813d0407baSopenharmony_ci } 3823d0407baSopenharmony_ci } 3833d0407baSopenharmony_ci rockchip_dfi_start_hardware_counter(edev); 3843d0407baSopenharmony_ci 3853d0407baSopenharmony_ci return busier_ch; 3863d0407baSopenharmony_ci} 3873d0407baSopenharmony_ci 3883d0407baSopenharmony_cistatic int rockchip_dfi_disable(struct devfreq_event_dev *edev) 3893d0407baSopenharmony_ci{ 3903d0407baSopenharmony_ci struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); 3913d0407baSopenharmony_ci 3923d0407baSopenharmony_ci rockchip_dfi_stop_hardware_counter(edev); 3933d0407baSopenharmony_ci if (info->clk) { 3943d0407baSopenharmony_ci clk_disable_unprepare(info->clk); 3953d0407baSopenharmony_ci } 3963d0407baSopenharmony_ci 3973d0407baSopenharmony_ci return 0; 3983d0407baSopenharmony_ci} 3993d0407baSopenharmony_ci 4003d0407baSopenharmony_cistatic int rockchip_dfi_enable(struct devfreq_event_dev *edev) 4013d0407baSopenharmony_ci{ 4023d0407baSopenharmony_ci struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); 4033d0407baSopenharmony_ci int ret; 4043d0407baSopenharmony_ci 4053d0407baSopenharmony_ci if (info->clk) { 4063d0407baSopenharmony_ci ret = clk_prepare_enable(info->clk); 4073d0407baSopenharmony_ci if (ret) { 4083d0407baSopenharmony_ci dev_err(&edev->dev, "failed to enable dfi clk: %d\n", ret); 4093d0407baSopenharmony_ci return ret; 4103d0407baSopenharmony_ci } 4113d0407baSopenharmony_ci } 4123d0407baSopenharmony_ci 4133d0407baSopenharmony_ci rockchip_dfi_start_hardware_counter(edev); 4143d0407baSopenharmony_ci return 0; 4153d0407baSopenharmony_ci} 4163d0407baSopenharmony_ci 4173d0407baSopenharmony_cistatic int rockchip_dfi_set_event(struct devfreq_event_dev *edev) 4183d0407baSopenharmony_ci{ 4193d0407baSopenharmony_ci return 0; 4203d0407baSopenharmony_ci} 4213d0407baSopenharmony_ci 4223d0407baSopenharmony_cistatic int rockchip_dfi_get_event(struct devfreq_event_dev *edev, struct devfreq_event_data *edata) 4233d0407baSopenharmony_ci{ 4243d0407baSopenharmony_ci struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); 4253d0407baSopenharmony_ci int busier_ch; 4263d0407baSopenharmony_ci unsigned long flags; 4273d0407baSopenharmony_ci 4283d0407baSopenharmony_ci local_irq_save(flags); 4293d0407baSopenharmony_ci busier_ch = rockchip_dfi_get_busier_ch(edev); 4303d0407baSopenharmony_ci local_irq_restore(flags); 4313d0407baSopenharmony_ci 4323d0407baSopenharmony_ci edata->load_count = info->ch_usage[busier_ch].access; 4333d0407baSopenharmony_ci edata->total_count = info->ch_usage[busier_ch].total; 4343d0407baSopenharmony_ci 4353d0407baSopenharmony_ci return 0; 4363d0407baSopenharmony_ci} 4373d0407baSopenharmony_ci 4383d0407baSopenharmony_cistatic const struct devfreq_event_ops rockchip_dfi_ops = { 4393d0407baSopenharmony_ci .disable = rockchip_dfi_disable, 4403d0407baSopenharmony_ci .enable = rockchip_dfi_enable, 4413d0407baSopenharmony_ci .get_event = rockchip_dfi_get_event, 4423d0407baSopenharmony_ci .set_event = rockchip_dfi_set_event, 4433d0407baSopenharmony_ci}; 4443d0407baSopenharmony_ci 4453d0407baSopenharmony_cistatic __init int px30_dfi_init(struct platform_device *pdev, struct rockchip_dfi *data, 4463d0407baSopenharmony_ci struct devfreq_event_desc *desc) 4473d0407baSopenharmony_ci{ 4483d0407baSopenharmony_ci struct device_node *np = pdev->dev.of_node, *node; 4493d0407baSopenharmony_ci struct resource *res; 4503d0407baSopenharmony_ci u32 val_2, val_3; 4513d0407baSopenharmony_ci 4523d0407baSopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 4533d0407baSopenharmony_ci data->regs = devm_ioremap_resource(&pdev->dev, res); 4543d0407baSopenharmony_ci if (IS_ERR(data->regs)) { 4553d0407baSopenharmony_ci return PTR_ERR(data->regs); 4563d0407baSopenharmony_ci } 4573d0407baSopenharmony_ci 4583d0407baSopenharmony_ci node = of_parse_phandle(np, "rockchip,pmugrf", 0); 4593d0407baSopenharmony_ci if (node) { 4603d0407baSopenharmony_ci data->regmap_pmugrf = syscon_node_to_regmap(node); 4613d0407baSopenharmony_ci if (IS_ERR(data->regmap_pmugrf)) { 4623d0407baSopenharmony_ci return PTR_ERR(data->regmap_pmugrf); 4633d0407baSopenharmony_ci } 4643d0407baSopenharmony_ci } 4653d0407baSopenharmony_ci 4663d0407baSopenharmony_ci regmap_read(data->regmap_pmugrf, PX30_PMUGRF_OS_REG2, &val_2); 4673d0407baSopenharmony_ci regmap_read(data->regmap_pmugrf, PX30_PMUGRF_OS_REG3, &val_3); 4683d0407baSopenharmony_ci if (READ_SYSREG_VERSION(val_3) >= 0x3) { 4693d0407baSopenharmony_ci data->dram_type = READ_DRAMTYPE_INFO_V3(val_2, val_3); 4703d0407baSopenharmony_ci } else { 4713d0407baSopenharmony_ci data->dram_type = READ_DRAMTYPE_INFO(val_2); 4723d0407baSopenharmony_ci } 4733d0407baSopenharmony_ci data->ch_msk = 1; 4743d0407baSopenharmony_ci data->clk = NULL; 4753d0407baSopenharmony_ci 4763d0407baSopenharmony_ci desc->ops = &rockchip_dfi_ops; 4773d0407baSopenharmony_ci 4783d0407baSopenharmony_ci return 0; 4793d0407baSopenharmony_ci} 4803d0407baSopenharmony_ci 4813d0407baSopenharmony_cistatic __init int rk3128_dfi_init(struct platform_device *pdev, struct rockchip_dfi *data, 4823d0407baSopenharmony_ci struct devfreq_event_desc *desc) 4833d0407baSopenharmony_ci{ 4843d0407baSopenharmony_ci struct device_node *np = pdev->dev.of_node, *node; 4853d0407baSopenharmony_ci 4863d0407baSopenharmony_ci node = of_parse_phandle(np, "rockchip,grf", 0); 4873d0407baSopenharmony_ci if (node) { 4883d0407baSopenharmony_ci data->regmap_grf = syscon_node_to_regmap(node); 4893d0407baSopenharmony_ci if (IS_ERR(data->regmap_grf)) { 4903d0407baSopenharmony_ci return PTR_ERR(data->regmap_grf); 4913d0407baSopenharmony_ci } 4923d0407baSopenharmony_ci } 4933d0407baSopenharmony_ci 4943d0407baSopenharmony_ci desc->ops = &rk3128_dfi_ops; 4953d0407baSopenharmony_ci 4963d0407baSopenharmony_ci return 0; 4973d0407baSopenharmony_ci} 4983d0407baSopenharmony_ci 4993d0407baSopenharmony_cistatic __init int rk3288_dfi_init(struct platform_device *pdev, struct rockchip_dfi *data, 5003d0407baSopenharmony_ci struct devfreq_event_desc *desc) 5013d0407baSopenharmony_ci{ 5023d0407baSopenharmony_ci struct device_node *np = pdev->dev.of_node, *node; 5033d0407baSopenharmony_ci u32 val; 5043d0407baSopenharmony_ci 5053d0407baSopenharmony_ci node = of_parse_phandle(np, "rockchip,pmu", 0); 5063d0407baSopenharmony_ci if (node) { 5073d0407baSopenharmony_ci data->regmap_pmu = syscon_node_to_regmap(node); 5083d0407baSopenharmony_ci if (IS_ERR(data->regmap_pmu)) { 5093d0407baSopenharmony_ci return PTR_ERR(data->regmap_pmu); 5103d0407baSopenharmony_ci } 5113d0407baSopenharmony_ci } 5123d0407baSopenharmony_ci 5133d0407baSopenharmony_ci node = of_parse_phandle(np, "rockchip,grf", 0); 5143d0407baSopenharmony_ci if (node) { 5153d0407baSopenharmony_ci data->regmap_grf = syscon_node_to_regmap(node); 5163d0407baSopenharmony_ci if (IS_ERR(data->regmap_grf)) { 5173d0407baSopenharmony_ci return PTR_ERR(data->regmap_grf); 5183d0407baSopenharmony_ci } 5193d0407baSopenharmony_ci } 5203d0407baSopenharmony_ci 5213d0407baSopenharmony_ci regmap_read(data->regmap_pmu, RK3288_PMU_SYS_REG2, &val); 5223d0407baSopenharmony_ci data->dram_type = READ_DRAMTYPE_INFO(val); 5233d0407baSopenharmony_ci data->ch_msk = READ_CH_INFO(val); 5243d0407baSopenharmony_ci 5253d0407baSopenharmony_ci if (data->dram_type == DDR3) { 5263d0407baSopenharmony_ci regmap_write(data->regmap_grf, RK3288_GRF_SOC_CON4, RK3288_DDR3_SEL); 5273d0407baSopenharmony_ci } else { 5283d0407baSopenharmony_ci regmap_write(data->regmap_grf, RK3288_GRF_SOC_CON4, RK3288_LPDDR_SEL); 5293d0407baSopenharmony_ci } 5303d0407baSopenharmony_ci 5313d0407baSopenharmony_ci desc->ops = &rk3288_dfi_ops; 5323d0407baSopenharmony_ci 5333d0407baSopenharmony_ci return 0; 5343d0407baSopenharmony_ci} 5353d0407baSopenharmony_ci 5363d0407baSopenharmony_cistatic __init int rk3368_dfi_init(struct platform_device *pdev, struct rockchip_dfi *data, 5373d0407baSopenharmony_ci struct devfreq_event_desc *desc) 5383d0407baSopenharmony_ci{ 5393d0407baSopenharmony_ci struct device *dev = &pdev->dev; 5403d0407baSopenharmony_ci 5413d0407baSopenharmony_ci if (!dev->parent || !dev->parent->of_node) { 5423d0407baSopenharmony_ci return -EINVAL; 5433d0407baSopenharmony_ci } 5443d0407baSopenharmony_ci 5453d0407baSopenharmony_ci data->regmap_grf = syscon_node_to_regmap(dev->parent->of_node); 5463d0407baSopenharmony_ci if (IS_ERR(data->regmap_grf)) { 5473d0407baSopenharmony_ci return PTR_ERR(data->regmap_grf); 5483d0407baSopenharmony_ci } 5493d0407baSopenharmony_ci 5503d0407baSopenharmony_ci desc->ops = &rk3368_dfi_ops; 5513d0407baSopenharmony_ci 5523d0407baSopenharmony_ci return 0; 5533d0407baSopenharmony_ci} 5543d0407baSopenharmony_ci 5553d0407baSopenharmony_cistatic __init int rockchip_dfi_init(struct platform_device *pdev, struct rockchip_dfi *data, 5563d0407baSopenharmony_ci struct devfreq_event_desc *desc) 5573d0407baSopenharmony_ci{ 5583d0407baSopenharmony_ci struct device *dev = &pdev->dev; 5593d0407baSopenharmony_ci struct device_node *np = pdev->dev.of_node, *node; 5603d0407baSopenharmony_ci u32 val; 5613d0407baSopenharmony_ci 5623d0407baSopenharmony_ci data->regs = devm_platform_ioremap_resource(pdev, 0); 5633d0407baSopenharmony_ci if (IS_ERR(data->regs)) { 5643d0407baSopenharmony_ci return PTR_ERR(data->regs); 5653d0407baSopenharmony_ci } 5663d0407baSopenharmony_ci 5673d0407baSopenharmony_ci data->clk = devm_clk_get(dev, "pclk_ddr_mon"); 5683d0407baSopenharmony_ci if (IS_ERR(data->clk)) { 5693d0407baSopenharmony_ci dev_err(dev, "Cannot get the clk dmc_clk\n"); 5703d0407baSopenharmony_ci return PTR_ERR(data->clk); 5713d0407baSopenharmony_ci } 5723d0407baSopenharmony_ci 5733d0407baSopenharmony_ci /* try to find the optional reference to the pmu syscon */ 5743d0407baSopenharmony_ci node = of_parse_phandle(np, "rockchip,pmu", 0); 5753d0407baSopenharmony_ci if (node) { 5763d0407baSopenharmony_ci data->regmap_pmu = syscon_node_to_regmap(node); 5773d0407baSopenharmony_ci of_node_put(node); 5783d0407baSopenharmony_ci if (IS_ERR(data->regmap_pmu)) { 5793d0407baSopenharmony_ci return PTR_ERR(data->regmap_pmu); 5803d0407baSopenharmony_ci } 5813d0407baSopenharmony_ci } 5823d0407baSopenharmony_ci 5833d0407baSopenharmony_ci regmap_read(data->regmap_pmu, PMUGRF_OS_REG2, &val); 5843d0407baSopenharmony_ci data->dram_type = READ_DRAMTYPE_INFO(val); 5853d0407baSopenharmony_ci data->ch_msk = READ_CH_INFO(val); 5863d0407baSopenharmony_ci 5873d0407baSopenharmony_ci desc->ops = &rockchip_dfi_ops; 5883d0407baSopenharmony_ci 5893d0407baSopenharmony_ci return 0; 5903d0407baSopenharmony_ci} 5913d0407baSopenharmony_ci 5923d0407baSopenharmony_cistatic __init int rk3328_dfi_init(struct platform_device *pdev, struct rockchip_dfi *data, 5933d0407baSopenharmony_ci struct devfreq_event_desc *desc) 5943d0407baSopenharmony_ci{ 5953d0407baSopenharmony_ci struct device_node *np = pdev->dev.of_node, *node; 5963d0407baSopenharmony_ci struct resource *res; 5973d0407baSopenharmony_ci u32 val; 5983d0407baSopenharmony_ci 5993d0407baSopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 6003d0407baSopenharmony_ci data->regs = devm_ioremap_resource(&pdev->dev, res); 6013d0407baSopenharmony_ci if (IS_ERR(data->regs)) { 6023d0407baSopenharmony_ci return PTR_ERR(data->regs); 6033d0407baSopenharmony_ci } 6043d0407baSopenharmony_ci 6053d0407baSopenharmony_ci node = of_parse_phandle(np, "rockchip,grf", 0); 6063d0407baSopenharmony_ci if (node) { 6073d0407baSopenharmony_ci data->regmap_grf = syscon_node_to_regmap(node); 6083d0407baSopenharmony_ci if (IS_ERR(data->regmap_grf)) { 6093d0407baSopenharmony_ci return PTR_ERR(data->regmap_grf); 6103d0407baSopenharmony_ci } 6113d0407baSopenharmony_ci } 6123d0407baSopenharmony_ci 6133d0407baSopenharmony_ci regmap_read(data->regmap_grf, RK3328_GRF_OS_REG2, &val); 6143d0407baSopenharmony_ci data->dram_type = READ_DRAMTYPE_INFO(val); 6153d0407baSopenharmony_ci data->ch_msk = 1; 6163d0407baSopenharmony_ci data->clk = NULL; 6173d0407baSopenharmony_ci 6183d0407baSopenharmony_ci desc->ops = &rockchip_dfi_ops; 6193d0407baSopenharmony_ci 6203d0407baSopenharmony_ci return 0; 6213d0407baSopenharmony_ci} 6223d0407baSopenharmony_ci 6233d0407baSopenharmony_cistatic const struct of_device_id rockchip_dfi_id_match[] = { 6243d0407baSopenharmony_ci {.compatible = "rockchip,px30-dfi", .data = px30_dfi_init}, 6253d0407baSopenharmony_ci {.compatible = "rockchip,rk1808-dfi", .data = px30_dfi_init}, 6263d0407baSopenharmony_ci {.compatible = "rockchip,rk3128-dfi", .data = rk3128_dfi_init}, 6273d0407baSopenharmony_ci {.compatible = "rockchip,rk3288-dfi", .data = rk3288_dfi_init}, 6283d0407baSopenharmony_ci {.compatible = "rockchip,rk3328-dfi", .data = rk3328_dfi_init}, 6293d0407baSopenharmony_ci {.compatible = "rockchip,rk3368-dfi", .data = rk3368_dfi_init}, 6303d0407baSopenharmony_ci {.compatible = "rockchip,rk3399-dfi", .data = rockchip_dfi_init}, 6313d0407baSopenharmony_ci {.compatible = "rockchip,rk3568-dfi", .data = px30_dfi_init}, 6323d0407baSopenharmony_ci {.compatible = "rockchip,rv1126-dfi", .data = px30_dfi_init}, 6333d0407baSopenharmony_ci {}, 6343d0407baSopenharmony_ci}; 6353d0407baSopenharmony_ci 6363d0407baSopenharmony_cistatic int rockchip_dfi_probe(struct platform_device *pdev) 6373d0407baSopenharmony_ci{ 6383d0407baSopenharmony_ci struct device *dev = &pdev->dev; 6393d0407baSopenharmony_ci struct rockchip_dfi *data; 6403d0407baSopenharmony_ci struct devfreq_event_desc *desc; 6413d0407baSopenharmony_ci struct device_node *np = pdev->dev.of_node; 6423d0407baSopenharmony_ci const struct of_device_id *match; 6433d0407baSopenharmony_ci int (*init)(struct platform_device * pdev, struct rockchip_dfi * data, struct devfreq_event_desc * desc); 6443d0407baSopenharmony_ci 6453d0407baSopenharmony_ci data = devm_kzalloc(dev, sizeof(struct rockchip_dfi), GFP_KERNEL); 6463d0407baSopenharmony_ci if (!data) { 6473d0407baSopenharmony_ci return -ENOMEM; 6483d0407baSopenharmony_ci } 6493d0407baSopenharmony_ci 6503d0407baSopenharmony_ci desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); 6513d0407baSopenharmony_ci if (!desc) { 6523d0407baSopenharmony_ci return -ENOMEM; 6533d0407baSopenharmony_ci } 6543d0407baSopenharmony_ci 6553d0407baSopenharmony_ci match = of_match_node(rockchip_dfi_id_match, pdev->dev.of_node); 6563d0407baSopenharmony_ci if (match) { 6573d0407baSopenharmony_ci init = match->data; 6583d0407baSopenharmony_ci if (init) { 6593d0407baSopenharmony_ci if (init(pdev, data, desc)) { 6603d0407baSopenharmony_ci return -EINVAL; 6613d0407baSopenharmony_ci } 6623d0407baSopenharmony_ci } else { 6633d0407baSopenharmony_ci return 0; 6643d0407baSopenharmony_ci } 6653d0407baSopenharmony_ci } else { 6663d0407baSopenharmony_ci return 0; 6673d0407baSopenharmony_ci } 6683d0407baSopenharmony_ci 6693d0407baSopenharmony_ci desc->driver_data = data; 6703d0407baSopenharmony_ci desc->name = np->name; 6713d0407baSopenharmony_ci 6723d0407baSopenharmony_ci data->edev = devm_devfreq_event_add_edev(dev, desc); 6733d0407baSopenharmony_ci if (IS_ERR(data->edev)) { 6743d0407baSopenharmony_ci dev_err(dev, "failed to add devfreq-event device\n"); 6753d0407baSopenharmony_ci return PTR_ERR(data->edev); 6763d0407baSopenharmony_ci } 6773d0407baSopenharmony_ci data->desc = desc; 6783d0407baSopenharmony_ci data->dev = &pdev->dev; 6793d0407baSopenharmony_ci 6803d0407baSopenharmony_ci platform_set_drvdata(pdev, data); 6813d0407baSopenharmony_ci 6823d0407baSopenharmony_ci return 0; 6833d0407baSopenharmony_ci} 6843d0407baSopenharmony_ci 6853d0407baSopenharmony_cistatic struct platform_driver rockchip_dfi_driver = { 6863d0407baSopenharmony_ci .probe = rockchip_dfi_probe, 6873d0407baSopenharmony_ci .driver = 6883d0407baSopenharmony_ci { 6893d0407baSopenharmony_ci .name = "rockchip-dfi", 6903d0407baSopenharmony_ci .of_match_table = rockchip_dfi_id_match, 6913d0407baSopenharmony_ci }, 6923d0407baSopenharmony_ci}; 6933d0407baSopenharmony_cimodule_platform_driver(rockchip_dfi_driver); 6943d0407baSopenharmony_ci 6953d0407baSopenharmony_ciMODULE_LICENSE("GPL v2"); 6963d0407baSopenharmony_ciMODULE_AUTHOR("Lin Huang <hl@rock-chips.com>"); 6973d0407baSopenharmony_ciMODULE_DESCRIPTION("Rockchip DFI driver"); 698