cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

rockchip-dfi.c (6205B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
      4 * Author: Lin Huang <hl@rock-chips.com>
      5 */
      6
      7#include <linux/clk.h>
      8#include <linux/devfreq-event.h>
      9#include <linux/kernel.h>
     10#include <linux/err.h>
     11#include <linux/init.h>
     12#include <linux/io.h>
     13#include <linux/mfd/syscon.h>
     14#include <linux/module.h>
     15#include <linux/platform_device.h>
     16#include <linux/regmap.h>
     17#include <linux/slab.h>
     18#include <linux/list.h>
     19#include <linux/of.h>
     20
     21#include <soc/rockchip/rk3399_grf.h>
     22
     23#define RK3399_DMC_NUM_CH	2
     24
     25/* DDRMON_CTRL */
     26#define DDRMON_CTRL	0x04
     27#define CLR_DDRMON_CTRL	(0x1f0000 << 0)
     28#define LPDDR4_EN	(0x10001 << 4)
     29#define HARDWARE_EN	(0x10001 << 3)
     30#define LPDDR3_EN	(0x10001 << 2)
     31#define SOFTWARE_EN	(0x10001 << 1)
     32#define SOFTWARE_DIS	(0x10000 << 1)
     33#define TIME_CNT_EN	(0x10001 << 0)
     34
     35#define DDRMON_CH0_COUNT_NUM		0x28
     36#define DDRMON_CH0_DFI_ACCESS_NUM	0x2c
     37#define DDRMON_CH1_COUNT_NUM		0x3c
     38#define DDRMON_CH1_DFI_ACCESS_NUM	0x40
     39
     40struct dmc_usage {
     41	u32 access;
     42	u32 total;
     43};
     44
     45/*
     46 * The dfi controller can monitor DDR load. It has an upper and lower threshold
     47 * for the operating points. Whenever the usage leaves these bounds an event is
     48 * generated to indicate the DDR frequency should be changed.
     49 */
     50struct rockchip_dfi {
     51	struct devfreq_event_dev *edev;
     52	struct devfreq_event_desc *desc;
     53	struct dmc_usage ch_usage[RK3399_DMC_NUM_CH];
     54	struct device *dev;
     55	void __iomem *regs;
     56	struct regmap *regmap_pmu;
     57	struct clk *clk;
     58};
     59
     60static void rockchip_dfi_start_hardware_counter(struct devfreq_event_dev *edev)
     61{
     62	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
     63	void __iomem *dfi_regs = info->regs;
     64	u32 val;
     65	u32 ddr_type;
     66
     67	/* get ddr type */
     68	regmap_read(info->regmap_pmu, RK3399_PMUGRF_OS_REG2, &val);
     69	ddr_type = (val >> RK3399_PMUGRF_DDRTYPE_SHIFT) &
     70		    RK3399_PMUGRF_DDRTYPE_MASK;
     71
     72	/* clear DDRMON_CTRL setting */
     73	writel_relaxed(CLR_DDRMON_CTRL, dfi_regs + DDRMON_CTRL);
     74
     75	/* set ddr type to dfi */
     76	if (ddr_type == RK3399_PMUGRF_DDRTYPE_LPDDR3)
     77		writel_relaxed(LPDDR3_EN, dfi_regs + DDRMON_CTRL);
     78	else if (ddr_type == RK3399_PMUGRF_DDRTYPE_LPDDR4)
     79		writel_relaxed(LPDDR4_EN, dfi_regs + DDRMON_CTRL);
     80
     81	/* enable count, use software mode */
     82	writel_relaxed(SOFTWARE_EN, dfi_regs + DDRMON_CTRL);
     83}
     84
     85static void rockchip_dfi_stop_hardware_counter(struct devfreq_event_dev *edev)
     86{
     87	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
     88	void __iomem *dfi_regs = info->regs;
     89
     90	writel_relaxed(SOFTWARE_DIS, dfi_regs + DDRMON_CTRL);
     91}
     92
     93static int rockchip_dfi_get_busier_ch(struct devfreq_event_dev *edev)
     94{
     95	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
     96	u32 tmp, max = 0;
     97	u32 i, busier_ch = 0;
     98	void __iomem *dfi_regs = info->regs;
     99
    100	rockchip_dfi_stop_hardware_counter(edev);
    101
    102	/* Find out which channel is busier */
    103	for (i = 0; i < RK3399_DMC_NUM_CH; i++) {
    104		info->ch_usage[i].access = readl_relaxed(dfi_regs +
    105				DDRMON_CH0_DFI_ACCESS_NUM + i * 20) * 4;
    106		info->ch_usage[i].total = readl_relaxed(dfi_regs +
    107				DDRMON_CH0_COUNT_NUM + i * 20);
    108		tmp = info->ch_usage[i].access;
    109		if (tmp > max) {
    110			busier_ch = i;
    111			max = tmp;
    112		}
    113	}
    114	rockchip_dfi_start_hardware_counter(edev);
    115
    116	return busier_ch;
    117}
    118
    119static int rockchip_dfi_disable(struct devfreq_event_dev *edev)
    120{
    121	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
    122
    123	rockchip_dfi_stop_hardware_counter(edev);
    124	clk_disable_unprepare(info->clk);
    125
    126	return 0;
    127}
    128
    129static int rockchip_dfi_enable(struct devfreq_event_dev *edev)
    130{
    131	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
    132	int ret;
    133
    134	ret = clk_prepare_enable(info->clk);
    135	if (ret) {
    136		dev_err(&edev->dev, "failed to enable dfi clk: %d\n", ret);
    137		return ret;
    138	}
    139
    140	rockchip_dfi_start_hardware_counter(edev);
    141	return 0;
    142}
    143
    144static int rockchip_dfi_set_event(struct devfreq_event_dev *edev)
    145{
    146	return 0;
    147}
    148
    149static int rockchip_dfi_get_event(struct devfreq_event_dev *edev,
    150				  struct devfreq_event_data *edata)
    151{
    152	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
    153	int busier_ch;
    154
    155	busier_ch = rockchip_dfi_get_busier_ch(edev);
    156
    157	edata->load_count = info->ch_usage[busier_ch].access;
    158	edata->total_count = info->ch_usage[busier_ch].total;
    159
    160	return 0;
    161}
    162
    163static const struct devfreq_event_ops rockchip_dfi_ops = {
    164	.disable = rockchip_dfi_disable,
    165	.enable = rockchip_dfi_enable,
    166	.get_event = rockchip_dfi_get_event,
    167	.set_event = rockchip_dfi_set_event,
    168};
    169
    170static const struct of_device_id rockchip_dfi_id_match[] = {
    171	{ .compatible = "rockchip,rk3399-dfi" },
    172	{ },
    173};
    174MODULE_DEVICE_TABLE(of, rockchip_dfi_id_match);
    175
    176static int rockchip_dfi_probe(struct platform_device *pdev)
    177{
    178	struct device *dev = &pdev->dev;
    179	struct rockchip_dfi *data;
    180	struct devfreq_event_desc *desc;
    181	struct device_node *np = pdev->dev.of_node, *node;
    182
    183	data = devm_kzalloc(dev, sizeof(struct rockchip_dfi), GFP_KERNEL);
    184	if (!data)
    185		return -ENOMEM;
    186
    187	data->regs = devm_platform_ioremap_resource(pdev, 0);
    188	if (IS_ERR(data->regs))
    189		return PTR_ERR(data->regs);
    190
    191	data->clk = devm_clk_get(dev, "pclk_ddr_mon");
    192	if (IS_ERR(data->clk)) {
    193		dev_err(dev, "Cannot get the clk dmc_clk\n");
    194		return PTR_ERR(data->clk);
    195	}
    196
    197	/* try to find the optional reference to the pmu syscon */
    198	node = of_parse_phandle(np, "rockchip,pmu", 0);
    199	if (node) {
    200		data->regmap_pmu = syscon_node_to_regmap(node);
    201		of_node_put(node);
    202		if (IS_ERR(data->regmap_pmu))
    203			return PTR_ERR(data->regmap_pmu);
    204	}
    205	data->dev = dev;
    206
    207	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
    208	if (!desc)
    209		return -ENOMEM;
    210
    211	desc->ops = &rockchip_dfi_ops;
    212	desc->driver_data = data;
    213	desc->name = np->name;
    214	data->desc = desc;
    215
    216	data->edev = devm_devfreq_event_add_edev(&pdev->dev, desc);
    217	if (IS_ERR(data->edev)) {
    218		dev_err(&pdev->dev,
    219			"failed to add devfreq-event device\n");
    220		return PTR_ERR(data->edev);
    221	}
    222
    223	platform_set_drvdata(pdev, data);
    224
    225	return 0;
    226}
    227
    228static struct platform_driver rockchip_dfi_driver = {
    229	.probe	= rockchip_dfi_probe,
    230	.driver = {
    231		.name	= "rockchip-dfi",
    232		.of_match_table = rockchip_dfi_id_match,
    233	},
    234};
    235module_platform_driver(rockchip_dfi_driver);
    236
    237MODULE_LICENSE("GPL v2");
    238MODULE_AUTHOR("Lin Huang <hl@rock-chips.com>");
    239MODULE_DESCRIPTION("Rockchip DFI driver");