.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd |
---|
3 | 4 | * 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 | 5 | */ |
---|
14 | 6 | |
---|
15 | 7 | #include <linux/clk.h> |
---|
.. | .. |
---|
26 | 18 | #include <linux/list.h> |
---|
27 | 19 | #include <linux/of.h> |
---|
28 | 20 | |
---|
| 21 | +#include <soc/rockchip/rk3399_grf.h> |
---|
| 22 | + |
---|
29 | 23 | #define PX30_PMUGRF_OS_REG2 0x208 |
---|
30 | 24 | #define PX30_PMUGRF_OS_REG3 0x20c |
---|
| 25 | + |
---|
| 26 | +#define RK3588_PMUGRF_OS_REG(n) (0x200 + (n) * 4) |
---|
31 | 27 | |
---|
32 | 28 | #define RK3128_GRF_SOC_CON0 0x140 |
---|
33 | 29 | #define RK3128_GRF_OS_REG1 0x1cc |
---|
.. | .. |
---|
56 | 52 | #define RK3368_DFI_EN (0x30003 << 5) |
---|
57 | 53 | #define RK3368_DFI_DIS (0x30000 << 5) |
---|
58 | 54 | |
---|
59 | | -#define MAX_DMC_NUM_CH 2 |
---|
| 55 | +#define RK3528_PMUGRF_OFFSET 0x70000 |
---|
| 56 | +#define RK3528_PMUGRF_OS_REG18 0x248 |
---|
| 57 | +#define RK3528_PMUGRF_OS_REG19 0x24c |
---|
| 58 | + |
---|
| 59 | +#define MAX_DMC_NUM_CH 4 |
---|
60 | 60 | #define READ_DRAMTYPE_INFO(n) (((n) >> 13) & 0x7) |
---|
61 | 61 | #define READ_CH_INFO(n) (((n) >> 28) & 0x3) |
---|
62 | 62 | #define READ_DRAMTYPE_INFO_V3(n, m) ((((n) >> 13) & 0x7) | ((((m) >> 12) & 0x3) << 3)) |
---|
63 | 63 | #define READ_SYSREG_VERSION(m) (((m) >> 28) & 0xf) |
---|
| 64 | +#define READ_LP5_BANK_MODE(m) (((m) >> 1) & 0x3) |
---|
| 65 | +#define READ_LP5_CKR(m) (((m) >> 0) & 0x1) |
---|
64 | 66 | /* DDRMON_CTRL */ |
---|
65 | 67 | #define DDRMON_CTRL 0x04 |
---|
66 | | -#define CLR_DDRMON_CTRL (0x3f0000 << 0) |
---|
| 68 | +#define CLR_DDRMON_CTRL (0xffff0000 << 0) |
---|
| 69 | +#define LPDDR5_BANK_MODE(m) ((0x30000 | ((m) & 0x3)) << 7) |
---|
| 70 | +#define LPDDR5_EN (0x10001 << 6) |
---|
67 | 71 | #define DDR4_EN (0x10001 << 5) |
---|
68 | 72 | #define LPDDR4_EN (0x10001 << 4) |
---|
69 | 73 | #define HARDWARE_EN (0x10001 << 3) |
---|
.. | .. |
---|
87 | 91 | LPDDR3 = 6, |
---|
88 | 92 | LPDDR4 = 7, |
---|
89 | 93 | LPDDR4X = 8, |
---|
| 94 | + LPDDR5 = 9, |
---|
| 95 | + DDR5 = 10, |
---|
90 | 96 | UNUSED = 0xFF |
---|
91 | 97 | }; |
---|
92 | 98 | |
---|
93 | 99 | struct dmc_usage { |
---|
94 | | - u32 access; |
---|
95 | | - u32 total; |
---|
| 100 | + u64 access; |
---|
| 101 | + u64 total; |
---|
96 | 102 | }; |
---|
97 | 103 | |
---|
98 | 104 | /* |
---|
.. | .. |
---|
111 | 117 | struct regmap *regmap_pmugrf; |
---|
112 | 118 | struct clk *clk; |
---|
113 | 119 | u32 dram_type; |
---|
| 120 | + u32 mon_idx; |
---|
| 121 | + u32 count_rate; |
---|
| 122 | + u32 dram_dynamic_info_reg; |
---|
| 123 | + /* 0: BG mode, 1: 16 Bank mode, 2: 8 bank mode */ |
---|
| 124 | + u32 lp5_bank_mode; |
---|
| 125 | + /* 0: clk:dqs = 1:2, 1: 1:4 */ |
---|
| 126 | + u32 lp5_ckr; |
---|
114 | 127 | /* |
---|
115 | 128 | * available mask, 1: available, 0: not available |
---|
116 | 129 | * each bit represent a channel |
---|
.. | .. |
---|
347 | 360 | { |
---|
348 | 361 | struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); |
---|
349 | 362 | void __iomem *dfi_regs = info->regs; |
---|
| 363 | + u32 mon_idx = 0, val_6 = 0; |
---|
| 364 | + u32 i; |
---|
350 | 365 | |
---|
351 | | - /* clear DDRMON_CTRL setting */ |
---|
352 | | - writel_relaxed(CLR_DDRMON_CTRL, dfi_regs + DDRMON_CTRL); |
---|
| 366 | + if (info->mon_idx) |
---|
| 367 | + mon_idx = info->mon_idx; |
---|
353 | 368 | |
---|
354 | | - /* set ddr type to dfi */ |
---|
355 | | - if (info->dram_type == LPDDR3 || info->dram_type == LPDDR2) |
---|
356 | | - writel_relaxed(LPDDR2_3_EN, dfi_regs + DDRMON_CTRL); |
---|
357 | | - else if (info->dram_type == LPDDR4 || info->dram_type == LPDDR4X) |
---|
358 | | - writel_relaxed(LPDDR4_EN, dfi_regs + DDRMON_CTRL); |
---|
359 | | - else if (info->dram_type == DDR4) |
---|
360 | | - writel_relaxed(DDR4_EN, dfi_regs + DDRMON_CTRL); |
---|
| 369 | + if (info->dram_dynamic_info_reg) |
---|
| 370 | + regmap_read(info->regmap_pmugrf, info->dram_dynamic_info_reg, &val_6); |
---|
361 | 371 | |
---|
362 | | - /* enable count, use software mode */ |
---|
363 | | - writel_relaxed(SOFTWARE_EN, dfi_regs + DDRMON_CTRL); |
---|
| 372 | + if (info->dram_type == LPDDR5) { |
---|
| 373 | + info->lp5_bank_mode = READ_LP5_BANK_MODE(val_6); |
---|
| 374 | + info->lp5_ckr = READ_LP5_CKR(val_6); |
---|
| 375 | + } |
---|
| 376 | + |
---|
| 377 | + for (i = 0; i < MAX_DMC_NUM_CH; i++) { |
---|
| 378 | + if (!(info->ch_msk & BIT(i))) |
---|
| 379 | + continue; |
---|
| 380 | + /* clear DDRMON_CTRL setting */ |
---|
| 381 | + writel_relaxed(CLR_DDRMON_CTRL, dfi_regs + i * mon_idx + DDRMON_CTRL); |
---|
| 382 | + |
---|
| 383 | + /* set ddr type to dfi */ |
---|
| 384 | + if (info->dram_type == LPDDR3 || info->dram_type == LPDDR2) |
---|
| 385 | + writel_relaxed(LPDDR2_3_EN, dfi_regs + i * mon_idx + DDRMON_CTRL); |
---|
| 386 | + else if (info->dram_type == LPDDR4 || info->dram_type == LPDDR4X) |
---|
| 387 | + writel_relaxed(LPDDR4_EN, dfi_regs + i * mon_idx + DDRMON_CTRL); |
---|
| 388 | + else if (info->dram_type == DDR4) |
---|
| 389 | + writel_relaxed(DDR4_EN, dfi_regs + i * mon_idx + DDRMON_CTRL); |
---|
| 390 | + else if (info->dram_type == LPDDR5) |
---|
| 391 | + writel_relaxed(LPDDR5_EN | LPDDR5_BANK_MODE(info->lp5_bank_mode), |
---|
| 392 | + dfi_regs + i * mon_idx + DDRMON_CTRL); |
---|
| 393 | + |
---|
| 394 | + /* enable count, use software mode */ |
---|
| 395 | + writel_relaxed(SOFTWARE_EN, dfi_regs + i * mon_idx + DDRMON_CTRL); |
---|
| 396 | + } |
---|
364 | 397 | } |
---|
365 | 398 | |
---|
366 | 399 | static void rockchip_dfi_stop_hardware_counter(struct devfreq_event_dev *edev) |
---|
367 | 400 | { |
---|
368 | 401 | struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); |
---|
369 | 402 | void __iomem *dfi_regs = info->regs; |
---|
| 403 | + u32 mon_idx = 0, i; |
---|
370 | 404 | |
---|
371 | | - writel_relaxed(SOFTWARE_DIS, dfi_regs + DDRMON_CTRL); |
---|
| 405 | + if (info->mon_idx) |
---|
| 406 | + mon_idx = info->mon_idx; |
---|
| 407 | + |
---|
| 408 | + for (i = 0; i < MAX_DMC_NUM_CH; i++) { |
---|
| 409 | + if (!(info->ch_msk & BIT(i))) |
---|
| 410 | + continue; |
---|
| 411 | + writel_relaxed(SOFTWARE_DIS, dfi_regs + i * mon_idx + DDRMON_CTRL); |
---|
| 412 | + } |
---|
372 | 413 | } |
---|
373 | 414 | |
---|
374 | 415 | static int rockchip_dfi_get_busier_ch(struct devfreq_event_dev *edev) |
---|
.. | .. |
---|
377 | 418 | u32 tmp, max = 0; |
---|
378 | 419 | u32 i, busier_ch = 0; |
---|
379 | 420 | void __iomem *dfi_regs = info->regs; |
---|
| 421 | + u32 mon_idx = 0x20, count_rate = 1; |
---|
380 | 422 | |
---|
381 | 423 | rockchip_dfi_stop_hardware_counter(edev); |
---|
| 424 | + |
---|
| 425 | + if (info->mon_idx) |
---|
| 426 | + mon_idx = info->mon_idx; |
---|
| 427 | + if (info->count_rate) |
---|
| 428 | + count_rate = info->count_rate; |
---|
382 | 429 | |
---|
383 | 430 | /* Find out which channel is busier */ |
---|
384 | 431 | for (i = 0; i < MAX_DMC_NUM_CH; i++) { |
---|
385 | 432 | if (!(info->ch_msk & BIT(i))) |
---|
386 | 433 | continue; |
---|
387 | 434 | |
---|
| 435 | + /* rk3588 counter is dfi clk rate */ |
---|
388 | 436 | info->ch_usage[i].total = readl_relaxed(dfi_regs + |
---|
389 | | - DDRMON_CH0_COUNT_NUM + i * 20); |
---|
| 437 | + DDRMON_CH0_COUNT_NUM + i * mon_idx) * count_rate; |
---|
390 | 438 | |
---|
391 | | - /* LPDDR4 and LPDDR4X BL = 16,other DDR type BL = 8 */ |
---|
| 439 | + /* LPDDR5 LPDDR4 and LPDDR4X BL = 16,other DDR type BL = 8 */ |
---|
392 | 440 | tmp = readl_relaxed(dfi_regs + |
---|
393 | | - DDRMON_CH0_DFI_ACCESS_NUM + i * 20); |
---|
| 441 | + DDRMON_CH0_DFI_ACCESS_NUM + i * mon_idx); |
---|
394 | 442 | if (info->dram_type == LPDDR4 || info->dram_type == LPDDR4X) |
---|
395 | 443 | tmp *= 8; |
---|
| 444 | + else if (info->dram_type == LPDDR5) |
---|
| 445 | + tmp *= 16 / (4 << info->lp5_ckr); |
---|
396 | 446 | else |
---|
397 | 447 | tmp *= 4; |
---|
398 | 448 | info->ch_usage[i].access = tmp; |
---|
.. | .. |
---|
464 | 514 | .get_event = rockchip_dfi_get_event, |
---|
465 | 515 | .set_event = rockchip_dfi_set_event, |
---|
466 | 516 | }; |
---|
| 517 | + |
---|
| 518 | +static __maybe_unused __init int rk3588_dfi_init(struct platform_device *pdev, |
---|
| 519 | + struct rockchip_dfi *data, |
---|
| 520 | + struct devfreq_event_desc *desc) |
---|
| 521 | +{ |
---|
| 522 | + struct device_node *np = pdev->dev.of_node; |
---|
| 523 | + struct resource *res; |
---|
| 524 | + u32 val_2, val_3, val_4; |
---|
| 525 | + |
---|
| 526 | + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
| 527 | + data->regs = devm_ioremap_resource(&pdev->dev, res); |
---|
| 528 | + if (IS_ERR(data->regs)) |
---|
| 529 | + return PTR_ERR(data->regs); |
---|
| 530 | + |
---|
| 531 | + data->regmap_pmugrf = syscon_regmap_lookup_by_phandle(np, "rockchip,pmu_grf"); |
---|
| 532 | + if (IS_ERR(data->regmap_pmugrf)) |
---|
| 533 | + return PTR_ERR(data->regmap_pmugrf); |
---|
| 534 | + |
---|
| 535 | + regmap_read(data->regmap_pmugrf, RK3588_PMUGRF_OS_REG(2), &val_2); |
---|
| 536 | + regmap_read(data->regmap_pmugrf, RK3588_PMUGRF_OS_REG(3), &val_3); |
---|
| 537 | + regmap_read(data->regmap_pmugrf, RK3588_PMUGRF_OS_REG(4), &val_4); |
---|
| 538 | + if (READ_SYSREG_VERSION(val_3) >= 0x3) |
---|
| 539 | + data->dram_type = READ_DRAMTYPE_INFO_V3(val_2, val_3); |
---|
| 540 | + else |
---|
| 541 | + data->dram_type = READ_DRAMTYPE_INFO(val_2); |
---|
| 542 | + |
---|
| 543 | + data->mon_idx = 0x4000; |
---|
| 544 | + if (data->dram_type == LPDDR5) |
---|
| 545 | + data->count_rate = 1; |
---|
| 546 | + else |
---|
| 547 | + data->count_rate = 2; |
---|
| 548 | + data->dram_dynamic_info_reg = RK3588_PMUGRF_OS_REG(6); |
---|
| 549 | + data->ch_msk = READ_CH_INFO(val_2) | READ_CH_INFO(val_4) << 2; |
---|
| 550 | + data->clk = NULL; |
---|
| 551 | + |
---|
| 552 | + desc->ops = &rockchip_dfi_ops; |
---|
| 553 | + |
---|
| 554 | + return 0; |
---|
| 555 | +} |
---|
467 | 556 | |
---|
468 | 557 | static __maybe_unused __init int px30_dfi_init(struct platform_device *pdev, |
---|
469 | 558 | struct rockchip_dfi *data, |
---|
.. | .. |
---|
577 | 666 | struct devfreq_event_desc *desc) |
---|
578 | 667 | { |
---|
579 | 668 | struct device *dev = &pdev->dev; |
---|
580 | | - struct resource *res; |
---|
581 | 669 | struct device_node *np = pdev->dev.of_node, *node; |
---|
582 | 670 | u32 val; |
---|
583 | 671 | |
---|
584 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
585 | | - data->regs = devm_ioremap_resource(&pdev->dev, res); |
---|
| 672 | + data->regs = devm_platform_ioremap_resource(pdev, 0); |
---|
586 | 673 | if (IS_ERR(data->regs)) |
---|
587 | 674 | return PTR_ERR(data->regs); |
---|
588 | 675 | |
---|
.. | .. |
---|
596 | 683 | node = of_parse_phandle(np, "rockchip,pmu", 0); |
---|
597 | 684 | if (node) { |
---|
598 | 685 | data->regmap_pmu = syscon_node_to_regmap(node); |
---|
| 686 | + of_node_put(node); |
---|
599 | 687 | if (IS_ERR(data->regmap_pmu)) |
---|
600 | 688 | return PTR_ERR(data->regmap_pmu); |
---|
601 | 689 | } |
---|
.. | .. |
---|
639 | 727 | return 0; |
---|
640 | 728 | } |
---|
641 | 729 | |
---|
| 730 | +static __maybe_unused __init int rk3528_dfi_init(struct platform_device *pdev, |
---|
| 731 | + struct rockchip_dfi *data, |
---|
| 732 | + struct devfreq_event_desc *desc) |
---|
| 733 | +{ |
---|
| 734 | + struct device_node *np = pdev->dev.of_node, *node; |
---|
| 735 | + struct resource *res; |
---|
| 736 | + u32 val_18, val_19; |
---|
| 737 | + |
---|
| 738 | + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
| 739 | + data->regs = devm_ioremap_resource(&pdev->dev, res); |
---|
| 740 | + if (IS_ERR(data->regs)) |
---|
| 741 | + return PTR_ERR(data->regs); |
---|
| 742 | + |
---|
| 743 | + node = of_parse_phandle(np, "rockchip,grf", 0); |
---|
| 744 | + if (node) { |
---|
| 745 | + data->regmap_grf = syscon_node_to_regmap(node); |
---|
| 746 | + if (IS_ERR(data->regmap_grf)) |
---|
| 747 | + return PTR_ERR(data->regmap_grf); |
---|
| 748 | + } |
---|
| 749 | + |
---|
| 750 | + regmap_read(data->regmap_grf, RK3528_PMUGRF_OFFSET + RK3528_PMUGRF_OS_REG18, &val_18); |
---|
| 751 | + regmap_read(data->regmap_grf, RK3528_PMUGRF_OFFSET + RK3528_PMUGRF_OS_REG19, &val_19); |
---|
| 752 | + if (READ_SYSREG_VERSION(val_19) >= 0x3) |
---|
| 753 | + data->dram_type = READ_DRAMTYPE_INFO_V3(val_18, val_19); |
---|
| 754 | + else |
---|
| 755 | + data->dram_type = READ_DRAMTYPE_INFO(val_18); |
---|
| 756 | + data->count_rate = 2; |
---|
| 757 | + data->ch_msk = 1; |
---|
| 758 | + data->clk = NULL; |
---|
| 759 | + |
---|
| 760 | + desc->ops = &rockchip_dfi_ops; |
---|
| 761 | + |
---|
| 762 | + return 0; |
---|
| 763 | +} |
---|
| 764 | + |
---|
642 | 765 | static const struct of_device_id rockchip_dfi_id_match[] = { |
---|
643 | 766 | #ifdef CONFIG_CPU_PX30 |
---|
644 | 767 | { .compatible = "rockchip,px30-dfi", .data = px30_dfi_init }, |
---|
.. | .. |
---|
661 | 784 | #ifdef CONFIG_CPU_RK3399 |
---|
662 | 785 | { .compatible = "rockchip,rk3399-dfi", .data = rockchip_dfi_init }, |
---|
663 | 786 | #endif |
---|
| 787 | +#ifdef CONFIG_CPU_RK3528 |
---|
| 788 | + { .compatible = "rockchip,rk3528-dfi", .data = rk3528_dfi_init }, |
---|
| 789 | +#endif |
---|
| 790 | +#ifdef CONFIG_CPU_RK3562 |
---|
| 791 | + { .compatible = "rockchip,rk3562-dfi", .data = px30_dfi_init }, |
---|
| 792 | +#endif |
---|
664 | 793 | #ifdef CONFIG_CPU_RK3568 |
---|
665 | 794 | { .compatible = "rockchip,rk3568-dfi", .data = px30_dfi_init }, |
---|
666 | 795 | #endif |
---|
| 796 | +#ifdef CONFIG_CPU_RK3588 |
---|
| 797 | + { .compatible = "rockchip,rk3588-dfi", .data = rk3588_dfi_init }, |
---|
| 798 | +#endif |
---|
667 | 799 | #ifdef CONFIG_CPU_RV1126 |
---|
668 | 800 | { .compatible = "rockchip,rv1126-dfi", .data = px30_dfi_init }, |
---|
669 | 801 | #endif |
---|