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