Linux Audio

Check our new training course

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