394 lines · c
1// SPDX-License-Identifier: GPL-2.02/*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 3219#define DEFAULT_XTAL_FREQ 24000000UL20 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 DEBUG173static 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#endif198 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 DEBUG216 g12_dump_reg(info);217#endif218}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_new = 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");394