linux/drivers/devfreq/event/rockchip-dfi.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
 * Author: Lin Huang <[email protected]>
 */

#include <linux/clk.h>
#include <linux/devfreq-event.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/seqlock.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/perf_event.h>

#include <soc/rockchip/rockchip_grf.h>
#include <soc/rockchip/rk3399_grf.h>
#include <soc/rockchip/rk3568_grf.h>
#include <soc/rockchip/rk3588_grf.h>

#define DMC_MAX_CHANNELS

#define HIWORD_UPDATE(val, mask)

/* DDRMON_CTRL */
#define DDRMON_CTRL
#define DDRMON_CTRL_DDR4
#define DDRMON_CTRL_LPDDR4
#define DDRMON_CTRL_HARDWARE_EN
#define DDRMON_CTRL_LPDDR23
#define DDRMON_CTRL_SOFTWARE_EN
#define DDRMON_CTRL_TIMER_CNT_EN
#define DDRMON_CTRL_DDR_TYPE_MASK

#define DDRMON_CH0_WR_NUM
#define DDRMON_CH0_RD_NUM
#define DDRMON_CH0_COUNT_NUM
#define DDRMON_CH0_DFI_ACCESS_NUM
#define DDRMON_CH1_COUNT_NUM
#define DDRMON_CH1_DFI_ACCESS_NUM

#define PERF_EVENT_CYCLES
#define PERF_EVENT_READ_BYTES
#define PERF_EVENT_WRITE_BYTES
#define PERF_EVENT_READ_BYTES0
#define PERF_EVENT_WRITE_BYTES0
#define PERF_EVENT_READ_BYTES1
#define PERF_EVENT_WRITE_BYTES1
#define PERF_EVENT_READ_BYTES2
#define PERF_EVENT_WRITE_BYTES2
#define PERF_EVENT_READ_BYTES3
#define PERF_EVENT_WRITE_BYTES3
#define PERF_EVENT_BYTES
#define PERF_ACCESS_TYPE_MAX

/**
 * struct dmc_count_channel - structure to hold counter values from the DDR controller
 * @access:       Number of read and write accesses
 * @clock_cycles: DDR clock cycles
 * @read_access:  number of read accesses
 * @write_access: number of write accesses
 */
struct dmc_count_channel {};

struct dmc_count {};

/*
 * The dfi controller can monitor DDR load. It has an upper and lower threshold
 * for the operating points. Whenever the usage leaves these bounds an event is
 * generated to indicate the DDR frequency should be changed.
 */
struct rockchip_dfi {};

static int rockchip_dfi_enable(struct rockchip_dfi *dfi)
{}

static void rockchip_dfi_disable(struct rockchip_dfi *dfi)
{}

static void rockchip_dfi_read_counters(struct rockchip_dfi *dfi, struct dmc_count *res)
{}

static int rockchip_dfi_event_disable(struct devfreq_event_dev *edev)
{}

static int rockchip_dfi_event_enable(struct devfreq_event_dev *edev)
{}

static int rockchip_dfi_set_event(struct devfreq_event_dev *edev)
{}

static int rockchip_dfi_get_event(struct devfreq_event_dev *edev,
				  struct devfreq_event_data *edata)
{}

static const struct devfreq_event_ops rockchip_dfi_ops =;

#ifdef CONFIG_PERF_EVENTS

static void rockchip_ddr_perf_counters_add(struct rockchip_dfi *dfi,
					   const struct dmc_count *now,
					   struct dmc_count *res)
{}

static ssize_t ddr_perf_cpumask_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{}

static struct device_attribute ddr_perf_cpumask_attr =;

static struct attribute *ddr_perf_cpumask_attrs[] =;

static const struct attribute_group ddr_perf_cpumask_attr_group =;

PMU_EVENT_ATTR_STRING(cycles, ddr_pmu_cycles, "event="__stringify(PERF_EVENT_CYCLES))

#define DFI_PMU_EVENT_ATTR(_name, _var, _str)

DFI_PMU_EVENT_ATTR(read-bytes0, ddr_pmu_read_bytes0, "event="__stringify(PERF_EVENT_READ_BYTES0));
DFI_PMU_EVENT_ATTR(write-bytes0, ddr_pmu_write_bytes0, "event="__stringify(PERF_EVENT_WRITE_BYTES0));

DFI_PMU_EVENT_ATTR(read-bytes1, ddr_pmu_read_bytes1, "event="__stringify(PERF_EVENT_READ_BYTES1));
DFI_PMU_EVENT_ATTR(write-bytes1, ddr_pmu_write_bytes1, "event="__stringify(PERF_EVENT_WRITE_BYTES1));

DFI_PMU_EVENT_ATTR(read-bytes2, ddr_pmu_read_bytes2, "event="__stringify(PERF_EVENT_READ_BYTES2));
DFI_PMU_EVENT_ATTR(write-bytes2, ddr_pmu_write_bytes2, "event="__stringify(PERF_EVENT_WRITE_BYTES2));

DFI_PMU_EVENT_ATTR(read-bytes3, ddr_pmu_read_bytes3, "event="__stringify(PERF_EVENT_READ_BYTES3));
DFI_PMU_EVENT_ATTR(write-bytes3, ddr_pmu_write_bytes3, "event="__stringify(PERF_EVENT_WRITE_BYTES3));

DFI_PMU_EVENT_ATTR(read-bytes, ddr_pmu_read_bytes, "event="__stringify(PERF_EVENT_READ_BYTES));
DFI_PMU_EVENT_ATTR(write-bytes, ddr_pmu_write_bytes, "event="__stringify(PERF_EVENT_WRITE_BYTES));

DFI_PMU_EVENT_ATTR(bytes, ddr_pmu_bytes, "event="__stringify(PERF_EVENT_BYTES));

#define DFI_ATTR_MB(_name)

static struct attribute *ddr_perf_events_attrs[] =;

static const struct attribute_group ddr_perf_events_attr_group =;

PMU_FORMAT_ATTR();

static struct attribute *ddr_perf_format_attrs[] =;

static const struct attribute_group ddr_perf_format_attr_group =;

static const struct attribute_group *attr_groups[] =;

static int rockchip_ddr_perf_event_init(struct perf_event *event)
{}

static u64 rockchip_ddr_perf_event_get_count(struct perf_event *event)
{}

static void rockchip_ddr_perf_event_update(struct perf_event *event)
{}

static void rockchip_ddr_perf_event_start(struct perf_event *event, int flags)
{}

static int rockchip_ddr_perf_event_add(struct perf_event *event, int flags)
{}

static void rockchip_ddr_perf_event_stop(struct perf_event *event, int flags)
{}

static void rockchip_ddr_perf_event_del(struct perf_event *event, int flags)
{}

static enum hrtimer_restart rockchip_dfi_timer(struct hrtimer *timer)
{
	struct rockchip_dfi *dfi = container_of(timer, struct rockchip_dfi, timer);
	struct dmc_count now, total;

	rockchip_dfi_read_counters(dfi, &now);

	write_seqlock(&dfi->count_seqlock);

	rockchip_ddr_perf_counters_add(dfi, &now, &total);
	dfi->total_count = total;
	dfi->last_perf_count = now;

	write_sequnlock(&dfi->count_seqlock);

	hrtimer_forward_now(&dfi->timer, ns_to_ktime(NSEC_PER_SEC));

	return HRTIMER_RESTART;
};

static int ddr_perf_offline_cpu(unsigned int cpu, struct hlist_node *node)
{}

static void rockchip_ddr_cpuhp_remove_state(void *data)
{}

static void rockchip_ddr_cpuhp_remove_instance(void *data)
{}

static void rockchip_ddr_perf_remove(void *data)
{}

static int rockchip_ddr_perf_init(struct rockchip_dfi *dfi)
{}
#else
static int rockchip_ddr_perf_init(struct rockchip_dfi *dfi)
{
	return 0;
}
#endif

static int rk3399_dfi_init(struct rockchip_dfi *dfi)
{
	struct regmap *regmap_pmu = dfi->regmap_pmu;
	u32 val;

	dfi->clk = devm_clk_get(dfi->dev, "pclk_ddr_mon");
	if (IS_ERR(dfi->clk))
		return dev_err_probe(dfi->dev, PTR_ERR(dfi->clk),
				     "Cannot get the clk pclk_ddr_mon\n");

	/* get ddr type */
	regmap_read(regmap_pmu, RK3399_PMUGRF_OS_REG2, &val);
	dfi->ddr_type = FIELD_GET(RK3399_PMUGRF_OS_REG2_DDRTYPE, val);

	dfi->channel_mask = GENMASK(1, 0);
	dfi->max_channels = 2;

	dfi->buswidth[0] = FIELD_GET(RK3399_PMUGRF_OS_REG2_BW_CH0, val) == 0 ? 4 : 2;
	dfi->buswidth[1] = FIELD_GET(RK3399_PMUGRF_OS_REG2_BW_CH1, val) == 0 ? 4 : 2;

	dfi->ddrmon_stride = 0x14;
	dfi->ddrmon_ctrl_single = true;

	return 0;
};

static int rk3568_dfi_init(struct rockchip_dfi *dfi)
{
	struct regmap *regmap_pmu = dfi->regmap_pmu;
	u32 reg2, reg3;

	regmap_read(regmap_pmu, RK3568_PMUGRF_OS_REG2, &reg2);
	regmap_read(regmap_pmu, RK3568_PMUGRF_OS_REG3, &reg3);

	/* lower 3 bits of the DDR type */
	dfi->ddr_type = FIELD_GET(RK3568_PMUGRF_OS_REG2_DRAMTYPE_INFO, reg2);

	/*
	 * For version three and higher the upper two bits of the DDR type are
	 * in RK3568_PMUGRF_OS_REG3
	 */
	if (FIELD_GET(RK3568_PMUGRF_OS_REG3_SYSREG_VERSION, reg3) >= 0x3)
		dfi->ddr_type |= FIELD_GET(RK3568_PMUGRF_OS_REG3_DRAMTYPE_INFO_V3, reg3) << 3;

	dfi->channel_mask = BIT(0);
	dfi->max_channels = 1;

	dfi->buswidth[0] = FIELD_GET(RK3568_PMUGRF_OS_REG2_BW_CH0, reg2) == 0 ? 4 : 2;

	dfi->ddrmon_stride = 0x0; /* not relevant, we only have a single channel on this SoC */
	dfi->ddrmon_ctrl_single = true;

	return 0;
};

static int rk3588_dfi_init(struct rockchip_dfi *dfi)
{
	struct regmap *regmap_pmu = dfi->regmap_pmu;
	u32 reg2, reg3, reg4;

	regmap_read(regmap_pmu, RK3588_PMUGRF_OS_REG2, &reg2);
	regmap_read(regmap_pmu, RK3588_PMUGRF_OS_REG3, &reg3);
	regmap_read(regmap_pmu, RK3588_PMUGRF_OS_REG4, &reg4);

	/* lower 3 bits of the DDR type */
	dfi->ddr_type = FIELD_GET(RK3588_PMUGRF_OS_REG2_DRAMTYPE_INFO, reg2);

	/*
	 * For version three and higher the upper two bits of the DDR type are
	 * in RK3588_PMUGRF_OS_REG3
	 */
	if (FIELD_GET(RK3588_PMUGRF_OS_REG3_SYSREG_VERSION, reg3) >= 0x3)
		dfi->ddr_type |= FIELD_GET(RK3588_PMUGRF_OS_REG3_DRAMTYPE_INFO_V3, reg3) << 3;

	dfi->buswidth[0] = FIELD_GET(RK3588_PMUGRF_OS_REG2_BW_CH0, reg2) == 0 ? 4 : 2;
	dfi->buswidth[1] = FIELD_GET(RK3588_PMUGRF_OS_REG2_BW_CH1, reg2) == 0 ? 4 : 2;
	dfi->buswidth[2] = FIELD_GET(RK3568_PMUGRF_OS_REG2_BW_CH0, reg4) == 0 ? 4 : 2;
	dfi->buswidth[3] = FIELD_GET(RK3588_PMUGRF_OS_REG2_BW_CH1, reg4) == 0 ? 4 : 2;
	dfi->channel_mask = FIELD_GET(RK3588_PMUGRF_OS_REG2_CH_INFO, reg2) |
			    FIELD_GET(RK3588_PMUGRF_OS_REG2_CH_INFO, reg4) << 2;
	dfi->max_channels = 4;

	dfi->ddrmon_stride = 0x4000;

	return 0;
};

static const struct of_device_id rockchip_dfi_id_match[] =;

MODULE_DEVICE_TABLE(of, rockchip_dfi_id_match);

static int rockchip_dfi_probe(struct platform_device *pdev)
{}

static struct platform_driver rockchip_dfi_driver =;
module_platform_driver();

MODULE_LICENSE();
MODULE_AUTHOR();
MODULE_DESCRIPTION();