Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (c) 2022 Amlogic, Inc. All rights reserved.
  4 */
  5
  6#include <linux/err.h>
  7#include <linux/io.h>
  8#include <linux/kernel.h>
  9#include <linux/module.h>
 10#include <linux/of.h>
 11#include <linux/perf_event.h>
 12#include <linux/platform_device.h>
 13#include <linux/printk.h>
 14#include <linux/types.h>
 15
 16#include <soc/amlogic/meson_ddr_pmu.h>
 17
 18#define PORT_MAJOR		32
 19#define DEFAULT_XTAL_FREQ	24000000UL
 20
 21#define DMC_QOS_IRQ		BIT(30)
 22
 23/* DMC bandwidth monitor register address offset */
 24#define DMC_MON_G12_CTRL0		(0x0  << 2)
 25#define DMC_MON_G12_CTRL1		(0x1  << 2)
 26#define DMC_MON_G12_CTRL2		(0x2  << 2)
 27#define DMC_MON_G12_CTRL3		(0x3  << 2)
 28#define DMC_MON_G12_CTRL4		(0x4  << 2)
 29#define DMC_MON_G12_CTRL5		(0x5  << 2)
 30#define DMC_MON_G12_CTRL6		(0x6  << 2)
 31#define DMC_MON_G12_CTRL7		(0x7  << 2)
 32#define DMC_MON_G12_CTRL8		(0x8  << 2)
 33
 34#define DMC_MON_G12_ALL_REQ_CNT		(0x9  << 2)
 35#define DMC_MON_G12_ALL_GRANT_CNT	(0xa  << 2)
 36#define DMC_MON_G12_ONE_GRANT_CNT	(0xb  << 2)
 37#define DMC_MON_G12_SEC_GRANT_CNT	(0xc  << 2)
 38#define DMC_MON_G12_THD_GRANT_CNT	(0xd  << 2)
 39#define DMC_MON_G12_FOR_GRANT_CNT	(0xe  << 2)
 40#define DMC_MON_G12_TIMER		(0xf  << 2)
 41
 42/* Each bit represent a axi line */
 43PMU_FORMAT_ATTR(event, "config:0-7");
 44PMU_FORMAT_ATTR(arm, "config1:0");
 45PMU_FORMAT_ATTR(gpu, "config1:1");
 46PMU_FORMAT_ATTR(pcie, "config1:2");
 47PMU_FORMAT_ATTR(hdcp, "config1:3");
 48PMU_FORMAT_ATTR(hevc_front, "config1:4");
 49PMU_FORMAT_ATTR(usb3_0, "config1:6");
 50PMU_FORMAT_ATTR(device, "config1:7");
 51PMU_FORMAT_ATTR(hevc_back, "config1:8");
 52PMU_FORMAT_ATTR(h265enc, "config1:9");
 53PMU_FORMAT_ATTR(vpu_read1, "config1:16");
 54PMU_FORMAT_ATTR(vpu_read2, "config1:17");
 55PMU_FORMAT_ATTR(vpu_read3, "config1:18");
 56PMU_FORMAT_ATTR(vpu_write1, "config1:19");
 57PMU_FORMAT_ATTR(vpu_write2, "config1:20");
 58PMU_FORMAT_ATTR(vdec, "config1:21");
 59PMU_FORMAT_ATTR(hcodec, "config1:22");
 60PMU_FORMAT_ATTR(ge2d, "config1:23");
 61
 62PMU_FORMAT_ATTR(spicc1, "config1:32");
 63PMU_FORMAT_ATTR(usb0, "config1:33");
 64PMU_FORMAT_ATTR(dma, "config1:34");
 65PMU_FORMAT_ATTR(arb0, "config1:35");
 66PMU_FORMAT_ATTR(sd_emmc_b, "config1:36");
 67PMU_FORMAT_ATTR(usb1, "config1:37");
 68PMU_FORMAT_ATTR(audio, "config1:38");
 69PMU_FORMAT_ATTR(aififo, "config1:39");
 70PMU_FORMAT_ATTR(parser, "config1:41");
 71PMU_FORMAT_ATTR(ao_cpu, "config1:42");
 72PMU_FORMAT_ATTR(sd_emmc_c, "config1:43");
 73PMU_FORMAT_ATTR(spicc2, "config1:44");
 74PMU_FORMAT_ATTR(ethernet, "config1:45");
 75PMU_FORMAT_ATTR(sana, "config1:46");
 76
 77/* for sm1 and g12b */
 78PMU_FORMAT_ATTR(nna, "config1:10");
 79
 80/* for g12b only */
 81PMU_FORMAT_ATTR(gdc, "config1:11");
 82PMU_FORMAT_ATTR(mipi_isp, "config1:12");
 83PMU_FORMAT_ATTR(arm1, "config1:13");
 84PMU_FORMAT_ATTR(sd_emmc_a, "config1:40");
 85
 86static struct attribute *g12_pmu_format_attrs[] = {
 87	&format_attr_event.attr,
 88	&format_attr_arm.attr,
 89	&format_attr_gpu.attr,
 90	&format_attr_nna.attr,
 91	&format_attr_gdc.attr,
 92	&format_attr_arm1.attr,
 93	&format_attr_mipi_isp.attr,
 94	&format_attr_sd_emmc_a.attr,
 95	&format_attr_pcie.attr,
 96	&format_attr_hdcp.attr,
 97	&format_attr_hevc_front.attr,
 98	&format_attr_usb3_0.attr,
 99	&format_attr_device.attr,
100	&format_attr_hevc_back.attr,
101	&format_attr_h265enc.attr,
102	&format_attr_vpu_read1.attr,
103	&format_attr_vpu_read2.attr,
104	&format_attr_vpu_read3.attr,
105	&format_attr_vpu_write1.attr,
106	&format_attr_vpu_write2.attr,
107	&format_attr_vdec.attr,
108	&format_attr_hcodec.attr,
109	&format_attr_ge2d.attr,
110	&format_attr_spicc1.attr,
111	&format_attr_usb0.attr,
112	&format_attr_dma.attr,
113	&format_attr_arb0.attr,
114	&format_attr_sd_emmc_b.attr,
115	&format_attr_usb1.attr,
116	&format_attr_audio.attr,
117	&format_attr_aififo.attr,
118	&format_attr_parser.attr,
119	&format_attr_ao_cpu.attr,
120	&format_attr_sd_emmc_c.attr,
121	&format_attr_spicc2.attr,
122	&format_attr_ethernet.attr,
123	&format_attr_sana.attr,
124	NULL,
125};
126
127/* calculate ddr clock */
128static unsigned long dmc_g12_get_freq_quick(struct dmc_info *info)
129{
130	unsigned int val;
131	unsigned int n, m, od1;
132	unsigned int od_div = 0xfff;
133	unsigned long freq = 0;
134
135	val = readl(info->pll_reg);
136	val = val & 0xfffff;
137	switch ((val >> 16) & 7) {
138	case 0:
139		od_div = 2;
140		break;
141
142	case 1:
143		od_div = 3;
144		break;
145
146	case 2:
147		od_div = 4;
148		break;
149
150	case 3:
151		od_div = 6;
152		break;
153
154	case 4:
155		od_div = 8;
156		break;
157
158	default:
159		break;
160	}
161
162	m = val & 0x1ff;
163	n = ((val >> 10) & 0x1f);
164	od1 = (((val >> 19) & 0x1)) == 1 ? 2 : 1;
165	freq = DEFAULT_XTAL_FREQ / 1000;        /* avoid overflow */
166	if (n)
167		freq = ((((freq * m) / n) >> od1) / od_div) * 1000;
168
169	return freq;
170}
171
172#ifdef DEBUG
173static void g12_dump_reg(struct dmc_info *db)
174{
175	int s = 0, i;
176	unsigned int r;
177
178	for (i = 0; i < 9; i++) {
179		r  = readl(db->ddr_reg[0] + (DMC_MON_G12_CTRL0 + (i << 2)));
180		pr_notice("DMC_MON_CTRL%d:        %08x\n", i, r);
181	}
182	r  = readl(db->ddr_reg[0] + DMC_MON_G12_ALL_REQ_CNT);
183	pr_notice("DMC_MON_ALL_REQ_CNT:  %08x\n", r);
184	r  = readl(db->ddr_reg[0] + DMC_MON_G12_ALL_GRANT_CNT);
185	pr_notice("DMC_MON_ALL_GRANT_CNT:%08x\n", r);
186	r  = readl(db->ddr_reg[0] + DMC_MON_G12_ONE_GRANT_CNT);
187	pr_notice("DMC_MON_ONE_GRANT_CNT:%08x\n", r);
188	r  = readl(db->ddr_reg[0] + DMC_MON_G12_SEC_GRANT_CNT);
189	pr_notice("DMC_MON_SEC_GRANT_CNT:%08x\n", r);
190	r  = readl(db->ddr_reg[0] + DMC_MON_G12_THD_GRANT_CNT);
191	pr_notice("DMC_MON_THD_GRANT_CNT:%08x\n", r);
192	r  = readl(db->ddr_reg[0] + DMC_MON_G12_FOR_GRANT_CNT);
193	pr_notice("DMC_MON_FOR_GRANT_CNT:%08x\n", r);
194	r  = readl(db->ddr_reg[0] + DMC_MON_G12_TIMER);
195	pr_notice("DMC_MON_TIMER:        %08x\n", r);
196}
197#endif
198
199static void dmc_g12_counter_enable(struct dmc_info *info)
200{
201	unsigned int val;
202	unsigned long clock_count = dmc_g12_get_freq_quick(info) / 10; /* 100ms */
203
204	writel(clock_count, info->ddr_reg[0] + DMC_MON_G12_TIMER);
205
206	val = readl(info->ddr_reg[0] + DMC_MON_G12_CTRL0);
207
208	/* enable all channel */
209	val =  BIT(31) |	/* enable bit */
210	       BIT(20) |	/* use timer  */
211	       0x0f;		/* 4 channels */
212
213	writel(val, info->ddr_reg[0] + DMC_MON_G12_CTRL0);
214
215#ifdef DEBUG
216	g12_dump_reg(info);
217#endif
218}
219
220static void dmc_g12_config_fiter(struct dmc_info *info,
221				 int port, int channel)
222{
223	u32 val;
224	u32 rp[MAX_CHANNEL_NUM] = {DMC_MON_G12_CTRL1, DMC_MON_G12_CTRL3,
225					DMC_MON_G12_CTRL5, DMC_MON_G12_CTRL7};
226	u32 rs[MAX_CHANNEL_NUM] = {DMC_MON_G12_CTRL2, DMC_MON_G12_CTRL4,
227					DMC_MON_G12_CTRL6, DMC_MON_G12_CTRL8};
228	int subport = -1;
229
230	/* clear all port mask */
231	if (port < 0) {
232		writel(0, info->ddr_reg[0] + rp[channel]);
233		writel(0, info->ddr_reg[0] + rs[channel]);
234		return;
235	}
236
237	if (port >= PORT_MAJOR)
238		subport = port - PORT_MAJOR;
239
240	if (subport < 0) {
241		val = readl(info->ddr_reg[0] + rp[channel]);
242		val |=  (1 << port);
243		writel(val, info->ddr_reg[0] + rp[channel]);
244		val = 0xffff;
245		writel(val, info->ddr_reg[0] + rs[channel]);
246	} else {
247		val = BIT(23);		/* select device */
248		writel(val, info->ddr_reg[0] + rp[channel]);
249		val = readl(info->ddr_reg[0] + rs[channel]);
250		val |= (1 << subport);
251		writel(val, info->ddr_reg[0] + rs[channel]);
252	}
253}
254
255static void dmc_g12_set_axi_filter(struct dmc_info *info, int axi_id, int channel)
256{
257	if (channel > info->hw_info->chann_nr)
258		return;
259
260	dmc_g12_config_fiter(info, axi_id, channel);
261}
262
263static void dmc_g12_counter_disable(struct dmc_info *info)
264{
265	int i;
266
267	/* clear timer */
268	writel(0, info->ddr_reg[0] + DMC_MON_G12_CTRL0);
269	writel(0, info->ddr_reg[0] + DMC_MON_G12_TIMER);
270
271	writel(0, info->ddr_reg[0] + DMC_MON_G12_ALL_REQ_CNT);
272	writel(0, info->ddr_reg[0] + DMC_MON_G12_ALL_GRANT_CNT);
273	writel(0, info->ddr_reg[0] + DMC_MON_G12_ONE_GRANT_CNT);
274	writel(0, info->ddr_reg[0] + DMC_MON_G12_SEC_GRANT_CNT);
275	writel(0, info->ddr_reg[0] + DMC_MON_G12_THD_GRANT_CNT);
276	writel(0, info->ddr_reg[0] + DMC_MON_G12_FOR_GRANT_CNT);
277
278	/* clear port channel mapping */
279	for (i = 0; i < info->hw_info->chann_nr; i++)
280		dmc_g12_config_fiter(info, -1, i);
281}
282
283static void dmc_g12_get_counters(struct dmc_info *info,
284				 struct dmc_counter *counter)
285{
286	int i;
287	unsigned int reg;
288
289	counter->all_cnt = readl(info->ddr_reg[0] + DMC_MON_G12_ALL_GRANT_CNT);
290	counter->all_req   = readl(info->ddr_reg[0] + DMC_MON_G12_ALL_REQ_CNT);
291
292	for (i = 0; i < info->hw_info->chann_nr; i++) {
293		reg = DMC_MON_G12_ONE_GRANT_CNT + (i << 2);
294		counter->channel_cnt[i] = readl(info->ddr_reg[0] + reg);
295	}
296}
297
298static int dmc_g12_irq_handler(struct dmc_info *info,
299			       struct dmc_counter *counter)
300{
301	unsigned int val;
302	int ret = -EINVAL;
303
304	val = readl(info->ddr_reg[0] + DMC_MON_G12_CTRL0);
305	if (val & DMC_QOS_IRQ) {
306		dmc_g12_get_counters(info, counter);
307		/* clear irq flags */
308		writel(val, info->ddr_reg[0] + DMC_MON_G12_CTRL0);
309		ret = 0;
310	}
311	return ret;
312}
313
314static const struct dmc_hw_info g12a_dmc_info = {
315	.enable		= dmc_g12_counter_enable,
316	.disable	= dmc_g12_counter_disable,
317	.irq_handler	= dmc_g12_irq_handler,
318	.get_counters	= dmc_g12_get_counters,
319	.set_axi_filter	= dmc_g12_set_axi_filter,
320
321	.dmc_nr = 1,
322	.chann_nr = 4,
323	.capability = {0X7EFF00FF03DF, 0},
324	.fmt_attr = g12_pmu_format_attrs,
325};
326
327static const struct dmc_hw_info g12b_dmc_info = {
328	.enable		= dmc_g12_counter_enable,
329	.disable	= dmc_g12_counter_disable,
330	.irq_handler	= dmc_g12_irq_handler,
331	.get_counters	= dmc_g12_get_counters,
332	.set_axi_filter	= dmc_g12_set_axi_filter,
333
334	.dmc_nr = 1,
335	.chann_nr = 4,
336	.capability = {0X7FFF00FF3FDF, 0},
337	.fmt_attr = g12_pmu_format_attrs,
338};
339
340static const struct dmc_hw_info sm1_dmc_info = {
341	.enable		= dmc_g12_counter_enable,
342	.disable	= dmc_g12_counter_disable,
343	.irq_handler	= dmc_g12_irq_handler,
344	.get_counters	= dmc_g12_get_counters,
345	.set_axi_filter	= dmc_g12_set_axi_filter,
346
347	.dmc_nr = 1,
348	.chann_nr = 4,
349	.capability = {0X7EFF00FF07DF, 0},
350	.fmt_attr = g12_pmu_format_attrs,
351};
352
353static int g12_ddr_pmu_probe(struct platform_device *pdev)
354{
355	return meson_ddr_pmu_create(pdev);
356}
357
358static void g12_ddr_pmu_remove(struct platform_device *pdev)
359{
360	meson_ddr_pmu_remove(pdev);
361}
362
363static const struct of_device_id meson_ddr_pmu_dt_match[] = {
364	{
365		.compatible = "amlogic,g12a-ddr-pmu",
366		.data = &g12a_dmc_info,
367	},
368	{
369		.compatible = "amlogic,g12b-ddr-pmu",
370		.data = &g12b_dmc_info,
371	},
372	{
373		.compatible = "amlogic,sm1-ddr-pmu",
374		.data = &sm1_dmc_info,
375	},
376	{}
377};
378MODULE_DEVICE_TABLE(of, meson_ddr_pmu_dt_match);
379
380static struct platform_driver g12_ddr_pmu_driver = {
381	.probe = g12_ddr_pmu_probe,
382	.remove = g12_ddr_pmu_remove,
383
384	.driver = {
385		.name = "meson-g12-ddr-pmu",
386		.of_match_table = meson_ddr_pmu_dt_match,
387	},
388};
389
390module_platform_driver(g12_ddr_pmu_driver);
391MODULE_AUTHOR("Jiucheng Xu");
392MODULE_LICENSE("GPL");
393MODULE_DESCRIPTION("Amlogic G12 series SoC DDR PMU");