All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2] memory: mediatek: add DRAM controller driver
@ 2021-04-16  9:32 ` Po-Kai Chi
  0 siblings, 0 replies; 22+ messages in thread
From: Po-Kai Chi @ 2021-04-16  9:32 UTC (permalink / raw)
  To: Matthias Brugger
  Cc: linux-kernel, linux-mediatek, devicetree, wsd_upstream, CC Hwang

These patch series introduces the MediaTek DRAM controller driver (DRAMC)
on MT6779 SoC, and enables to be built as a module by default for the
ARM64 builds.

MediaTek DRAMC driver provides cross-platform features as below:
  - API provided to other kernel modules for querying DRAM type,
    rank count, rank size, channel count and mode register settings.
  - Sysfs interface used to pass DRAM mode register settings and current
    DRAM data rate to user-space for MediaTek ecosystem.

The API user includes MediaTek External Memory Interface (EMI) and
DVFS Resource Control (DVFSRC), which will be sent to mainline later.

Changes since v1:
- add prefix to vendor properties in device tree, dt-binding and drivers
- fix dt-binding check fail

Po-Kai Chi (4):
  dt-bindings: memory: Add binding for MediaTek DRAM Controller
  memory: mediatek: add DRAM controller driver
  arm64: dts: add DRAMC node for MT6779
  arm64: defconfig: Enable MediaTek DRAMC common driver

 .../memory-controllers/mediatek,dramc.yaml    | 162 ++++
 arch/arm64/boot/dts/mediatek/mt6779-evb.dts   |   9 +
 arch/arm64/boot/dts/mediatek/mt6779.dtsi      |  18 +
 arch/arm64/configs/defconfig                  |   1 +
 drivers/memory/Kconfig                        |   1 +
 drivers/memory/Makefile                       |   1 +
 drivers/memory/mediatek/Kconfig               |   9 +
 drivers/memory/mediatek/Makefile              |   3 +
 drivers/memory/mediatek/mtk-dramc.c           | 711 ++++++++++++++++++
 include/memory/mediatek/dramc.h               |  18 +
 10 files changed, 933 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/memory-controllers/mediatek,dramc.yaml
 create mode 100644 drivers/memory/mediatek/Kconfig
 create mode 100644 drivers/memory/mediatek/Makefile
 create mode 100644 drivers/memory/mediatek/mtk-dramc.c
 create mode 100644 include/memory/mediatek/dramc.h


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2] memory: mediatek: add DRAM controller driver
@ 2021-04-16  9:32 ` Po-Kai Chi
  0 siblings, 0 replies; 22+ messages in thread
From: Po-Kai Chi @ 2021-04-16  9:32 UTC (permalink / raw)
  To: Matthias Brugger
  Cc: linux-kernel, linux-mediatek, devicetree, wsd_upstream, CC Hwang

These patch series introduces the MediaTek DRAM controller driver (DRAMC)
on MT6779 SoC, and enables to be built as a module by default for the
ARM64 builds.

MediaTek DRAMC driver provides cross-platform features as below:
  - API provided to other kernel modules for querying DRAM type,
    rank count, rank size, channel count and mode register settings.
  - Sysfs interface used to pass DRAM mode register settings and current
    DRAM data rate to user-space for MediaTek ecosystem.

The API user includes MediaTek External Memory Interface (EMI) and
DVFS Resource Control (DVFSRC), which will be sent to mainline later.

Changes since v1:
- add prefix to vendor properties in device tree, dt-binding and drivers
- fix dt-binding check fail

Po-Kai Chi (4):
  dt-bindings: memory: Add binding for MediaTek DRAM Controller
  memory: mediatek: add DRAM controller driver
  arm64: dts: add DRAMC node for MT6779
  arm64: defconfig: Enable MediaTek DRAMC common driver

 .../memory-controllers/mediatek,dramc.yaml    | 162 ++++
 arch/arm64/boot/dts/mediatek/mt6779-evb.dts   |   9 +
 arch/arm64/boot/dts/mediatek/mt6779.dtsi      |  18 +
 arch/arm64/configs/defconfig                  |   1 +
 drivers/memory/Kconfig                        |   1 +
 drivers/memory/Makefile                       |   1 +
 drivers/memory/mediatek/Kconfig               |   9 +
 drivers/memory/mediatek/Makefile              |   3 +
 drivers/memory/mediatek/mtk-dramc.c           | 711 ++++++++++++++++++
 include/memory/mediatek/dramc.h               |  18 +
 10 files changed, 933 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/memory-controllers/mediatek,dramc.yaml
 create mode 100644 drivers/memory/mediatek/Kconfig
 create mode 100644 drivers/memory/mediatek/Makefile
 create mode 100644 drivers/memory/mediatek/mtk-dramc.c
 create mode 100644 include/memory/mediatek/dramc.h
_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 1/4] dt-bindings: memory: Add binding for MediaTek DRAM Controller
  2021-04-16  9:32 ` Po-Kai Chi
@ 2021-04-16  9:32   ` Po-Kai Chi
  -1 siblings, 0 replies; 22+ messages in thread
From: Po-Kai Chi @ 2021-04-16  9:32 UTC (permalink / raw)
  To: Matthias Brugger
  Cc: linux-kernel, linux-mediatek, devicetree, wsd_upstream, CC Hwang,
	Po-Kai Chi

This patch adds the documentation of the device-tree binding for
MediaTek DRAM Controller.

Signed-off-by: Po-Kai Chi <pk.chi@mediatek.com>
---
 .../memory-controllers/mediatek,dramc.yaml         |  162 ++++++++++++++++++++
 1 file changed, 162 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/memory-controllers/mediatek,dramc.yaml

diff --git a/Documentation/devicetree/bindings/memory-controllers/mediatek,dramc.yaml b/Documentation/devicetree/bindings/memory-controllers/mediatek,dramc.yaml
new file mode 100644
index 0000000..18f77c2
--- /dev/null
+++ b/Documentation/devicetree/bindings/memory-controllers/mediatek,dramc.yaml
@@ -0,0 +1,162 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (c) 2021 MediaTek Inc.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/memory-controllers/mediatek,dramc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek DRAM Controller
+
+maintainers:
+  - Po-Kai Chi <pk.chi@mediatek.com>
+
+description: |
+  MediaTek DRAM controller (DRAMC) provides an interface to query information
+  about DRAM which collected from bootloader and device tree.
+  This is mainly used by MediaTek Extended Memory Interface (EMI) and DVFS Resource
+  Control (DVFSRC).
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - mediatek,mt6779-dramc
+
+  reg:
+    description:
+      Base address of MediaTek DRAM related hardware modules, each channel has
+      its own base address in order of
+      DRAMC_AO_{CH}, DRAMC_NAO_{CH}, DDRPHY_AO_{CH}.
+    minItems: 3            # 3 * N channels
+    maxItems: 6
+
+  mediatek,dram-type:
+    description:
+      The DRAM type of current DRAM chip.
+      This property is filled in by bootloader according to the board hardware
+      configuration.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 7
+
+  mediatek,support-channel-cnt:
+    description:
+      The maximum DRAM channel count supported by SoC.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 1
+    maximum: 4
+
+  mediatek,channel-cnt:
+    description:
+      The DRAM channel count of current DRAM chip.
+      This property is filled in by bootloader according to the board hardware
+      configuration.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 1
+    maximum: 4
+
+  mediatek,rank-cnt:
+    description:
+      The DRAM rank count of current DRAM chip.
+      This property is filled in by bootloader according to the board hardware
+      configuration.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 1
+    maximum: 2
+
+  mediatek,rank-size:
+    description:
+      An array of 64-bits unsigned integer that describes the size of each
+      DRAM rank.
+      This property is filled in by bootloader according to the board hardware
+      configuration.
+    $ref: /schemas/types.yaml#/definitions/uint64-array
+    minItems: 1
+    maxItems: 2
+    items:
+      minimum: 0x0
+      maximum: 0x100000000    # support up to 4GB in single rank
+
+  mediatek,mr-cnt:
+    description:
+      Specifies how many sets of DRAM mode register information to provide.
+      This property is filled in by bootloader according to the board hardware
+      configuration.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    maximum: 40    # total 40 MRs for JEDEC LPDDR4X
+
+  mediatek,mr:
+    description:
+      Pair of DRAM mode register information.
+      This property is filled in by bootloader according to the board hardware
+      configuration.
+    $ref: /schemas/types.yaml#/definitions/uint32-matrix
+    maxItems: 40    # align with mediatek,mr-cnt
+    items:
+      items:
+        - description:
+            Mode register index
+        - description:
+            Mode register value
+
+  mediatek,freq-cnt:
+    description:
+      Specifies how many sets of DRAM data clock rate supported by SoC.
+    $ref: /schemas/types.yaml#/definitions/uint32
+
+  mediatek,freq-step:
+    description:
+      The DRAM data clock rate may be slightly different from those defined
+      by the specification due to errors in multiples of the base frequency.
+      This describe the mapping from real data clock rate measured by
+      frequency meter to JEDEC data clock rate.
+    $ref: /schemas/types.yaml#/definitions/uint32-matrix
+    items:
+      items:
+        - description:
+            Real data rate
+        - description:
+            Spec data rate
+
+required:
+  - compatible
+  - reg
+  - mediatek,dram-type
+  - mediatek,support-channel-cnt
+  - mediatek,channel-cnt
+  - mediatek,rank-cnt
+  - mediatek,mr-cnt
+  - mediatek,freq-cnt
+
+additionalProperties: false
+
+examples:
+  - |
+    soc {
+        #address-cells = <2>;
+        #size-cells = <2>;
+
+        dramc@10230000 {
+            compatible = "mediatek,mt6779-dramc";
+            reg = <0 0x10230000 0 0x2000>, /* DRAMC AO CHA */
+                <0 0x10240000 0 0x2000>,   /* DRAMC AO CHB */
+                <0 0x10234000 0 0x1000>,   /* DRAMC NAO CHA */
+                <0 0x10244000 0 0x1000>,   /* DRAMC NAO CHB */
+                <0 0x10238000 0 0x2000>,   /* DDRPHY AO CHA */
+                <0 0x10248000 0 0x2000>;   /* DDRPHY AO CHB */
+            mediatek,dram-type = <0>;
+            mediatek,support-channel-cnt = <2>;
+            mediatek,channel-cnt = <2>;
+            mediatek,rank-cnt = <2>;
+            mediatek,rank-size = <0x40000000 0x40000000>;
+            mediatek,mr-cnt = <1>;
+            mediatek,mr = <0x5 0xff>;
+            mediatek,freq-cnt = <6>;
+            mediatek,freq-step = <3718 3733>,
+                    <3094 3200>,
+                    <2392 2400>,
+                    <1534 1600>,
+                    <1196 1200>,
+                    <754 800>;
+        };
+    };
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 1/4] dt-bindings: memory: Add binding for MediaTek DRAM Controller
@ 2021-04-16  9:32   ` Po-Kai Chi
  0 siblings, 0 replies; 22+ messages in thread
From: Po-Kai Chi @ 2021-04-16  9:32 UTC (permalink / raw)
  To: Matthias Brugger
  Cc: linux-kernel, linux-mediatek, devicetree, wsd_upstream, CC Hwang,
	Po-Kai Chi

This patch adds the documentation of the device-tree binding for
MediaTek DRAM Controller.

Signed-off-by: Po-Kai Chi <pk.chi@mediatek.com>
---
 .../memory-controllers/mediatek,dramc.yaml         |  162 ++++++++++++++++++++
 1 file changed, 162 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/memory-controllers/mediatek,dramc.yaml

diff --git a/Documentation/devicetree/bindings/memory-controllers/mediatek,dramc.yaml b/Documentation/devicetree/bindings/memory-controllers/mediatek,dramc.yaml
new file mode 100644
index 0000000..18f77c2
--- /dev/null
+++ b/Documentation/devicetree/bindings/memory-controllers/mediatek,dramc.yaml
@@ -0,0 +1,162 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (c) 2021 MediaTek Inc.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/memory-controllers/mediatek,dramc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek DRAM Controller
+
+maintainers:
+  - Po-Kai Chi <pk.chi@mediatek.com>
+
+description: |
+  MediaTek DRAM controller (DRAMC) provides an interface to query information
+  about DRAM which collected from bootloader and device tree.
+  This is mainly used by MediaTek Extended Memory Interface (EMI) and DVFS Resource
+  Control (DVFSRC).
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - mediatek,mt6779-dramc
+
+  reg:
+    description:
+      Base address of MediaTek DRAM related hardware modules, each channel has
+      its own base address in order of
+      DRAMC_AO_{CH}, DRAMC_NAO_{CH}, DDRPHY_AO_{CH}.
+    minItems: 3            # 3 * N channels
+    maxItems: 6
+
+  mediatek,dram-type:
+    description:
+      The DRAM type of current DRAM chip.
+      This property is filled in by bootloader according to the board hardware
+      configuration.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 7
+
+  mediatek,support-channel-cnt:
+    description:
+      The maximum DRAM channel count supported by SoC.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 1
+    maximum: 4
+
+  mediatek,channel-cnt:
+    description:
+      The DRAM channel count of current DRAM chip.
+      This property is filled in by bootloader according to the board hardware
+      configuration.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 1
+    maximum: 4
+
+  mediatek,rank-cnt:
+    description:
+      The DRAM rank count of current DRAM chip.
+      This property is filled in by bootloader according to the board hardware
+      configuration.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 1
+    maximum: 2
+
+  mediatek,rank-size:
+    description:
+      An array of 64-bits unsigned integer that describes the size of each
+      DRAM rank.
+      This property is filled in by bootloader according to the board hardware
+      configuration.
+    $ref: /schemas/types.yaml#/definitions/uint64-array
+    minItems: 1
+    maxItems: 2
+    items:
+      minimum: 0x0
+      maximum: 0x100000000    # support up to 4GB in single rank
+
+  mediatek,mr-cnt:
+    description:
+      Specifies how many sets of DRAM mode register information to provide.
+      This property is filled in by bootloader according to the board hardware
+      configuration.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    maximum: 40    # total 40 MRs for JEDEC LPDDR4X
+
+  mediatek,mr:
+    description:
+      Pair of DRAM mode register information.
+      This property is filled in by bootloader according to the board hardware
+      configuration.
+    $ref: /schemas/types.yaml#/definitions/uint32-matrix
+    maxItems: 40    # align with mediatek,mr-cnt
+    items:
+      items:
+        - description:
+            Mode register index
+        - description:
+            Mode register value
+
+  mediatek,freq-cnt:
+    description:
+      Specifies how many sets of DRAM data clock rate supported by SoC.
+    $ref: /schemas/types.yaml#/definitions/uint32
+
+  mediatek,freq-step:
+    description:
+      The DRAM data clock rate may be slightly different from those defined
+      by the specification due to errors in multiples of the base frequency.
+      This describe the mapping from real data clock rate measured by
+      frequency meter to JEDEC data clock rate.
+    $ref: /schemas/types.yaml#/definitions/uint32-matrix
+    items:
+      items:
+        - description:
+            Real data rate
+        - description:
+            Spec data rate
+
+required:
+  - compatible
+  - reg
+  - mediatek,dram-type
+  - mediatek,support-channel-cnt
+  - mediatek,channel-cnt
+  - mediatek,rank-cnt
+  - mediatek,mr-cnt
+  - mediatek,freq-cnt
+
+additionalProperties: false
+
+examples:
+  - |
+    soc {
+        #address-cells = <2>;
+        #size-cells = <2>;
+
+        dramc@10230000 {
+            compatible = "mediatek,mt6779-dramc";
+            reg = <0 0x10230000 0 0x2000>, /* DRAMC AO CHA */
+                <0 0x10240000 0 0x2000>,   /* DRAMC AO CHB */
+                <0 0x10234000 0 0x1000>,   /* DRAMC NAO CHA */
+                <0 0x10244000 0 0x1000>,   /* DRAMC NAO CHB */
+                <0 0x10238000 0 0x2000>,   /* DDRPHY AO CHA */
+                <0 0x10248000 0 0x2000>;   /* DDRPHY AO CHB */
+            mediatek,dram-type = <0>;
+            mediatek,support-channel-cnt = <2>;
+            mediatek,channel-cnt = <2>;
+            mediatek,rank-cnt = <2>;
+            mediatek,rank-size = <0x40000000 0x40000000>;
+            mediatek,mr-cnt = <1>;
+            mediatek,mr = <0x5 0xff>;
+            mediatek,freq-cnt = <6>;
+            mediatek,freq-step = <3718 3733>,
+                    <3094 3200>,
+                    <2392 2400>,
+                    <1534 1600>,
+                    <1196 1200>,
+                    <754 800>;
+        };
+    };
-- 
1.7.9.5
_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 2/4] memory: mediatek: add DRAM controller driver
  2021-04-16  9:32 ` Po-Kai Chi
@ 2021-04-16  9:32   ` Po-Kai Chi
  -1 siblings, 0 replies; 22+ messages in thread
From: Po-Kai Chi @ 2021-04-16  9:32 UTC (permalink / raw)
  To: Matthias Brugger
  Cc: linux-kernel, linux-mediatek, devicetree, wsd_upstream, CC Hwang,
	Po-Kai Chi

MediaTek DRAM controller (DRAMC) driver provides cross-platform features
as below:

1. provide APIs for low power feature queries
2. create sysfs to pass the DRAM information to user-space

Signed-off-by: Po-Kai Chi <pk.chi@mediatek.com>
---
 drivers/memory/Kconfig              |    1 +
 drivers/memory/Makefile             |    1 +
 drivers/memory/mediatek/Kconfig     |    9 +
 drivers/memory/mediatek/Makefile    |    3 +
 drivers/memory/mediatek/mtk-dramc.c |  711 +++++++++++++++++++++++++++++++++++
 include/memory/mediatek/dramc.h     |   18 +
 6 files changed, 743 insertions(+)
 create mode 100644 drivers/memory/mediatek/Kconfig
 create mode 100644 drivers/memory/mediatek/Makefile
 create mode 100644 drivers/memory/mediatek/mtk-dramc.c
 create mode 100644 include/memory/mediatek/dramc.h

diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index 72c0df1..056e906 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -225,6 +225,7 @@ config STM32_FMC2_EBI
 	  devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on
 	  SOCs containing the FMC2 External Bus Interface.
 
+source "drivers/memory/mediatek/Kconfig"
 source "drivers/memory/samsung/Kconfig"
 source "drivers/memory/tegra/Kconfig"
 
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index bc7663e..cd4f8cf 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_PL353_SMC)		+= pl353-smc.o
 obj-$(CONFIG_RENESAS_RPCIF)	+= renesas-rpc-if.o
 obj-$(CONFIG_STM32_FMC2_EBI)	+= stm32-fmc2-ebi.o
 
+obj-$(CONFIG_MTK_DRAMC)		+= mediatek/
 obj-$(CONFIG_SAMSUNG_MC)	+= samsung/
 obj-$(CONFIG_TEGRA_MC)		+= tegra/
 obj-$(CONFIG_TI_EMIF_SRAM)	+= ti-emif-sram.o
diff --git a/drivers/memory/mediatek/Kconfig b/drivers/memory/mediatek/Kconfig
new file mode 100644
index 0000000..a1618b0
--- /dev/null
+++ b/drivers/memory/mediatek/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config MTK_DRAMC
+	tristate "MediaTek DRAMC driver"
+	help
+	  This selects the MediaTek(R) DRAMC driver.
+	  Provide the API for DRAMC low power scenario, and the interface
+	  for reporting DRAM information, e.g. DRAM mode register (MR) for
+	  DRAM vendor ID, temperature, and density.
diff --git a/drivers/memory/mediatek/Makefile b/drivers/memory/mediatek/Makefile
new file mode 100644
index 0000000..632be48
--- /dev/null
+++ b/drivers/memory/mediatek/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_MTK_DRAMC)	+= mtk-dramc.o
diff --git a/drivers/memory/mediatek/mtk-dramc.c b/drivers/memory/mediatek/mtk-dramc.c
new file mode 100644
index 0000000..155b3b7
--- /dev/null
+++ b/drivers/memory/mediatek/mtk-dramc.c
@@ -0,0 +1,711 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/printk.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <memory/mediatek/dramc.h>
+
+#define DRAMC_DRV_NAME	"mtk-dramc"
+
+struct mr_info_t {
+	unsigned int mr_index;
+	unsigned int mr_value;
+};
+
+/*
+ * struct reg_ctrl_t - to describe the bits required in a register
+ * @offset: register address offset from a base
+ * @mask: bitmask of the target bits
+ * @shift: starting bit of the target bits
+ */
+struct reg_ctrl_t {
+	unsigned int offset;
+	unsigned int mask;
+	unsigned int shift;
+};
+
+struct fmeter_dev_t {
+	unsigned int crystal_freq;
+	unsigned int shu_of;
+	struct reg_ctrl_t shu_lv;
+	struct reg_ctrl_t pll_id;
+	struct reg_ctrl_t pll_md[2];
+	struct reg_ctrl_t sdmpcw[2];
+	struct reg_ctrl_t prediv[2];
+	struct reg_ctrl_t posdiv[2];
+	struct reg_ctrl_t ckdiv4[2];
+	struct reg_ctrl_t cldiv2[2];
+	struct reg_ctrl_t fbksel[2];
+	struct reg_ctrl_t dqopen[2];
+};
+
+struct mr4_dev_t {
+	struct reg_ctrl_t mr4_rg;
+};
+
+struct dramc_dev_t {
+	unsigned int dram_type;
+	unsigned int support_channel_cnt;
+	unsigned int channel_cnt;
+	unsigned int rank_cnt;
+	unsigned int mr_cnt;
+	unsigned int freq_cnt;
+	unsigned int *rank_size;
+	unsigned int *freq_step;
+	struct mr_info_t *mr_info_ptr;
+	void __iomem **dramc_chn_base_ao;
+	void __iomem **dramc_chn_base_nao;
+	void __iomem **ddrphy_chn_base_ao;
+	void *mr4_dev_ptr;
+	void *fmeter_dev_ptr;
+};
+
+enum DRAM_TYPE {
+	TYPE_NONE = 0,
+	TYPE_DDR1,
+	TYPE_LPDDR2,
+	TYPE_LPDDR3,
+	TYPE_PCDDR3,
+	TYPE_LPDDR4,
+	TYPE_LPDDR4X,
+	TYPE_LPDDR4P
+};
+
+static const struct fmeter_dev_t fmeter_v0_mt6779_t = {
+	.crystal_freq = 52,
+	.shu_of = 0x500,
+	.shu_lv = { .offset = 0x00e4, .mask = 0x00000006, .shift = 1 },
+	.pll_id = { .offset = 0x0510, .mask = 0x80000000, .shift = 31 },
+	.pll_md = {
+		{ .offset = 0x0d84, .mask = 0x00000100, .shift = 8 },
+		{ .offset = 0x0d84, .mask = 0x00000100, .shift = 8 },
+	},
+	.sdmpcw = {
+		{ .offset = 0x0d9c, .mask = 0xffff0000, .shift = 16 },
+		{ .offset = 0x0d94, .mask = 0xffff0000, .shift = 16 },
+	},
+	.prediv = {
+		{ .offset = 0x0da8, .mask = 0x000c0000, .shift = 18 },
+		{ .offset = 0x0da0, .mask = 0x000c0000, .shift = 18 },
+	},
+	.posdiv = {
+		{ .offset = 0x0da8, .mask = 0x00000007, .shift = 0 },
+		{ .offset = 0x0da0, .mask = 0x00000007, .shift = 0 },
+	},
+	.ckdiv4 = {
+		{ .offset = 0x0d18, .mask = 0x08000000, .shift = 27 },
+		{ .offset = 0x0d18, .mask = 0x08000000, .shift = 27 },
+	},
+	.cldiv2 = {
+		{ .offset = 0x0c38, .mask = 0x80000000, .shift = 31 },
+		{ .offset = 0x0c38, .mask = 0x80000000, .shift = 31 },
+	},
+};
+
+static const struct mr4_dev_t mr4_v1_mt6779_t = {
+	.mr4_rg = { .offset = 0x0090, .mask = 0x0000ffff, .shift = 0 },
+};
+
+struct mtk_dramc_compatible {
+	const struct fmeter_dev_t *fmeter;
+	const struct mr4_dev_t *mr4;
+};
+
+static const struct mtk_dramc_compatible mt6779_compat = {
+	.fmeter = &fmeter_v0_mt6779_t,
+	.mr4 = &mr4_v1_mt6779_t,
+};
+
+static const struct of_device_id mtk_dramc_of_ids[] = {
+	{ .compatible = "mediatek,mt6779-dramc", .data = &mt6779_compat },
+	{}
+};
+MODULE_DEVICE_TABLE(of, mtk_dramc_of_ids);
+
+/*
+ * mtk_dramc_get_drvdata_by_ids - get the dramc driver data
+ *
+ * Return the dramc driver data
+ */
+static struct dramc_dev_t *mtk_dramc_get_drvdata_by_ids(void)
+{
+	struct device_node *np;
+	struct platform_device *dramc_pdev;
+
+	np = of_find_matching_node_and_match(NULL, mtk_dramc_of_ids, NULL);
+	dramc_pdev = of_find_device_by_node(np);
+
+	if (!dramc_pdev)
+		return NULL;
+
+	return (struct dramc_dev_t *)platform_get_drvdata(dramc_pdev);
+}
+
+static ssize_t mr_show(struct device_driver *driver, char *buf)
+{
+	struct dramc_dev_t *dramc_dev_ptr;
+	struct mr_info_t *mr_info_ptr;
+	unsigned int i;
+	ssize_t ret;
+
+	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
+
+	if (!dramc_dev_ptr)
+		return 0;
+
+	mr_info_ptr = dramc_dev_ptr->mr_info_ptr;
+
+	for (ret = 0, i = 0; i < dramc_dev_ptr->mr_cnt; i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"mr%d: 0x%x\n",
+				mr_info_ptr[i].mr_index,
+				mr_info_ptr[i].mr_value);
+		if (ret >= PAGE_SIZE)
+			return strlen(buf);
+	}
+
+	return strlen(buf);
+}
+
+static ssize_t mr4_show(struct device_driver *driver, char *buf)
+{
+	struct dramc_dev_t *dramc_dev_ptr;
+	unsigned int i;
+	ssize_t ret;
+
+	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
+
+	if (!dramc_dev_ptr)
+		return 0;
+
+	for (ret = 0, i = 0; i < dramc_dev_ptr->channel_cnt; i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"mr4: ch%d 0x%x\n",
+				i, mtk_dramc_get_mr4(i));
+		if (ret >= PAGE_SIZE)
+			return strlen(buf);
+	}
+
+	return strlen(buf);
+}
+
+static ssize_t dram_data_rate_show(struct device_driver *driver, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "DRAM data rate = %d\n",
+			mtk_dramc_get_data_rate());
+}
+
+static DRIVER_ATTR_RO(mr);
+static DRIVER_ATTR_RO(mr4);
+static DRIVER_ATTR_RO(dram_data_rate);
+
+static int dramc_probe(struct platform_device *pdev)
+{
+	struct device_node *dramc_node = pdev->dev.of_node;
+	struct dramc_dev_t *dramc_dev_ptr;
+	const struct mtk_dramc_compatible *dev_comp;
+	struct resource *res;
+	unsigned int i, size;
+	int ret;
+
+	pr_info("%s: module probe.\n", __func__);
+
+	dramc_dev_ptr = devm_kmalloc(&pdev->dev,
+				     sizeof(struct dramc_dev_t),
+				     GFP_KERNEL);
+
+	dev_comp =
+		(struct mtk_dramc_compatible *)
+			of_device_get_match_data(&pdev->dev);
+
+	if (!dramc_dev_ptr)
+		return -ENOMEM;
+
+	ret = of_property_read_u32(dramc_node,
+				   "mediatek,dram-type",
+				   &dramc_dev_ptr->dram_type);
+	if (ret) {
+		pr_info("%s: get dram_type fail\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32(dramc_node,
+				   "mediatek,support-channel-cnt",
+				   &dramc_dev_ptr->support_channel_cnt);
+	if (ret) {
+		pr_info("%s: get support_channel_cnt fail\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32(dramc_node,
+				   "mediatek,channel-cnt",
+				   &dramc_dev_ptr->channel_cnt);
+	if (ret) {
+		pr_info("%s: get channel_cnt fail\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32(dramc_node,
+				   "mediatek,rank-cnt",
+				   &dramc_dev_ptr->rank_cnt);
+	if (ret) {
+		pr_info("%s: get rank_cnt fail\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32(dramc_node,
+				   "mediatek,mr-cnt",
+				   &dramc_dev_ptr->mr_cnt);
+	if (ret) {
+		pr_info("%s: get mr_cnt fail\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32(dramc_node,
+				   "mediatek,freq-cnt",
+				   &dramc_dev_ptr->freq_cnt);
+	if (ret) {
+		pr_info("%s: get freq_cnt fail\n", __func__);
+		return -EINVAL;
+	}
+
+	dramc_dev_ptr->mr4_dev_ptr = (void *)dev_comp->mr4;
+
+	pr_info("%s: %s(%d),%s(%d),%s(%d),%s(%d),%s(%d),%s(%d),%s(%s)\n",
+		__func__,
+		"dram_type", dramc_dev_ptr->dram_type,
+		"support_channel_cnt", dramc_dev_ptr->support_channel_cnt,
+		"channel_cnt", dramc_dev_ptr->channel_cnt,
+		"rank_cnt", dramc_dev_ptr->rank_cnt,
+		"mr_cnt", dramc_dev_ptr->mr_cnt,
+		"freq_cnt", dramc_dev_ptr->freq_cnt,
+		"mr4", (dramc_dev_ptr->mr4_dev_ptr) ? "true" : "false");
+
+	size = sizeof(unsigned int) * dramc_dev_ptr->rank_cnt;
+	dramc_dev_ptr->rank_size = devm_kmalloc(&pdev->dev, size, GFP_KERNEL);
+	if (!(dramc_dev_ptr->rank_size))
+		return -ENOMEM;
+	ret = of_property_read_u32_array(dramc_node,
+					 "mediatek,rank-size",
+					 dramc_dev_ptr->rank_size,
+					 dramc_dev_ptr->rank_cnt);
+	if (ret) {
+		pr_info("%s: get rank_size fail\n", __func__);
+		return -EINVAL;
+	}
+
+	if (dramc_dev_ptr->mr_cnt) {
+		size = sizeof(struct mr_info_t) * dramc_dev_ptr->mr_cnt;
+		dramc_dev_ptr->mr_info_ptr = devm_kmalloc(&pdev->dev,
+							  size,
+							  GFP_KERNEL);
+		if (!(dramc_dev_ptr->mr_info_ptr))
+			return -ENOMEM;
+		ret =
+		    of_property_read_u32_array(dramc_node,
+					       "mediatek,mr",
+					       (unsigned int *)dramc_dev_ptr->mr_info_ptr,
+					       size >> 2);
+		if (ret) {
+			pr_info("%s: get mr_info fail\n", __func__);
+			return -EINVAL;
+		}
+		for (i = 0; i < dramc_dev_ptr->mr_cnt; i++)
+			pr_info("%s: mr%d(%x)\n", __func__,
+				dramc_dev_ptr->mr_info_ptr[i].mr_index,
+				dramc_dev_ptr->mr_info_ptr[i].mr_value);
+	}
+
+	if (dramc_dev_ptr->freq_cnt) {
+		size = sizeof(unsigned int) * dramc_dev_ptr->freq_cnt * 2;
+		dramc_dev_ptr->freq_step =
+			devm_kmalloc(&pdev->dev, size, GFP_KERNEL);
+		if (!(dramc_dev_ptr->freq_step))
+			return -ENOMEM;
+		ret = of_property_read_u32_array(dramc_node,
+						 "mediatek,freq-step",
+						 dramc_dev_ptr->freq_step,
+						 dramc_dev_ptr->freq_cnt * 2);
+		if (ret) {
+			pr_info("%s: get freq_step fail\n", __func__);
+			return -EINVAL;
+		}
+	}
+
+	size = sizeof(phys_addr_t) * dramc_dev_ptr->support_channel_cnt;
+	dramc_dev_ptr->dramc_chn_base_ao = devm_kmalloc(&pdev->dev,
+							size, GFP_KERNEL);
+	if (!(dramc_dev_ptr->dramc_chn_base_ao))
+		return -ENOMEM;
+	dramc_dev_ptr->dramc_chn_base_nao = devm_kmalloc(&pdev->dev,
+							 size, GFP_KERNEL);
+	if (!(dramc_dev_ptr->dramc_chn_base_nao))
+		return -ENOMEM;
+	dramc_dev_ptr->ddrphy_chn_base_ao = devm_kmalloc(&pdev->dev,
+							 size, GFP_KERNEL);
+	if (!(dramc_dev_ptr->ddrphy_chn_base_ao))
+		return -ENOMEM;
+
+	for (i = 0; i < dramc_dev_ptr->support_channel_cnt; i++) {
+		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+		dramc_dev_ptr->dramc_chn_base_ao[i] =
+			devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(dramc_dev_ptr->dramc_chn_base_ao[i])) {
+			pr_info("%s: unable to map ch%d DRAMC AO base\n",
+				__func__, i);
+			return -EINVAL;
+		}
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM,
+					    i + dramc_dev_ptr->support_channel_cnt);
+		dramc_dev_ptr->dramc_chn_base_nao[i] =
+			devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(dramc_dev_ptr->dramc_chn_base_nao[i])) {
+			pr_info("%s: unable to map ch%d DRAMC NAO base\n",
+				__func__, i);
+			return -EINVAL;
+		}
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM,
+					    i + dramc_dev_ptr->support_channel_cnt * 2);
+		dramc_dev_ptr->ddrphy_chn_base_ao[i] =
+			devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(dramc_dev_ptr->ddrphy_chn_base_ao[i])) {
+			pr_info("%s: unable to map ch%d DDRPHY AO base\n",
+				__func__, i);
+			return -EINVAL;
+		}
+	}
+
+	dramc_dev_ptr->fmeter_dev_ptr = (void *)dev_comp->fmeter;
+
+	ret = driver_create_file(pdev->dev.driver,
+				 &driver_attr_dram_data_rate);
+	if (ret) {
+		pr_info("%s: fail to create dram_data_rate sysfs\n", __func__);
+		return ret;
+	}
+
+	ret = driver_create_file(pdev->dev.driver,
+				 &driver_attr_mr);
+	if (ret) {
+		pr_info("%s: fail to create mr sysfs\n", __func__);
+		return ret;
+	}
+
+	if (dramc_dev_ptr->mr4_dev_ptr) {
+		ret = driver_create_file(pdev->dev.driver,
+					 &driver_attr_mr4);
+		if (ret) {
+			pr_info("%s: fail to create mr4 sysfs\n", __func__);
+			return ret;
+		}
+	}
+
+	platform_set_drvdata(pdev, dramc_dev_ptr);
+	pr_info("%s: DRAM data type = %d\n", __func__,
+		mtk_dramc_get_ddr_type());
+
+	pr_info("%s: DRAM data clock rate = %d\n", __func__,
+		mtk_dramc_get_data_rate());
+
+	return ret;
+}
+
+/*
+ * mtk_dramc_get_steps_freq - get the data clock rate of target DVFS step
+ * @step: the step index of DVFS
+ *
+ * Return the DRAM spec data clock rate (MHz)
+ */
+int mtk_dramc_get_steps_freq(unsigned int step)
+{
+	struct dramc_dev_t *dramc_dev_ptr;
+
+	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
+
+	if (!dramc_dev_ptr)
+		return -ENODEV;
+
+	if (step < dramc_dev_ptr->freq_cnt)
+		return dramc_dev_ptr->freq_step[step * 2 + 1];
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(mtk_dramc_get_steps_freq);
+
+/*
+ * decode_freq - decode the spec data clock rate
+ * @vco_freq: real data clock rate
+ *
+ * Return the DRAM spec data clock rate (MHz)
+ */
+static unsigned int decode_freq(unsigned int vco_freq)
+{
+	int i;
+	struct dramc_dev_t *dramc_dev_ptr;
+
+	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
+
+	if (!dramc_dev_ptr)
+		return 0;
+
+	for (i = 0; i < dramc_dev_ptr->freq_cnt * 2; i += 2)
+		if (vco_freq == dramc_dev_ptr->freq_step[i])
+			return dramc_dev_ptr->freq_step[i + 1];
+
+	return vco_freq;
+}
+
+static unsigned int fmeter_v0(struct dramc_dev_t *dramc_dev_ptr)
+{
+	struct fmeter_dev_t *fmeter_dev_ptr =
+		(struct fmeter_dev_t *)dramc_dev_ptr->fmeter_dev_ptr;
+	unsigned int shu_lv_val;
+	unsigned int pll_id_val;
+	unsigned int pll_md_val;
+	unsigned int sdmpcw_val;
+	unsigned int prediv_val;
+	unsigned int posdiv_val;
+	unsigned int ckdiv4_val;
+	unsigned int cldiv2_val;
+	unsigned int offset;
+	unsigned int vco_freq;
+
+	shu_lv_val = (readl(dramc_dev_ptr->dramc_chn_base_ao[0] +
+		fmeter_dev_ptr->shu_lv.offset) &
+		fmeter_dev_ptr->shu_lv.mask) >>
+		fmeter_dev_ptr->shu_lv.shift;
+
+	pll_id_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] +
+		fmeter_dev_ptr->pll_id.offset) &
+		fmeter_dev_ptr->pll_id.mask) >>
+		fmeter_dev_ptr->pll_id.shift;
+
+	offset = fmeter_dev_ptr->pll_md[pll_id_val].offset +
+		fmeter_dev_ptr->shu_of * shu_lv_val;
+	pll_md_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
+		fmeter_dev_ptr->pll_md[pll_id_val].mask) >>
+		fmeter_dev_ptr->pll_md[pll_id_val].shift;
+
+	offset = fmeter_dev_ptr->sdmpcw[pll_id_val].offset +
+		fmeter_dev_ptr->shu_of * shu_lv_val;
+	sdmpcw_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
+		fmeter_dev_ptr->sdmpcw[pll_id_val].mask) >>
+		fmeter_dev_ptr->sdmpcw[pll_id_val].shift;
+
+	offset = fmeter_dev_ptr->prediv[pll_id_val].offset +
+		fmeter_dev_ptr->shu_of * shu_lv_val;
+	prediv_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
+		fmeter_dev_ptr->prediv[pll_id_val].mask) >>
+		fmeter_dev_ptr->prediv[pll_id_val].shift;
+
+	offset = fmeter_dev_ptr->posdiv[pll_id_val].offset +
+		fmeter_dev_ptr->shu_of * shu_lv_val;
+	posdiv_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
+		fmeter_dev_ptr->posdiv[pll_id_val].mask) >>
+		fmeter_dev_ptr->posdiv[pll_id_val].shift;
+
+	offset = fmeter_dev_ptr->ckdiv4[pll_id_val].offset +
+		fmeter_dev_ptr->shu_of * shu_lv_val;
+	ckdiv4_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
+		fmeter_dev_ptr->ckdiv4[pll_id_val].mask) >>
+		fmeter_dev_ptr->ckdiv4[pll_id_val].shift;
+
+	offset = fmeter_dev_ptr->cldiv2[pll_id_val].offset +
+		fmeter_dev_ptr->shu_of * shu_lv_val;
+	cldiv2_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
+		fmeter_dev_ptr->cldiv2[pll_id_val].mask) >>
+		fmeter_dev_ptr->cldiv2[pll_id_val].shift;
+
+	vco_freq = ((fmeter_dev_ptr->crystal_freq >> prediv_val) *
+		(sdmpcw_val >> 8)) >>
+		posdiv_val >> ckdiv4_val >> pll_md_val >> cldiv2_val;
+
+	return decode_freq(vco_freq);
+}
+
+/*
+ * mtk_dramc_get_data_rate - calculate DRAM data rate
+ *
+ * Return DRAM data rate (MB/s)
+ */
+unsigned int mtk_dramc_get_data_rate(void)
+{
+	struct dramc_dev_t *dramc_dev_ptr;
+	struct fmeter_dev_t *fmeter_dev_ptr;
+
+	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
+
+	if (!dramc_dev_ptr)
+		return 0;
+
+	fmeter_dev_ptr = (struct fmeter_dev_t *)dramc_dev_ptr->fmeter_dev_ptr;
+	if (!fmeter_dev_ptr)
+		return 0;
+
+	return fmeter_v0(dramc_dev_ptr);
+}
+EXPORT_SYMBOL(mtk_dramc_get_data_rate);
+
+static unsigned int mr4_v1(struct dramc_dev_t *dramc_dev_ptr, unsigned int ch)
+{
+	struct mr4_dev_t *mr4_dev_ptr =
+		(struct mr4_dev_t *)dramc_dev_ptr->mr4_dev_ptr;
+
+	return (readl(dramc_dev_ptr->dramc_chn_base_nao[ch] +
+		mr4_dev_ptr->mr4_rg.offset) & mr4_dev_ptr->mr4_rg.mask) >>
+		mr4_dev_ptr->mr4_rg.shift;
+}
+
+/*
+ * mtk_dramc_get_mr4 - get the DRAM MR4 value of specific DRAM channel
+ * @ch:	the channel index
+ *
+ * Return the MR4 value
+ */
+unsigned int mtk_dramc_get_mr4(unsigned int ch)
+{
+	struct dramc_dev_t *dramc_dev_ptr;
+	struct mr4_dev_t *mr4_dev_ptr;
+
+	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
+
+	if (!dramc_dev_ptr)
+		return 0;
+
+	mr4_dev_ptr = (struct mr4_dev_t *)dramc_dev_ptr->mr4_dev_ptr;
+	if (!mr4_dev_ptr)
+		return 0;
+
+	if (ch >= dramc_dev_ptr->channel_cnt)
+		return 0;
+
+	return mr4_v1(dramc_dev_ptr, ch);
+}
+EXPORT_SYMBOL(mtk_dramc_get_mr4);
+
+/*
+ * mtk_dramc_get_ddr_type - get DRAM type
+ *
+ * Return the DRAM type
+ */
+unsigned int mtk_dramc_get_ddr_type(void)
+{
+	struct dramc_dev_t *dramc_dev_ptr;
+
+	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
+
+	if (!dramc_dev_ptr)
+		return 0;
+
+	return dramc_dev_ptr->dram_type;
+}
+EXPORT_SYMBOL(mtk_dramc_get_ddr_type);
+
+/*
+ * mtk_dramc_get_channel_count - get DRAM channel count
+ *
+ * Return the DRAM channel count
+ */
+unsigned int mtk_dramc_get_channel_count(void)
+{
+	struct dramc_dev_t *dramc_dev_ptr;
+
+	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
+
+	if (!dramc_dev_ptr)
+		return 0;
+
+	return dramc_dev_ptr->channel_cnt;
+}
+EXPORT_SYMBOL(mtk_dramc_get_channel_count);
+
+/*
+ * mtk_dramc_get_rank_count - get DRAM rank count
+ *
+ * Return the DRAM rank count
+ */
+unsigned int mtk_dramc_get_rank_count(void)
+{
+	struct dramc_dev_t *dramc_dev_ptr;
+
+	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
+
+	if (!dramc_dev_ptr)
+		return 0;
+
+	return dramc_dev_ptr->rank_cnt;
+}
+EXPORT_SYMBOL(mtk_dramc_get_rank_count);
+
+/*
+ * mtk_dramc_get_rank_size - get size of DRAM rank
+ *
+ * Return the size of specific DRAM rank
+ */
+unsigned int mtk_dramc_get_rank_size(unsigned int rank)
+{
+	struct dramc_dev_t *dramc_dev_ptr;
+
+	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
+
+	if (!dramc_dev_ptr)
+		return 0;
+
+	if (rank < dramc_dev_ptr->rank_cnt)
+		return dramc_dev_ptr->rank_size[rank];
+	else
+		return 0;
+}
+EXPORT_SYMBOL(mtk_dramc_get_rank_size);
+
+static int dramc_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver dramc_drv = {
+	.probe = dramc_probe,
+	.remove = dramc_remove,
+	.driver = {
+		.name = DRAMC_DRV_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = mtk_dramc_of_ids,
+	},
+};
+
+static int __init dramc_drv_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&dramc_drv);
+	if (ret) {
+		pr_info("%s: init fail, ret 0x%x\n", __func__, ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static void __exit dramc_drv_exit(void)
+{
+	platform_driver_unregister(&dramc_drv);
+}
+
+module_init(dramc_drv_init);
+module_exit(dramc_drv_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MediaTek DRAMC Driver");
+MODULE_AUTHOR("Po-Kai Chi <pk.chi@mediatek.com>");
diff --git a/include/memory/mediatek/dramc.h b/include/memory/mediatek/dramc.h
new file mode 100644
index 0000000..c8d200f
--- /dev/null
+++ b/include/memory/mediatek/dramc.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ */
+
+#ifndef __DRAMC_H__
+#define __DRAMC_H__
+
+int mtk_dramc_get_steps_freq(unsigned int step);
+unsigned int mtk_dramc_get_ddr_type(void);
+unsigned int mtk_dramc_get_data_rate(void);
+unsigned int mtk_dramc_get_mr4(unsigned int ch);
+unsigned int mtk_dramc_get_channel_count(void);
+unsigned int mtk_dramc_get_rank_count(void);
+unsigned int mtk_dramc_get_rank_size(unsigned int rk);
+
+#endif /* __DRAMC_H__ */
+
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 2/4] memory: mediatek: add DRAM controller driver
@ 2021-04-16  9:32   ` Po-Kai Chi
  0 siblings, 0 replies; 22+ messages in thread
From: Po-Kai Chi @ 2021-04-16  9:32 UTC (permalink / raw)
  To: Matthias Brugger
  Cc: linux-kernel, linux-mediatek, devicetree, wsd_upstream, CC Hwang,
	Po-Kai Chi

MediaTek DRAM controller (DRAMC) driver provides cross-platform features
as below:

1. provide APIs for low power feature queries
2. create sysfs to pass the DRAM information to user-space

Signed-off-by: Po-Kai Chi <pk.chi@mediatek.com>
---
 drivers/memory/Kconfig              |    1 +
 drivers/memory/Makefile             |    1 +
 drivers/memory/mediatek/Kconfig     |    9 +
 drivers/memory/mediatek/Makefile    |    3 +
 drivers/memory/mediatek/mtk-dramc.c |  711 +++++++++++++++++++++++++++++++++++
 include/memory/mediatek/dramc.h     |   18 +
 6 files changed, 743 insertions(+)
 create mode 100644 drivers/memory/mediatek/Kconfig
 create mode 100644 drivers/memory/mediatek/Makefile
 create mode 100644 drivers/memory/mediatek/mtk-dramc.c
 create mode 100644 include/memory/mediatek/dramc.h

diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index 72c0df1..056e906 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -225,6 +225,7 @@ config STM32_FMC2_EBI
 	  devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on
 	  SOCs containing the FMC2 External Bus Interface.
 
+source "drivers/memory/mediatek/Kconfig"
 source "drivers/memory/samsung/Kconfig"
 source "drivers/memory/tegra/Kconfig"
 
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index bc7663e..cd4f8cf 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_PL353_SMC)		+= pl353-smc.o
 obj-$(CONFIG_RENESAS_RPCIF)	+= renesas-rpc-if.o
 obj-$(CONFIG_STM32_FMC2_EBI)	+= stm32-fmc2-ebi.o
 
+obj-$(CONFIG_MTK_DRAMC)		+= mediatek/
 obj-$(CONFIG_SAMSUNG_MC)	+= samsung/
 obj-$(CONFIG_TEGRA_MC)		+= tegra/
 obj-$(CONFIG_TI_EMIF_SRAM)	+= ti-emif-sram.o
diff --git a/drivers/memory/mediatek/Kconfig b/drivers/memory/mediatek/Kconfig
new file mode 100644
index 0000000..a1618b0
--- /dev/null
+++ b/drivers/memory/mediatek/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config MTK_DRAMC
+	tristate "MediaTek DRAMC driver"
+	help
+	  This selects the MediaTek(R) DRAMC driver.
+	  Provide the API for DRAMC low power scenario, and the interface
+	  for reporting DRAM information, e.g. DRAM mode register (MR) for
+	  DRAM vendor ID, temperature, and density.
diff --git a/drivers/memory/mediatek/Makefile b/drivers/memory/mediatek/Makefile
new file mode 100644
index 0000000..632be48
--- /dev/null
+++ b/drivers/memory/mediatek/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_MTK_DRAMC)	+= mtk-dramc.o
diff --git a/drivers/memory/mediatek/mtk-dramc.c b/drivers/memory/mediatek/mtk-dramc.c
new file mode 100644
index 0000000..155b3b7
--- /dev/null
+++ b/drivers/memory/mediatek/mtk-dramc.c
@@ -0,0 +1,711 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/printk.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <memory/mediatek/dramc.h>
+
+#define DRAMC_DRV_NAME	"mtk-dramc"
+
+struct mr_info_t {
+	unsigned int mr_index;
+	unsigned int mr_value;
+};
+
+/*
+ * struct reg_ctrl_t - to describe the bits required in a register
+ * @offset: register address offset from a base
+ * @mask: bitmask of the target bits
+ * @shift: starting bit of the target bits
+ */
+struct reg_ctrl_t {
+	unsigned int offset;
+	unsigned int mask;
+	unsigned int shift;
+};
+
+struct fmeter_dev_t {
+	unsigned int crystal_freq;
+	unsigned int shu_of;
+	struct reg_ctrl_t shu_lv;
+	struct reg_ctrl_t pll_id;
+	struct reg_ctrl_t pll_md[2];
+	struct reg_ctrl_t sdmpcw[2];
+	struct reg_ctrl_t prediv[2];
+	struct reg_ctrl_t posdiv[2];
+	struct reg_ctrl_t ckdiv4[2];
+	struct reg_ctrl_t cldiv2[2];
+	struct reg_ctrl_t fbksel[2];
+	struct reg_ctrl_t dqopen[2];
+};
+
+struct mr4_dev_t {
+	struct reg_ctrl_t mr4_rg;
+};
+
+struct dramc_dev_t {
+	unsigned int dram_type;
+	unsigned int support_channel_cnt;
+	unsigned int channel_cnt;
+	unsigned int rank_cnt;
+	unsigned int mr_cnt;
+	unsigned int freq_cnt;
+	unsigned int *rank_size;
+	unsigned int *freq_step;
+	struct mr_info_t *mr_info_ptr;
+	void __iomem **dramc_chn_base_ao;
+	void __iomem **dramc_chn_base_nao;
+	void __iomem **ddrphy_chn_base_ao;
+	void *mr4_dev_ptr;
+	void *fmeter_dev_ptr;
+};
+
+enum DRAM_TYPE {
+	TYPE_NONE = 0,
+	TYPE_DDR1,
+	TYPE_LPDDR2,
+	TYPE_LPDDR3,
+	TYPE_PCDDR3,
+	TYPE_LPDDR4,
+	TYPE_LPDDR4X,
+	TYPE_LPDDR4P
+};
+
+static const struct fmeter_dev_t fmeter_v0_mt6779_t = {
+	.crystal_freq = 52,
+	.shu_of = 0x500,
+	.shu_lv = { .offset = 0x00e4, .mask = 0x00000006, .shift = 1 },
+	.pll_id = { .offset = 0x0510, .mask = 0x80000000, .shift = 31 },
+	.pll_md = {
+		{ .offset = 0x0d84, .mask = 0x00000100, .shift = 8 },
+		{ .offset = 0x0d84, .mask = 0x00000100, .shift = 8 },
+	},
+	.sdmpcw = {
+		{ .offset = 0x0d9c, .mask = 0xffff0000, .shift = 16 },
+		{ .offset = 0x0d94, .mask = 0xffff0000, .shift = 16 },
+	},
+	.prediv = {
+		{ .offset = 0x0da8, .mask = 0x000c0000, .shift = 18 },
+		{ .offset = 0x0da0, .mask = 0x000c0000, .shift = 18 },
+	},
+	.posdiv = {
+		{ .offset = 0x0da8, .mask = 0x00000007, .shift = 0 },
+		{ .offset = 0x0da0, .mask = 0x00000007, .shift = 0 },
+	},
+	.ckdiv4 = {
+		{ .offset = 0x0d18, .mask = 0x08000000, .shift = 27 },
+		{ .offset = 0x0d18, .mask = 0x08000000, .shift = 27 },
+	},
+	.cldiv2 = {
+		{ .offset = 0x0c38, .mask = 0x80000000, .shift = 31 },
+		{ .offset = 0x0c38, .mask = 0x80000000, .shift = 31 },
+	},
+};
+
+static const struct mr4_dev_t mr4_v1_mt6779_t = {
+	.mr4_rg = { .offset = 0x0090, .mask = 0x0000ffff, .shift = 0 },
+};
+
+struct mtk_dramc_compatible {
+	const struct fmeter_dev_t *fmeter;
+	const struct mr4_dev_t *mr4;
+};
+
+static const struct mtk_dramc_compatible mt6779_compat = {
+	.fmeter = &fmeter_v0_mt6779_t,
+	.mr4 = &mr4_v1_mt6779_t,
+};
+
+static const struct of_device_id mtk_dramc_of_ids[] = {
+	{ .compatible = "mediatek,mt6779-dramc", .data = &mt6779_compat },
+	{}
+};
+MODULE_DEVICE_TABLE(of, mtk_dramc_of_ids);
+
+/*
+ * mtk_dramc_get_drvdata_by_ids - get the dramc driver data
+ *
+ * Return the dramc driver data
+ */
+static struct dramc_dev_t *mtk_dramc_get_drvdata_by_ids(void)
+{
+	struct device_node *np;
+	struct platform_device *dramc_pdev;
+
+	np = of_find_matching_node_and_match(NULL, mtk_dramc_of_ids, NULL);
+	dramc_pdev = of_find_device_by_node(np);
+
+	if (!dramc_pdev)
+		return NULL;
+
+	return (struct dramc_dev_t *)platform_get_drvdata(dramc_pdev);
+}
+
+static ssize_t mr_show(struct device_driver *driver, char *buf)
+{
+	struct dramc_dev_t *dramc_dev_ptr;
+	struct mr_info_t *mr_info_ptr;
+	unsigned int i;
+	ssize_t ret;
+
+	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
+
+	if (!dramc_dev_ptr)
+		return 0;
+
+	mr_info_ptr = dramc_dev_ptr->mr_info_ptr;
+
+	for (ret = 0, i = 0; i < dramc_dev_ptr->mr_cnt; i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"mr%d: 0x%x\n",
+				mr_info_ptr[i].mr_index,
+				mr_info_ptr[i].mr_value);
+		if (ret >= PAGE_SIZE)
+			return strlen(buf);
+	}
+
+	return strlen(buf);
+}
+
+static ssize_t mr4_show(struct device_driver *driver, char *buf)
+{
+	struct dramc_dev_t *dramc_dev_ptr;
+	unsigned int i;
+	ssize_t ret;
+
+	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
+
+	if (!dramc_dev_ptr)
+		return 0;
+
+	for (ret = 0, i = 0; i < dramc_dev_ptr->channel_cnt; i++) {
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				"mr4: ch%d 0x%x\n",
+				i, mtk_dramc_get_mr4(i));
+		if (ret >= PAGE_SIZE)
+			return strlen(buf);
+	}
+
+	return strlen(buf);
+}
+
+static ssize_t dram_data_rate_show(struct device_driver *driver, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "DRAM data rate = %d\n",
+			mtk_dramc_get_data_rate());
+}
+
+static DRIVER_ATTR_RO(mr);
+static DRIVER_ATTR_RO(mr4);
+static DRIVER_ATTR_RO(dram_data_rate);
+
+static int dramc_probe(struct platform_device *pdev)
+{
+	struct device_node *dramc_node = pdev->dev.of_node;
+	struct dramc_dev_t *dramc_dev_ptr;
+	const struct mtk_dramc_compatible *dev_comp;
+	struct resource *res;
+	unsigned int i, size;
+	int ret;
+
+	pr_info("%s: module probe.\n", __func__);
+
+	dramc_dev_ptr = devm_kmalloc(&pdev->dev,
+				     sizeof(struct dramc_dev_t),
+				     GFP_KERNEL);
+
+	dev_comp =
+		(struct mtk_dramc_compatible *)
+			of_device_get_match_data(&pdev->dev);
+
+	if (!dramc_dev_ptr)
+		return -ENOMEM;
+
+	ret = of_property_read_u32(dramc_node,
+				   "mediatek,dram-type",
+				   &dramc_dev_ptr->dram_type);
+	if (ret) {
+		pr_info("%s: get dram_type fail\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32(dramc_node,
+				   "mediatek,support-channel-cnt",
+				   &dramc_dev_ptr->support_channel_cnt);
+	if (ret) {
+		pr_info("%s: get support_channel_cnt fail\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32(dramc_node,
+				   "mediatek,channel-cnt",
+				   &dramc_dev_ptr->channel_cnt);
+	if (ret) {
+		pr_info("%s: get channel_cnt fail\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32(dramc_node,
+				   "mediatek,rank-cnt",
+				   &dramc_dev_ptr->rank_cnt);
+	if (ret) {
+		pr_info("%s: get rank_cnt fail\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32(dramc_node,
+				   "mediatek,mr-cnt",
+				   &dramc_dev_ptr->mr_cnt);
+	if (ret) {
+		pr_info("%s: get mr_cnt fail\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32(dramc_node,
+				   "mediatek,freq-cnt",
+				   &dramc_dev_ptr->freq_cnt);
+	if (ret) {
+		pr_info("%s: get freq_cnt fail\n", __func__);
+		return -EINVAL;
+	}
+
+	dramc_dev_ptr->mr4_dev_ptr = (void *)dev_comp->mr4;
+
+	pr_info("%s: %s(%d),%s(%d),%s(%d),%s(%d),%s(%d),%s(%d),%s(%s)\n",
+		__func__,
+		"dram_type", dramc_dev_ptr->dram_type,
+		"support_channel_cnt", dramc_dev_ptr->support_channel_cnt,
+		"channel_cnt", dramc_dev_ptr->channel_cnt,
+		"rank_cnt", dramc_dev_ptr->rank_cnt,
+		"mr_cnt", dramc_dev_ptr->mr_cnt,
+		"freq_cnt", dramc_dev_ptr->freq_cnt,
+		"mr4", (dramc_dev_ptr->mr4_dev_ptr) ? "true" : "false");
+
+	size = sizeof(unsigned int) * dramc_dev_ptr->rank_cnt;
+	dramc_dev_ptr->rank_size = devm_kmalloc(&pdev->dev, size, GFP_KERNEL);
+	if (!(dramc_dev_ptr->rank_size))
+		return -ENOMEM;
+	ret = of_property_read_u32_array(dramc_node,
+					 "mediatek,rank-size",
+					 dramc_dev_ptr->rank_size,
+					 dramc_dev_ptr->rank_cnt);
+	if (ret) {
+		pr_info("%s: get rank_size fail\n", __func__);
+		return -EINVAL;
+	}
+
+	if (dramc_dev_ptr->mr_cnt) {
+		size = sizeof(struct mr_info_t) * dramc_dev_ptr->mr_cnt;
+		dramc_dev_ptr->mr_info_ptr = devm_kmalloc(&pdev->dev,
+							  size,
+							  GFP_KERNEL);
+		if (!(dramc_dev_ptr->mr_info_ptr))
+			return -ENOMEM;
+		ret =
+		    of_property_read_u32_array(dramc_node,
+					       "mediatek,mr",
+					       (unsigned int *)dramc_dev_ptr->mr_info_ptr,
+					       size >> 2);
+		if (ret) {
+			pr_info("%s: get mr_info fail\n", __func__);
+			return -EINVAL;
+		}
+		for (i = 0; i < dramc_dev_ptr->mr_cnt; i++)
+			pr_info("%s: mr%d(%x)\n", __func__,
+				dramc_dev_ptr->mr_info_ptr[i].mr_index,
+				dramc_dev_ptr->mr_info_ptr[i].mr_value);
+	}
+
+	if (dramc_dev_ptr->freq_cnt) {
+		size = sizeof(unsigned int) * dramc_dev_ptr->freq_cnt * 2;
+		dramc_dev_ptr->freq_step =
+			devm_kmalloc(&pdev->dev, size, GFP_KERNEL);
+		if (!(dramc_dev_ptr->freq_step))
+			return -ENOMEM;
+		ret = of_property_read_u32_array(dramc_node,
+						 "mediatek,freq-step",
+						 dramc_dev_ptr->freq_step,
+						 dramc_dev_ptr->freq_cnt * 2);
+		if (ret) {
+			pr_info("%s: get freq_step fail\n", __func__);
+			return -EINVAL;
+		}
+	}
+
+	size = sizeof(phys_addr_t) * dramc_dev_ptr->support_channel_cnt;
+	dramc_dev_ptr->dramc_chn_base_ao = devm_kmalloc(&pdev->dev,
+							size, GFP_KERNEL);
+	if (!(dramc_dev_ptr->dramc_chn_base_ao))
+		return -ENOMEM;
+	dramc_dev_ptr->dramc_chn_base_nao = devm_kmalloc(&pdev->dev,
+							 size, GFP_KERNEL);
+	if (!(dramc_dev_ptr->dramc_chn_base_nao))
+		return -ENOMEM;
+	dramc_dev_ptr->ddrphy_chn_base_ao = devm_kmalloc(&pdev->dev,
+							 size, GFP_KERNEL);
+	if (!(dramc_dev_ptr->ddrphy_chn_base_ao))
+		return -ENOMEM;
+
+	for (i = 0; i < dramc_dev_ptr->support_channel_cnt; i++) {
+		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+		dramc_dev_ptr->dramc_chn_base_ao[i] =
+			devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(dramc_dev_ptr->dramc_chn_base_ao[i])) {
+			pr_info("%s: unable to map ch%d DRAMC AO base\n",
+				__func__, i);
+			return -EINVAL;
+		}
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM,
+					    i + dramc_dev_ptr->support_channel_cnt);
+		dramc_dev_ptr->dramc_chn_base_nao[i] =
+			devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(dramc_dev_ptr->dramc_chn_base_nao[i])) {
+			pr_info("%s: unable to map ch%d DRAMC NAO base\n",
+				__func__, i);
+			return -EINVAL;
+		}
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM,
+					    i + dramc_dev_ptr->support_channel_cnt * 2);
+		dramc_dev_ptr->ddrphy_chn_base_ao[i] =
+			devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(dramc_dev_ptr->ddrphy_chn_base_ao[i])) {
+			pr_info("%s: unable to map ch%d DDRPHY AO base\n",
+				__func__, i);
+			return -EINVAL;
+		}
+	}
+
+	dramc_dev_ptr->fmeter_dev_ptr = (void *)dev_comp->fmeter;
+
+	ret = driver_create_file(pdev->dev.driver,
+				 &driver_attr_dram_data_rate);
+	if (ret) {
+		pr_info("%s: fail to create dram_data_rate sysfs\n", __func__);
+		return ret;
+	}
+
+	ret = driver_create_file(pdev->dev.driver,
+				 &driver_attr_mr);
+	if (ret) {
+		pr_info("%s: fail to create mr sysfs\n", __func__);
+		return ret;
+	}
+
+	if (dramc_dev_ptr->mr4_dev_ptr) {
+		ret = driver_create_file(pdev->dev.driver,
+					 &driver_attr_mr4);
+		if (ret) {
+			pr_info("%s: fail to create mr4 sysfs\n", __func__);
+			return ret;
+		}
+	}
+
+	platform_set_drvdata(pdev, dramc_dev_ptr);
+	pr_info("%s: DRAM data type = %d\n", __func__,
+		mtk_dramc_get_ddr_type());
+
+	pr_info("%s: DRAM data clock rate = %d\n", __func__,
+		mtk_dramc_get_data_rate());
+
+	return ret;
+}
+
+/*
+ * mtk_dramc_get_steps_freq - get the data clock rate of target DVFS step
+ * @step: the step index of DVFS
+ *
+ * Return the DRAM spec data clock rate (MHz)
+ */
+int mtk_dramc_get_steps_freq(unsigned int step)
+{
+	struct dramc_dev_t *dramc_dev_ptr;
+
+	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
+
+	if (!dramc_dev_ptr)
+		return -ENODEV;
+
+	if (step < dramc_dev_ptr->freq_cnt)
+		return dramc_dev_ptr->freq_step[step * 2 + 1];
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(mtk_dramc_get_steps_freq);
+
+/*
+ * decode_freq - decode the spec data clock rate
+ * @vco_freq: real data clock rate
+ *
+ * Return the DRAM spec data clock rate (MHz)
+ */
+static unsigned int decode_freq(unsigned int vco_freq)
+{
+	int i;
+	struct dramc_dev_t *dramc_dev_ptr;
+
+	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
+
+	if (!dramc_dev_ptr)
+		return 0;
+
+	for (i = 0; i < dramc_dev_ptr->freq_cnt * 2; i += 2)
+		if (vco_freq == dramc_dev_ptr->freq_step[i])
+			return dramc_dev_ptr->freq_step[i + 1];
+
+	return vco_freq;
+}
+
+static unsigned int fmeter_v0(struct dramc_dev_t *dramc_dev_ptr)
+{
+	struct fmeter_dev_t *fmeter_dev_ptr =
+		(struct fmeter_dev_t *)dramc_dev_ptr->fmeter_dev_ptr;
+	unsigned int shu_lv_val;
+	unsigned int pll_id_val;
+	unsigned int pll_md_val;
+	unsigned int sdmpcw_val;
+	unsigned int prediv_val;
+	unsigned int posdiv_val;
+	unsigned int ckdiv4_val;
+	unsigned int cldiv2_val;
+	unsigned int offset;
+	unsigned int vco_freq;
+
+	shu_lv_val = (readl(dramc_dev_ptr->dramc_chn_base_ao[0] +
+		fmeter_dev_ptr->shu_lv.offset) &
+		fmeter_dev_ptr->shu_lv.mask) >>
+		fmeter_dev_ptr->shu_lv.shift;
+
+	pll_id_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] +
+		fmeter_dev_ptr->pll_id.offset) &
+		fmeter_dev_ptr->pll_id.mask) >>
+		fmeter_dev_ptr->pll_id.shift;
+
+	offset = fmeter_dev_ptr->pll_md[pll_id_val].offset +
+		fmeter_dev_ptr->shu_of * shu_lv_val;
+	pll_md_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
+		fmeter_dev_ptr->pll_md[pll_id_val].mask) >>
+		fmeter_dev_ptr->pll_md[pll_id_val].shift;
+
+	offset = fmeter_dev_ptr->sdmpcw[pll_id_val].offset +
+		fmeter_dev_ptr->shu_of * shu_lv_val;
+	sdmpcw_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
+		fmeter_dev_ptr->sdmpcw[pll_id_val].mask) >>
+		fmeter_dev_ptr->sdmpcw[pll_id_val].shift;
+
+	offset = fmeter_dev_ptr->prediv[pll_id_val].offset +
+		fmeter_dev_ptr->shu_of * shu_lv_val;
+	prediv_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
+		fmeter_dev_ptr->prediv[pll_id_val].mask) >>
+		fmeter_dev_ptr->prediv[pll_id_val].shift;
+
+	offset = fmeter_dev_ptr->posdiv[pll_id_val].offset +
+		fmeter_dev_ptr->shu_of * shu_lv_val;
+	posdiv_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
+		fmeter_dev_ptr->posdiv[pll_id_val].mask) >>
+		fmeter_dev_ptr->posdiv[pll_id_val].shift;
+
+	offset = fmeter_dev_ptr->ckdiv4[pll_id_val].offset +
+		fmeter_dev_ptr->shu_of * shu_lv_val;
+	ckdiv4_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
+		fmeter_dev_ptr->ckdiv4[pll_id_val].mask) >>
+		fmeter_dev_ptr->ckdiv4[pll_id_val].shift;
+
+	offset = fmeter_dev_ptr->cldiv2[pll_id_val].offset +
+		fmeter_dev_ptr->shu_of * shu_lv_val;
+	cldiv2_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
+		fmeter_dev_ptr->cldiv2[pll_id_val].mask) >>
+		fmeter_dev_ptr->cldiv2[pll_id_val].shift;
+
+	vco_freq = ((fmeter_dev_ptr->crystal_freq >> prediv_val) *
+		(sdmpcw_val >> 8)) >>
+		posdiv_val >> ckdiv4_val >> pll_md_val >> cldiv2_val;
+
+	return decode_freq(vco_freq);
+}
+
+/*
+ * mtk_dramc_get_data_rate - calculate DRAM data rate
+ *
+ * Return DRAM data rate (MB/s)
+ */
+unsigned int mtk_dramc_get_data_rate(void)
+{
+	struct dramc_dev_t *dramc_dev_ptr;
+	struct fmeter_dev_t *fmeter_dev_ptr;
+
+	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
+
+	if (!dramc_dev_ptr)
+		return 0;
+
+	fmeter_dev_ptr = (struct fmeter_dev_t *)dramc_dev_ptr->fmeter_dev_ptr;
+	if (!fmeter_dev_ptr)
+		return 0;
+
+	return fmeter_v0(dramc_dev_ptr);
+}
+EXPORT_SYMBOL(mtk_dramc_get_data_rate);
+
+static unsigned int mr4_v1(struct dramc_dev_t *dramc_dev_ptr, unsigned int ch)
+{
+	struct mr4_dev_t *mr4_dev_ptr =
+		(struct mr4_dev_t *)dramc_dev_ptr->mr4_dev_ptr;
+
+	return (readl(dramc_dev_ptr->dramc_chn_base_nao[ch] +
+		mr4_dev_ptr->mr4_rg.offset) & mr4_dev_ptr->mr4_rg.mask) >>
+		mr4_dev_ptr->mr4_rg.shift;
+}
+
+/*
+ * mtk_dramc_get_mr4 - get the DRAM MR4 value of specific DRAM channel
+ * @ch:	the channel index
+ *
+ * Return the MR4 value
+ */
+unsigned int mtk_dramc_get_mr4(unsigned int ch)
+{
+	struct dramc_dev_t *dramc_dev_ptr;
+	struct mr4_dev_t *mr4_dev_ptr;
+
+	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
+
+	if (!dramc_dev_ptr)
+		return 0;
+
+	mr4_dev_ptr = (struct mr4_dev_t *)dramc_dev_ptr->mr4_dev_ptr;
+	if (!mr4_dev_ptr)
+		return 0;
+
+	if (ch >= dramc_dev_ptr->channel_cnt)
+		return 0;
+
+	return mr4_v1(dramc_dev_ptr, ch);
+}
+EXPORT_SYMBOL(mtk_dramc_get_mr4);
+
+/*
+ * mtk_dramc_get_ddr_type - get DRAM type
+ *
+ * Return the DRAM type
+ */
+unsigned int mtk_dramc_get_ddr_type(void)
+{
+	struct dramc_dev_t *dramc_dev_ptr;
+
+	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
+
+	if (!dramc_dev_ptr)
+		return 0;
+
+	return dramc_dev_ptr->dram_type;
+}
+EXPORT_SYMBOL(mtk_dramc_get_ddr_type);
+
+/*
+ * mtk_dramc_get_channel_count - get DRAM channel count
+ *
+ * Return the DRAM channel count
+ */
+unsigned int mtk_dramc_get_channel_count(void)
+{
+	struct dramc_dev_t *dramc_dev_ptr;
+
+	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
+
+	if (!dramc_dev_ptr)
+		return 0;
+
+	return dramc_dev_ptr->channel_cnt;
+}
+EXPORT_SYMBOL(mtk_dramc_get_channel_count);
+
+/*
+ * mtk_dramc_get_rank_count - get DRAM rank count
+ *
+ * Return the DRAM rank count
+ */
+unsigned int mtk_dramc_get_rank_count(void)
+{
+	struct dramc_dev_t *dramc_dev_ptr;
+
+	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
+
+	if (!dramc_dev_ptr)
+		return 0;
+
+	return dramc_dev_ptr->rank_cnt;
+}
+EXPORT_SYMBOL(mtk_dramc_get_rank_count);
+
+/*
+ * mtk_dramc_get_rank_size - get size of DRAM rank
+ *
+ * Return the size of specific DRAM rank
+ */
+unsigned int mtk_dramc_get_rank_size(unsigned int rank)
+{
+	struct dramc_dev_t *dramc_dev_ptr;
+
+	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
+
+	if (!dramc_dev_ptr)
+		return 0;
+
+	if (rank < dramc_dev_ptr->rank_cnt)
+		return dramc_dev_ptr->rank_size[rank];
+	else
+		return 0;
+}
+EXPORT_SYMBOL(mtk_dramc_get_rank_size);
+
+static int dramc_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver dramc_drv = {
+	.probe = dramc_probe,
+	.remove = dramc_remove,
+	.driver = {
+		.name = DRAMC_DRV_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = mtk_dramc_of_ids,
+	},
+};
+
+static int __init dramc_drv_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&dramc_drv);
+	if (ret) {
+		pr_info("%s: init fail, ret 0x%x\n", __func__, ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static void __exit dramc_drv_exit(void)
+{
+	platform_driver_unregister(&dramc_drv);
+}
+
+module_init(dramc_drv_init);
+module_exit(dramc_drv_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MediaTek DRAMC Driver");
+MODULE_AUTHOR("Po-Kai Chi <pk.chi@mediatek.com>");
diff --git a/include/memory/mediatek/dramc.h b/include/memory/mediatek/dramc.h
new file mode 100644
index 0000000..c8d200f
--- /dev/null
+++ b/include/memory/mediatek/dramc.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ */
+
+#ifndef __DRAMC_H__
+#define __DRAMC_H__
+
+int mtk_dramc_get_steps_freq(unsigned int step);
+unsigned int mtk_dramc_get_ddr_type(void);
+unsigned int mtk_dramc_get_data_rate(void);
+unsigned int mtk_dramc_get_mr4(unsigned int ch);
+unsigned int mtk_dramc_get_channel_count(void);
+unsigned int mtk_dramc_get_rank_count(void);
+unsigned int mtk_dramc_get_rank_size(unsigned int rk);
+
+#endif /* __DRAMC_H__ */
+
-- 
1.7.9.5
_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 3/4] arm64: dts: add DRAMC node for MT6779
  2021-04-16  9:32 ` Po-Kai Chi
@ 2021-04-16  9:32   ` Po-Kai Chi
  -1 siblings, 0 replies; 22+ messages in thread
From: Po-Kai Chi @ 2021-04-16  9:32 UTC (permalink / raw)
  To: Matthias Brugger
  Cc: linux-kernel, linux-mediatek, devicetree, wsd_upstream, CC Hwang,
	Po-Kai Chi

Add the DRAMC node for the DRAMC kernel driver.

Properties are divided into three categories:

- Platform DTS:
    MediaTek DRAMC platform common part.

- Project DTS:
    Runtime filled in by bootloader according to the board
    hardware configuration.

- Driver level:
    Hardware-specific register settings, encapsulated as
    compatible data for better DTS compatibility.

Signed-off-by: Po-Kai Chi <pk.chi@mediatek.com>
---
 arch/arm64/boot/dts/mediatek/mt6779-evb.dts |    9 +++++++++
 arch/arm64/boot/dts/mediatek/mt6779.dtsi    |   18 ++++++++++++++++++
 2 files changed, 27 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt6779-evb.dts b/arch/arm64/boot/dts/mediatek/mt6779-evb.dts
index 164f5cb..5d2dde7 100644
--- a/arch/arm64/boot/dts/mediatek/mt6779-evb.dts
+++ b/arch/arm64/boot/dts/mediatek/mt6779-evb.dts
@@ -29,3 +29,12 @@
 &uart0 {
 	status = "okay";
 };
+
+&dramc {
+	mediatek,dram-type = <0>;
+	mediatek,channel-cnt = <2>;
+	mediatek,rank-cnt = <2>;
+	mediatek,rank-size = <0x0 0x0>;
+	mediatek,mr-cnt = <1>;
+	mediatek,mr = <0x5 0xff>;
+};
diff --git a/arch/arm64/boot/dts/mediatek/mt6779.dtsi b/arch/arm64/boot/dts/mediatek/mt6779.dtsi
index 9bdf514..28492a0 100644
--- a/arch/arm64/boot/dts/mediatek/mt6779.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt6779.dtsi
@@ -206,6 +206,24 @@
 			clock-names = "devapc-infra-clock";
 		};
 
+		dramc: dramc@10230000 {
+			compatible = "mediatek,mt6779-dramc";
+			reg = <0 0x10230000 0 0x2000>, /* DRAMC AO CHA */
+				<0 0x10240000 0 0x2000>, /* DRAMC AO CHB */
+				<0 0x10234000 0 0x1000>, /* DRAMC NAO CHA */
+				<0 0x10244000 0 0x1000>, /* DRAMC NAO CHB */
+				<0 0x10238000 0 0x2000>, /* DDRPHY AO CHA */
+				<0 0x10248000 0 0x2000>; /* DDRPHY AO CHB */
+			mediatek,support-channel-cnt = <2>;
+			mediatek,freq-cnt = <6>;
+			mediatek,freq-step = <3718 3733>,
+				<3094 3200>,
+				<2392 2400>,
+				<1534 1600>,
+				<1196 1200>,
+				<754 800>;
+		};
+
 		uart0: serial@11002000 {
 			compatible = "mediatek,mt6779-uart",
 				     "mediatek,mt6577-uart";
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 3/4] arm64: dts: add DRAMC node for MT6779
@ 2021-04-16  9:32   ` Po-Kai Chi
  0 siblings, 0 replies; 22+ messages in thread
From: Po-Kai Chi @ 2021-04-16  9:32 UTC (permalink / raw)
  To: Matthias Brugger
  Cc: linux-kernel, linux-mediatek, devicetree, wsd_upstream, CC Hwang,
	Po-Kai Chi

Add the DRAMC node for the DRAMC kernel driver.

Properties are divided into three categories:

- Platform DTS:
    MediaTek DRAMC platform common part.

- Project DTS:
    Runtime filled in by bootloader according to the board
    hardware configuration.

- Driver level:
    Hardware-specific register settings, encapsulated as
    compatible data for better DTS compatibility.

Signed-off-by: Po-Kai Chi <pk.chi@mediatek.com>
---
 arch/arm64/boot/dts/mediatek/mt6779-evb.dts |    9 +++++++++
 arch/arm64/boot/dts/mediatek/mt6779.dtsi    |   18 ++++++++++++++++++
 2 files changed, 27 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt6779-evb.dts b/arch/arm64/boot/dts/mediatek/mt6779-evb.dts
index 164f5cb..5d2dde7 100644
--- a/arch/arm64/boot/dts/mediatek/mt6779-evb.dts
+++ b/arch/arm64/boot/dts/mediatek/mt6779-evb.dts
@@ -29,3 +29,12 @@
 &uart0 {
 	status = "okay";
 };
+
+&dramc {
+	mediatek,dram-type = <0>;
+	mediatek,channel-cnt = <2>;
+	mediatek,rank-cnt = <2>;
+	mediatek,rank-size = <0x0 0x0>;
+	mediatek,mr-cnt = <1>;
+	mediatek,mr = <0x5 0xff>;
+};
diff --git a/arch/arm64/boot/dts/mediatek/mt6779.dtsi b/arch/arm64/boot/dts/mediatek/mt6779.dtsi
index 9bdf514..28492a0 100644
--- a/arch/arm64/boot/dts/mediatek/mt6779.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt6779.dtsi
@@ -206,6 +206,24 @@
 			clock-names = "devapc-infra-clock";
 		};
 
+		dramc: dramc@10230000 {
+			compatible = "mediatek,mt6779-dramc";
+			reg = <0 0x10230000 0 0x2000>, /* DRAMC AO CHA */
+				<0 0x10240000 0 0x2000>, /* DRAMC AO CHB */
+				<0 0x10234000 0 0x1000>, /* DRAMC NAO CHA */
+				<0 0x10244000 0 0x1000>, /* DRAMC NAO CHB */
+				<0 0x10238000 0 0x2000>, /* DDRPHY AO CHA */
+				<0 0x10248000 0 0x2000>; /* DDRPHY AO CHB */
+			mediatek,support-channel-cnt = <2>;
+			mediatek,freq-cnt = <6>;
+			mediatek,freq-step = <3718 3733>,
+				<3094 3200>,
+				<2392 2400>,
+				<1534 1600>,
+				<1196 1200>,
+				<754 800>;
+		};
+
 		uart0: serial@11002000 {
 			compatible = "mediatek,mt6779-uart",
 				     "mediatek,mt6577-uart";
-- 
1.7.9.5
_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 4/4] arm64: defconfig: Enable MediaTek DRAMC common driver
  2021-04-16  9:32 ` Po-Kai Chi
@ 2021-04-16  9:32   ` Po-Kai Chi
  -1 siblings, 0 replies; 22+ messages in thread
From: Po-Kai Chi @ 2021-04-16  9:32 UTC (permalink / raw)
  To: Matthias Brugger
  Cc: linux-kernel, linux-mediatek, devicetree, wsd_upstream, CC Hwang,
	Po-Kai Chi

This commit enables MediaTek DRAMC common driver to be built
as a module by default for the ARM64 builds.

Signed-off-by: Po-Kai Chi <pk.chi@mediatek.com>
---
 arch/arm64/configs/defconfig |    1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index d612f63..49d7464 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -1163,3 +1163,4 @@ CONFIG_DEBUG_KERNEL=y
 # CONFIG_DEBUG_PREEMPT is not set
 # CONFIG_FTRACE is not set
 CONFIG_MEMTEST=y
+CONFIG_MTK_DRAMC=m
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 4/4] arm64: defconfig: Enable MediaTek DRAMC common driver
@ 2021-04-16  9:32   ` Po-Kai Chi
  0 siblings, 0 replies; 22+ messages in thread
From: Po-Kai Chi @ 2021-04-16  9:32 UTC (permalink / raw)
  To: Matthias Brugger
  Cc: linux-kernel, linux-mediatek, devicetree, wsd_upstream, CC Hwang,
	Po-Kai Chi

This commit enables MediaTek DRAMC common driver to be built
as a module by default for the ARM64 builds.

Signed-off-by: Po-Kai Chi <pk.chi@mediatek.com>
---
 arch/arm64/configs/defconfig |    1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index d612f63..49d7464 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -1163,3 +1163,4 @@ CONFIG_DEBUG_KERNEL=y
 # CONFIG_DEBUG_PREEMPT is not set
 # CONFIG_FTRACE is not set
 CONFIG_MEMTEST=y
+CONFIG_MTK_DRAMC=m
-- 
1.7.9.5
_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* Re: [PATCH v2 2/4] memory: mediatek: add DRAM controller driver
  2021-04-16  9:32   ` Po-Kai Chi
@ 2021-04-20 18:28     ` Rob Herring
  -1 siblings, 0 replies; 22+ messages in thread
From: Rob Herring @ 2021-04-20 18:28 UTC (permalink / raw)
  To: Po-Kai Chi
  Cc: Matthias Brugger, linux-kernel, linux-mediatek, devicetree,
	wsd_upstream, CC Hwang

On Fri, Apr 16, 2021 at 05:32:16PM +0800, Po-Kai Chi wrote:
> MediaTek DRAM controller (DRAMC) driver provides cross-platform features
> as below:
> 
> 1. provide APIs for low power feature queries
> 2. create sysfs to pass the DRAM information to user-space

I'm hesistant with having both DT and sysfs vendor specific memory 
properties. I think we need something common here.

> 
> Signed-off-by: Po-Kai Chi <pk.chi@mediatek.com>
> ---
>  drivers/memory/Kconfig              |    1 +
>  drivers/memory/Makefile             |    1 +
>  drivers/memory/mediatek/Kconfig     |    9 +
>  drivers/memory/mediatek/Makefile    |    3 +
>  drivers/memory/mediatek/mtk-dramc.c |  711 +++++++++++++++++++++++++++++++++++
>  include/memory/mediatek/dramc.h     |   18 +
>  6 files changed, 743 insertions(+)
>  create mode 100644 drivers/memory/mediatek/Kconfig
>  create mode 100644 drivers/memory/mediatek/Makefile
>  create mode 100644 drivers/memory/mediatek/mtk-dramc.c
>  create mode 100644 include/memory/mediatek/dramc.h
> 
> diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
> index 72c0df1..056e906 100644
> --- a/drivers/memory/Kconfig
> +++ b/drivers/memory/Kconfig
> @@ -225,6 +225,7 @@ config STM32_FMC2_EBI
>  	  devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on
>  	  SOCs containing the FMC2 External Bus Interface.
>  
> +source "drivers/memory/mediatek/Kconfig"
>  source "drivers/memory/samsung/Kconfig"
>  source "drivers/memory/tegra/Kconfig"
>  
> diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
> index bc7663e..cd4f8cf 100644
> --- a/drivers/memory/Makefile
> +++ b/drivers/memory/Makefile
> @@ -25,6 +25,7 @@ obj-$(CONFIG_PL353_SMC)		+= pl353-smc.o
>  obj-$(CONFIG_RENESAS_RPCIF)	+= renesas-rpc-if.o
>  obj-$(CONFIG_STM32_FMC2_EBI)	+= stm32-fmc2-ebi.o
>  
> +obj-$(CONFIG_MTK_DRAMC)		+= mediatek/
>  obj-$(CONFIG_SAMSUNG_MC)	+= samsung/
>  obj-$(CONFIG_TEGRA_MC)		+= tegra/
>  obj-$(CONFIG_TI_EMIF_SRAM)	+= ti-emif-sram.o
> diff --git a/drivers/memory/mediatek/Kconfig b/drivers/memory/mediatek/Kconfig
> new file mode 100644
> index 0000000..a1618b0
> --- /dev/null
> +++ b/drivers/memory/mediatek/Kconfig
> @@ -0,0 +1,9 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +config MTK_DRAMC
> +	tristate "MediaTek DRAMC driver"
> +	help
> +	  This selects the MediaTek(R) DRAMC driver.
> +	  Provide the API for DRAMC low power scenario, and the interface
> +	  for reporting DRAM information, e.g. DRAM mode register (MR) for
> +	  DRAM vendor ID, temperature, and density.
> diff --git a/drivers/memory/mediatek/Makefile b/drivers/memory/mediatek/Makefile
> new file mode 100644
> index 0000000..632be48
> --- /dev/null
> +++ b/drivers/memory/mediatek/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +obj-$(CONFIG_MTK_DRAMC)	+= mtk-dramc.o
> diff --git a/drivers/memory/mediatek/mtk-dramc.c b/drivers/memory/mediatek/mtk-dramc.c
> new file mode 100644
> index 0000000..155b3b7
> --- /dev/null
> +++ b/drivers/memory/mediatek/mtk-dramc.c
> @@ -0,0 +1,711 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021 MediaTek Inc.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/printk.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <memory/mediatek/dramc.h>
> +
> +#define DRAMC_DRV_NAME	"mtk-dramc"
> +
> +struct mr_info_t {
> +	unsigned int mr_index;
> +	unsigned int mr_value;
> +};
> +
> +/*
> + * struct reg_ctrl_t - to describe the bits required in a register
> + * @offset: register address offset from a base
> + * @mask: bitmask of the target bits
> + * @shift: starting bit of the target bits
> + */
> +struct reg_ctrl_t {
> +	unsigned int offset;
> +	unsigned int mask;
> +	unsigned int shift;
> +};
> +
> +struct fmeter_dev_t {
> +	unsigned int crystal_freq;
> +	unsigned int shu_of;
> +	struct reg_ctrl_t shu_lv;
> +	struct reg_ctrl_t pll_id;
> +	struct reg_ctrl_t pll_md[2];
> +	struct reg_ctrl_t sdmpcw[2];
> +	struct reg_ctrl_t prediv[2];
> +	struct reg_ctrl_t posdiv[2];
> +	struct reg_ctrl_t ckdiv4[2];
> +	struct reg_ctrl_t cldiv2[2];
> +	struct reg_ctrl_t fbksel[2];
> +	struct reg_ctrl_t dqopen[2];
> +};
> +
> +struct mr4_dev_t {
> +	struct reg_ctrl_t mr4_rg;
> +};
> +
> +struct dramc_dev_t {
> +	unsigned int dram_type;
> +	unsigned int support_channel_cnt;
> +	unsigned int channel_cnt;
> +	unsigned int rank_cnt;
> +	unsigned int mr_cnt;
> +	unsigned int freq_cnt;
> +	unsigned int *rank_size;
> +	unsigned int *freq_step;
> +	struct mr_info_t *mr_info_ptr;
> +	void __iomem **dramc_chn_base_ao;
> +	void __iomem **dramc_chn_base_nao;
> +	void __iomem **ddrphy_chn_base_ao;
> +	void *mr4_dev_ptr;
> +	void *fmeter_dev_ptr;
> +};
> +
> +enum DRAM_TYPE {
> +	TYPE_NONE = 0,
> +	TYPE_DDR1,
> +	TYPE_LPDDR2,
> +	TYPE_LPDDR3,
> +	TYPE_PCDDR3,
> +	TYPE_LPDDR4,
> +	TYPE_LPDDR4X,
> +	TYPE_LPDDR4P
> +};

This doesn't appear to be used.

> +
> +static const struct fmeter_dev_t fmeter_v0_mt6779_t = {
> +	.crystal_freq = 52,
> +	.shu_of = 0x500,
> +	.shu_lv = { .offset = 0x00e4, .mask = 0x00000006, .shift = 1 },
> +	.pll_id = { .offset = 0x0510, .mask = 0x80000000, .shift = 31 },
> +	.pll_md = {
> +		{ .offset = 0x0d84, .mask = 0x00000100, .shift = 8 },
> +		{ .offset = 0x0d84, .mask = 0x00000100, .shift = 8 },
> +	},
> +	.sdmpcw = {
> +		{ .offset = 0x0d9c, .mask = 0xffff0000, .shift = 16 },
> +		{ .offset = 0x0d94, .mask = 0xffff0000, .shift = 16 },
> +	},
> +	.prediv = {
> +		{ .offset = 0x0da8, .mask = 0x000c0000, .shift = 18 },
> +		{ .offset = 0x0da0, .mask = 0x000c0000, .shift = 18 },
> +	},
> +	.posdiv = {
> +		{ .offset = 0x0da8, .mask = 0x00000007, .shift = 0 },
> +		{ .offset = 0x0da0, .mask = 0x00000007, .shift = 0 },
> +	},
> +	.ckdiv4 = {
> +		{ .offset = 0x0d18, .mask = 0x08000000, .shift = 27 },
> +		{ .offset = 0x0d18, .mask = 0x08000000, .shift = 27 },
> +	},
> +	.cldiv2 = {
> +		{ .offset = 0x0c38, .mask = 0x80000000, .shift = 31 },
> +		{ .offset = 0x0c38, .mask = 0x80000000, .shift = 31 },
> +	},
> +};
> +
> +static const struct mr4_dev_t mr4_v1_mt6779_t = {
> +	.mr4_rg = { .offset = 0x0090, .mask = 0x0000ffff, .shift = 0 },
> +};
> +
> +struct mtk_dramc_compatible {
> +	const struct fmeter_dev_t *fmeter;
> +	const struct mr4_dev_t *mr4;
> +};
> +
> +static const struct mtk_dramc_compatible mt6779_compat = {
> +	.fmeter = &fmeter_v0_mt6779_t,
> +	.mr4 = &mr4_v1_mt6779_t,
> +};
> +
> +static const struct of_device_id mtk_dramc_of_ids[] = {
> +	{ .compatible = "mediatek,mt6779-dramc", .data = &mt6779_compat },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, mtk_dramc_of_ids);
> +
> +/*
> + * mtk_dramc_get_drvdata_by_ids - get the dramc driver data
> + *
> + * Return the dramc driver data
> + */
> +static struct dramc_dev_t *mtk_dramc_get_drvdata_by_ids(void)
> +{
> +	struct device_node *np;
> +	struct platform_device *dramc_pdev;
> +
> +	np = of_find_matching_node_and_match(NULL, mtk_dramc_of_ids, NULL);
> +	dramc_pdev = of_find_device_by_node(np);
> +
> +	if (!dramc_pdev)
> +		return NULL;
> +
> +	return (struct dramc_dev_t *)platform_get_drvdata(dramc_pdev);
> +}
> +
> +static ssize_t mr_show(struct device_driver *driver, char *buf)
> +{
> +	struct dramc_dev_t *dramc_dev_ptr;
> +	struct mr_info_t *mr_info_ptr;
> +	unsigned int i;
> +	ssize_t ret;
> +
> +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> +
> +	if (!dramc_dev_ptr)
> +		return 0;
> +
> +	mr_info_ptr = dramc_dev_ptr->mr_info_ptr;
> +
> +	for (ret = 0, i = 0; i < dramc_dev_ptr->mr_cnt; i++) {
> +		ret += snprintf(buf + ret, PAGE_SIZE - ret,
> +				"mr%d: 0x%x\n",

The file name is 'mr' and what you read should be just the value.

Also, sysfs files require documentation too.

> +				mr_info_ptr[i].mr_index,
> +				mr_info_ptr[i].mr_value);
> +		if (ret >= PAGE_SIZE)
> +			return strlen(buf);
> +	}
> +
> +	return strlen(buf);
> +}
> +
> +static ssize_t mr4_show(struct device_driver *driver, char *buf)
> +{
> +	struct dramc_dev_t *dramc_dev_ptr;
> +	unsigned int i;
> +	ssize_t ret;
> +
> +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> +
> +	if (!dramc_dev_ptr)
> +		return 0;
> +
> +	for (ret = 0, i = 0; i < dramc_dev_ptr->channel_cnt; i++) {
> +		ret += snprintf(buf + ret, PAGE_SIZE - ret,
> +				"mr4: ch%d 0x%x\n",

Same issues here.

> +				i, mtk_dramc_get_mr4(i));
> +		if (ret >= PAGE_SIZE)
> +			return strlen(buf);
> +	}
> +
> +	return strlen(buf);
> +}
> +
> +static ssize_t dram_data_rate_show(struct device_driver *driver, char *buf)
> +{
> +	return snprintf(buf, PAGE_SIZE, "DRAM data rate = %d\n",
> +			mtk_dramc_get_data_rate());

And here.

> +}
> +
> +static DRIVER_ATTR_RO(mr);
> +static DRIVER_ATTR_RO(mr4);
> +static DRIVER_ATTR_RO(dram_data_rate);

A driver attr would be global. Shouldn't these be device attr's which 
are per instance.

You should also be using attribute groups.

> +
> +static int dramc_probe(struct platform_device *pdev)
> +{
> +	struct device_node *dramc_node = pdev->dev.of_node;
> +	struct dramc_dev_t *dramc_dev_ptr;
> +	const struct mtk_dramc_compatible *dev_comp;
> +	struct resource *res;
> +	unsigned int i, size;
> +	int ret;
> +
> +	pr_info("%s: module probe.\n", __func__);
> +
> +	dramc_dev_ptr = devm_kmalloc(&pdev->dev,

Probably want to use devm_kzalloc instead.

> +				     sizeof(struct dramc_dev_t),
> +				     GFP_KERNEL);
> +
> +	dev_comp =
> +		(struct mtk_dramc_compatible *)
> +			of_device_get_match_data(&pdev->dev);
> +
> +	if (!dramc_dev_ptr)
> +		return -ENOMEM;
> +
> +	ret = of_property_read_u32(dramc_node,
> +				   "mediatek,dram-type",
> +				   &dramc_dev_ptr->dram_type);
> +	if (ret) {
> +		pr_info("%s: get dram_type fail\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	ret = of_property_read_u32(dramc_node,
> +				   "mediatek,support-channel-cnt",
> +				   &dramc_dev_ptr->support_channel_cnt);
> +	if (ret) {
> +		pr_info("%s: get support_channel_cnt fail\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	ret = of_property_read_u32(dramc_node,
> +				   "mediatek,channel-cnt",
> +				   &dramc_dev_ptr->channel_cnt);
> +	if (ret) {
> +		pr_info("%s: get channel_cnt fail\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	ret = of_property_read_u32(dramc_node,
> +				   "mediatek,rank-cnt",
> +				   &dramc_dev_ptr->rank_cnt);
> +	if (ret) {
> +		pr_info("%s: get rank_cnt fail\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	ret = of_property_read_u32(dramc_node,
> +				   "mediatek,mr-cnt",
> +				   &dramc_dev_ptr->mr_cnt);
> +	if (ret) {
> +		pr_info("%s: get mr_cnt fail\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	ret = of_property_read_u32(dramc_node,
> +				   "mediatek,freq-cnt",
> +				   &dramc_dev_ptr->freq_cnt);
> +	if (ret) {
> +		pr_info("%s: get freq_cnt fail\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	dramc_dev_ptr->mr4_dev_ptr = (void *)dev_comp->mr4;
> +
> +	pr_info("%s: %s(%d),%s(%d),%s(%d),%s(%d),%s(%d),%s(%d),%s(%s)\n",
> +		__func__,
> +		"dram_type", dramc_dev_ptr->dram_type,
> +		"support_channel_cnt", dramc_dev_ptr->support_channel_cnt,
> +		"channel_cnt", dramc_dev_ptr->channel_cnt,
> +		"rank_cnt", dramc_dev_ptr->rank_cnt,
> +		"mr_cnt", dramc_dev_ptr->mr_cnt,
> +		"freq_cnt", dramc_dev_ptr->freq_cnt,
> +		"mr4", (dramc_dev_ptr->mr4_dev_ptr) ? "true" : "false");

Why do you need this? It's already in DT which is readable from 
userspace.

> +
> +	size = sizeof(unsigned int) * dramc_dev_ptr->rank_cnt;
> +	dramc_dev_ptr->rank_size = devm_kmalloc(&pdev->dev, size, GFP_KERNEL);
> +	if (!(dramc_dev_ptr->rank_size))
> +		return -ENOMEM;
> +	ret = of_property_read_u32_array(dramc_node,
> +					 "mediatek,rank-size",
> +					 dramc_dev_ptr->rank_size,
> +					 dramc_dev_ptr->rank_cnt);
> +	if (ret) {
> +		pr_info("%s: get rank_size fail\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	if (dramc_dev_ptr->mr_cnt) {
> +		size = sizeof(struct mr_info_t) * dramc_dev_ptr->mr_cnt;
> +		dramc_dev_ptr->mr_info_ptr = devm_kmalloc(&pdev->dev,
> +							  size,
> +							  GFP_KERNEL);
> +		if (!(dramc_dev_ptr->mr_info_ptr))
> +			return -ENOMEM;
> +		ret =
> +		    of_property_read_u32_array(dramc_node,
> +					       "mediatek,mr",
> +					       (unsigned int *)dramc_dev_ptr->mr_info_ptr,
> +					       size >> 2);
> +		if (ret) {
> +			pr_info("%s: get mr_info fail\n", __func__);
> +			return -EINVAL;
> +		}
> +		for (i = 0; i < dramc_dev_ptr->mr_cnt; i++)
> +			pr_info("%s: mr%d(%x)\n", __func__,
> +				dramc_dev_ptr->mr_info_ptr[i].mr_index,
> +				dramc_dev_ptr->mr_info_ptr[i].mr_value);
> +	}
> +
> +	if (dramc_dev_ptr->freq_cnt) {
> +		size = sizeof(unsigned int) * dramc_dev_ptr->freq_cnt * 2;
> +		dramc_dev_ptr->freq_step =
> +			devm_kmalloc(&pdev->dev, size, GFP_KERNEL);
> +		if (!(dramc_dev_ptr->freq_step))
> +			return -ENOMEM;
> +		ret = of_property_read_u32_array(dramc_node,
> +						 "mediatek,freq-step",
> +						 dramc_dev_ptr->freq_step,
> +						 dramc_dev_ptr->freq_cnt * 2);
> +		if (ret) {
> +			pr_info("%s: get freq_step fail\n", __func__);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	size = sizeof(phys_addr_t) * dramc_dev_ptr->support_channel_cnt;
> +	dramc_dev_ptr->dramc_chn_base_ao = devm_kmalloc(&pdev->dev,
> +							size, GFP_KERNEL);
> +	if (!(dramc_dev_ptr->dramc_chn_base_ao))
> +		return -ENOMEM;
> +	dramc_dev_ptr->dramc_chn_base_nao = devm_kmalloc(&pdev->dev,
> +							 size, GFP_KERNEL);
> +	if (!(dramc_dev_ptr->dramc_chn_base_nao))
> +		return -ENOMEM;
> +	dramc_dev_ptr->ddrphy_chn_base_ao = devm_kmalloc(&pdev->dev,
> +							 size, GFP_KERNEL);
> +	if (!(dramc_dev_ptr->ddrphy_chn_base_ao))
> +		return -ENOMEM;
> +
> +	for (i = 0; i < dramc_dev_ptr->support_channel_cnt; i++) {
> +		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
> +		dramc_dev_ptr->dramc_chn_base_ao[i] =
> +			devm_ioremap_resource(&pdev->dev, res);
> +		if (IS_ERR(dramc_dev_ptr->dramc_chn_base_ao[i])) {
> +			pr_info("%s: unable to map ch%d DRAMC AO base\n",
> +				__func__, i);
> +			return -EINVAL;
> +		}
> +
> +		res = platform_get_resource(pdev, IORESOURCE_MEM,
> +					    i + dramc_dev_ptr->support_channel_cnt);
> +		dramc_dev_ptr->dramc_chn_base_nao[i] =
> +			devm_ioremap_resource(&pdev->dev, res);
> +		if (IS_ERR(dramc_dev_ptr->dramc_chn_base_nao[i])) {
> +			pr_info("%s: unable to map ch%d DRAMC NAO base\n",
> +				__func__, i);
> +			return -EINVAL;
> +		}
> +
> +		res = platform_get_resource(pdev, IORESOURCE_MEM,
> +					    i + dramc_dev_ptr->support_channel_cnt * 2);
> +		dramc_dev_ptr->ddrphy_chn_base_ao[i] =
> +			devm_ioremap_resource(&pdev->dev, res);
> +		if (IS_ERR(dramc_dev_ptr->ddrphy_chn_base_ao[i])) {
> +			pr_info("%s: unable to map ch%d DDRPHY AO base\n",
> +				__func__, i);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	dramc_dev_ptr->fmeter_dev_ptr = (void *)dev_comp->fmeter;
> +
> +	ret = driver_create_file(pdev->dev.driver,
> +				 &driver_attr_dram_data_rate);
> +	if (ret) {
> +		pr_info("%s: fail to create dram_data_rate sysfs\n", __func__);
> +		return ret;
> +	}
> +
> +	ret = driver_create_file(pdev->dev.driver,
> +				 &driver_attr_mr);
> +	if (ret) {
> +		pr_info("%s: fail to create mr sysfs\n", __func__);
> +		return ret;
> +	}
> +
> +	if (dramc_dev_ptr->mr4_dev_ptr) {
> +		ret = driver_create_file(pdev->dev.driver,
> +					 &driver_attr_mr4);
> +		if (ret) {
> +			pr_info("%s: fail to create mr4 sysfs\n", __func__);
> +			return ret;
> +		}
> +	}
> +
> +	platform_set_drvdata(pdev, dramc_dev_ptr);
> +	pr_info("%s: DRAM data type = %d\n", __func__,
> +		mtk_dramc_get_ddr_type());
> +
> +	pr_info("%s: DRAM data clock rate = %d\n", __func__,
> +		mtk_dramc_get_data_rate());
> +
> +	return ret;
> +}
> +
> +/*
> + * mtk_dramc_get_steps_freq - get the data clock rate of target DVFS step
> + * @step: the step index of DVFS
> + *
> + * Return the DRAM spec data clock rate (MHz)
> + */
> +int mtk_dramc_get_steps_freq(unsigned int step)
> +{
> +	struct dramc_dev_t *dramc_dev_ptr;
> +
> +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> +
> +	if (!dramc_dev_ptr)
> +		return -ENODEV;
> +
> +	if (step < dramc_dev_ptr->freq_cnt)
> +		return dramc_dev_ptr->freq_step[step * 2 + 1];
> +
> +	return -EINVAL;
> +}
> +EXPORT_SYMBOL(mtk_dramc_get_steps_freq);
> +
> +/*
> + * decode_freq - decode the spec data clock rate
> + * @vco_freq: real data clock rate
> + *
> + * Return the DRAM spec data clock rate (MHz)
> + */
> +static unsigned int decode_freq(unsigned int vco_freq)
> +{
> +	int i;
> +	struct dramc_dev_t *dramc_dev_ptr;
> +
> +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> +
> +	if (!dramc_dev_ptr)
> +		return 0;
> +
> +	for (i = 0; i < dramc_dev_ptr->freq_cnt * 2; i += 2)
> +		if (vco_freq == dramc_dev_ptr->freq_step[i])
> +			return dramc_dev_ptr->freq_step[i + 1];
> +
> +	return vco_freq;
> +}
> +
> +static unsigned int fmeter_v0(struct dramc_dev_t *dramc_dev_ptr)
> +{
> +	struct fmeter_dev_t *fmeter_dev_ptr =
> +		(struct fmeter_dev_t *)dramc_dev_ptr->fmeter_dev_ptr;
> +	unsigned int shu_lv_val;
> +	unsigned int pll_id_val;
> +	unsigned int pll_md_val;
> +	unsigned int sdmpcw_val;
> +	unsigned int prediv_val;
> +	unsigned int posdiv_val;
> +	unsigned int ckdiv4_val;
> +	unsigned int cldiv2_val;
> +	unsigned int offset;
> +	unsigned int vco_freq;
> +
> +	shu_lv_val = (readl(dramc_dev_ptr->dramc_chn_base_ao[0] +
> +		fmeter_dev_ptr->shu_lv.offset) &
> +		fmeter_dev_ptr->shu_lv.mask) >>
> +		fmeter_dev_ptr->shu_lv.shift;
> +
> +	pll_id_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] +
> +		fmeter_dev_ptr->pll_id.offset) &
> +		fmeter_dev_ptr->pll_id.mask) >>
> +		fmeter_dev_ptr->pll_id.shift;
> +
> +	offset = fmeter_dev_ptr->pll_md[pll_id_val].offset +
> +		fmeter_dev_ptr->shu_of * shu_lv_val;
> +	pll_md_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> +		fmeter_dev_ptr->pll_md[pll_id_val].mask) >>
> +		fmeter_dev_ptr->pll_md[pll_id_val].shift;
> +
> +	offset = fmeter_dev_ptr->sdmpcw[pll_id_val].offset +
> +		fmeter_dev_ptr->shu_of * shu_lv_val;
> +	sdmpcw_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> +		fmeter_dev_ptr->sdmpcw[pll_id_val].mask) >>
> +		fmeter_dev_ptr->sdmpcw[pll_id_val].shift;
> +
> +	offset = fmeter_dev_ptr->prediv[pll_id_val].offset +
> +		fmeter_dev_ptr->shu_of * shu_lv_val;
> +	prediv_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> +		fmeter_dev_ptr->prediv[pll_id_val].mask) >>
> +		fmeter_dev_ptr->prediv[pll_id_val].shift;
> +
> +	offset = fmeter_dev_ptr->posdiv[pll_id_val].offset +
> +		fmeter_dev_ptr->shu_of * shu_lv_val;
> +	posdiv_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> +		fmeter_dev_ptr->posdiv[pll_id_val].mask) >>
> +		fmeter_dev_ptr->posdiv[pll_id_val].shift;
> +
> +	offset = fmeter_dev_ptr->ckdiv4[pll_id_val].offset +
> +		fmeter_dev_ptr->shu_of * shu_lv_val;
> +	ckdiv4_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> +		fmeter_dev_ptr->ckdiv4[pll_id_val].mask) >>
> +		fmeter_dev_ptr->ckdiv4[pll_id_val].shift;
> +
> +	offset = fmeter_dev_ptr->cldiv2[pll_id_val].offset +
> +		fmeter_dev_ptr->shu_of * shu_lv_val;
> +	cldiv2_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> +		fmeter_dev_ptr->cldiv2[pll_id_val].mask) >>
> +		fmeter_dev_ptr->cldiv2[pll_id_val].shift;
> +
> +	vco_freq = ((fmeter_dev_ptr->crystal_freq >> prediv_val) *
> +		(sdmpcw_val >> 8)) >>
> +		posdiv_val >> ckdiv4_val >> pll_md_val >> cldiv2_val;
> +
> +	return decode_freq(vco_freq);
> +}
> +
> +/*
> + * mtk_dramc_get_data_rate - calculate DRAM data rate
> + *
> + * Return DRAM data rate (MB/s)
> + */
> +unsigned int mtk_dramc_get_data_rate(void)
> +{
> +	struct dramc_dev_t *dramc_dev_ptr;
> +	struct fmeter_dev_t *fmeter_dev_ptr;
> +
> +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> +
> +	if (!dramc_dev_ptr)
> +		return 0;
> +
> +	fmeter_dev_ptr = (struct fmeter_dev_t *)dramc_dev_ptr->fmeter_dev_ptr;
> +	if (!fmeter_dev_ptr)
> +		return 0;
> +
> +	return fmeter_v0(dramc_dev_ptr);
> +}
> +EXPORT_SYMBOL(mtk_dramc_get_data_rate);
> +
> +static unsigned int mr4_v1(struct dramc_dev_t *dramc_dev_ptr, unsigned int ch)
> +{
> +	struct mr4_dev_t *mr4_dev_ptr =
> +		(struct mr4_dev_t *)dramc_dev_ptr->mr4_dev_ptr;
> +
> +	return (readl(dramc_dev_ptr->dramc_chn_base_nao[ch] +
> +		mr4_dev_ptr->mr4_rg.offset) & mr4_dev_ptr->mr4_rg.mask) >>
> +		mr4_dev_ptr->mr4_rg.shift;
> +}
> +
> +/*
> + * mtk_dramc_get_mr4 - get the DRAM MR4 value of specific DRAM channel
> + * @ch:	the channel index
> + *
> + * Return the MR4 value
> + */
> +unsigned int mtk_dramc_get_mr4(unsigned int ch)
> +{
> +	struct dramc_dev_t *dramc_dev_ptr;
> +	struct mr4_dev_t *mr4_dev_ptr;
> +
> +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> +
> +	if (!dramc_dev_ptr)
> +		return 0;
> +
> +	mr4_dev_ptr = (struct mr4_dev_t *)dramc_dev_ptr->mr4_dev_ptr;
> +	if (!mr4_dev_ptr)
> +		return 0;
> +
> +	if (ch >= dramc_dev_ptr->channel_cnt)
> +		return 0;
> +
> +	return mr4_v1(dramc_dev_ptr, ch);
> +}
> +EXPORT_SYMBOL(mtk_dramc_get_mr4);
> +
> +/*
> + * mtk_dramc_get_ddr_type - get DRAM type
> + *
> + * Return the DRAM type
> + */
> +unsigned int mtk_dramc_get_ddr_type(void)
> +{
> +	struct dramc_dev_t *dramc_dev_ptr;
> +
> +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> +
> +	if (!dramc_dev_ptr)
> +		return 0;
> +
> +	return dramc_dev_ptr->dram_type;
> +}
> +EXPORT_SYMBOL(mtk_dramc_get_ddr_type);
> +
> +/*
> + * mtk_dramc_get_channel_count - get DRAM channel count
> + *
> + * Return the DRAM channel count
> + */
> +unsigned int mtk_dramc_get_channel_count(void)
> +{
> +	struct dramc_dev_t *dramc_dev_ptr;
> +
> +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> +
> +	if (!dramc_dev_ptr)
> +		return 0;
> +
> +	return dramc_dev_ptr->channel_cnt;
> +}
> +EXPORT_SYMBOL(mtk_dramc_get_channel_count);
> +
> +/*
> + * mtk_dramc_get_rank_count - get DRAM rank count
> + *
> + * Return the DRAM rank count
> + */
> +unsigned int mtk_dramc_get_rank_count(void)
> +{
> +	struct dramc_dev_t *dramc_dev_ptr;
> +
> +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> +
> +	if (!dramc_dev_ptr)
> +		return 0;
> +
> +	return dramc_dev_ptr->rank_cnt;
> +}
> +EXPORT_SYMBOL(mtk_dramc_get_rank_count);
> +
> +/*
> + * mtk_dramc_get_rank_size - get size of DRAM rank
> + *
> + * Return the size of specific DRAM rank
> + */
> +unsigned int mtk_dramc_get_rank_size(unsigned int rank)
> +{
> +	struct dramc_dev_t *dramc_dev_ptr;
> +
> +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> +
> +	if (!dramc_dev_ptr)
> +		return 0;
> +
> +	if (rank < dramc_dev_ptr->rank_cnt)
> +		return dramc_dev_ptr->rank_size[rank];
> +	else
> +		return 0;
> +}
> +EXPORT_SYMBOL(mtk_dramc_get_rank_size);
> +
> +static int dramc_remove(struct platform_device *pdev)
> +{
> +	return 0;
> +}
> +
> +static struct platform_driver dramc_drv = {
> +	.probe = dramc_probe,
> +	.remove = dramc_remove,
> +	.driver = {
> +		.name = DRAMC_DRV_NAME,
> +		.owner = THIS_MODULE,
> +		.of_match_table = mtk_dramc_of_ids,
> +	},
> +};
> +
> +static int __init dramc_drv_init(void)
> +{
> +	int ret;
> +
> +	ret = platform_driver_register(&dramc_drv);
> +	if (ret) {
> +		pr_info("%s: init fail, ret 0x%x\n", __func__, ret);
> +		return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static void __exit dramc_drv_exit(void)
> +{
> +	platform_driver_unregister(&dramc_drv);
> +}
> +
> +module_init(dramc_drv_init);
> +module_exit(dramc_drv_exit);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("MediaTek DRAMC Driver");
> +MODULE_AUTHOR("Po-Kai Chi <pk.chi@mediatek.com>");
> diff --git a/include/memory/mediatek/dramc.h b/include/memory/mediatek/dramc.h
> new file mode 100644
> index 0000000..c8d200f
> --- /dev/null
> +++ b/include/memory/mediatek/dramc.h
> @@ -0,0 +1,18 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2021 MediaTek Inc.
> + */
> +
> +#ifndef __DRAMC_H__
> +#define __DRAMC_H__
> +
> +int mtk_dramc_get_steps_freq(unsigned int step);
> +unsigned int mtk_dramc_get_ddr_type(void);
> +unsigned int mtk_dramc_get_data_rate(void);
> +unsigned int mtk_dramc_get_mr4(unsigned int ch);
> +unsigned int mtk_dramc_get_channel_count(void);
> +unsigned int mtk_dramc_get_rank_count(void);
> +unsigned int mtk_dramc_get_rank_size(unsigned int rk);
> +
> +#endif /* __DRAMC_H__ */
> +
> -- 
> 1.7.9.5
> 

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v2 2/4] memory: mediatek: add DRAM controller driver
@ 2021-04-20 18:28     ` Rob Herring
  0 siblings, 0 replies; 22+ messages in thread
From: Rob Herring @ 2021-04-20 18:28 UTC (permalink / raw)
  To: Po-Kai Chi
  Cc: Matthias Brugger, linux-kernel, linux-mediatek, devicetree,
	wsd_upstream, CC Hwang

On Fri, Apr 16, 2021 at 05:32:16PM +0800, Po-Kai Chi wrote:
> MediaTek DRAM controller (DRAMC) driver provides cross-platform features
> as below:
> 
> 1. provide APIs for low power feature queries
> 2. create sysfs to pass the DRAM information to user-space

I'm hesistant with having both DT and sysfs vendor specific memory 
properties. I think we need something common here.

> 
> Signed-off-by: Po-Kai Chi <pk.chi@mediatek.com>
> ---
>  drivers/memory/Kconfig              |    1 +
>  drivers/memory/Makefile             |    1 +
>  drivers/memory/mediatek/Kconfig     |    9 +
>  drivers/memory/mediatek/Makefile    |    3 +
>  drivers/memory/mediatek/mtk-dramc.c |  711 +++++++++++++++++++++++++++++++++++
>  include/memory/mediatek/dramc.h     |   18 +
>  6 files changed, 743 insertions(+)
>  create mode 100644 drivers/memory/mediatek/Kconfig
>  create mode 100644 drivers/memory/mediatek/Makefile
>  create mode 100644 drivers/memory/mediatek/mtk-dramc.c
>  create mode 100644 include/memory/mediatek/dramc.h
> 
> diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
> index 72c0df1..056e906 100644
> --- a/drivers/memory/Kconfig
> +++ b/drivers/memory/Kconfig
> @@ -225,6 +225,7 @@ config STM32_FMC2_EBI
>  	  devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on
>  	  SOCs containing the FMC2 External Bus Interface.
>  
> +source "drivers/memory/mediatek/Kconfig"
>  source "drivers/memory/samsung/Kconfig"
>  source "drivers/memory/tegra/Kconfig"
>  
> diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
> index bc7663e..cd4f8cf 100644
> --- a/drivers/memory/Makefile
> +++ b/drivers/memory/Makefile
> @@ -25,6 +25,7 @@ obj-$(CONFIG_PL353_SMC)		+= pl353-smc.o
>  obj-$(CONFIG_RENESAS_RPCIF)	+= renesas-rpc-if.o
>  obj-$(CONFIG_STM32_FMC2_EBI)	+= stm32-fmc2-ebi.o
>  
> +obj-$(CONFIG_MTK_DRAMC)		+= mediatek/
>  obj-$(CONFIG_SAMSUNG_MC)	+= samsung/
>  obj-$(CONFIG_TEGRA_MC)		+= tegra/
>  obj-$(CONFIG_TI_EMIF_SRAM)	+= ti-emif-sram.o
> diff --git a/drivers/memory/mediatek/Kconfig b/drivers/memory/mediatek/Kconfig
> new file mode 100644
> index 0000000..a1618b0
> --- /dev/null
> +++ b/drivers/memory/mediatek/Kconfig
> @@ -0,0 +1,9 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +config MTK_DRAMC
> +	tristate "MediaTek DRAMC driver"
> +	help
> +	  This selects the MediaTek(R) DRAMC driver.
> +	  Provide the API for DRAMC low power scenario, and the interface
> +	  for reporting DRAM information, e.g. DRAM mode register (MR) for
> +	  DRAM vendor ID, temperature, and density.
> diff --git a/drivers/memory/mediatek/Makefile b/drivers/memory/mediatek/Makefile
> new file mode 100644
> index 0000000..632be48
> --- /dev/null
> +++ b/drivers/memory/mediatek/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +obj-$(CONFIG_MTK_DRAMC)	+= mtk-dramc.o
> diff --git a/drivers/memory/mediatek/mtk-dramc.c b/drivers/memory/mediatek/mtk-dramc.c
> new file mode 100644
> index 0000000..155b3b7
> --- /dev/null
> +++ b/drivers/memory/mediatek/mtk-dramc.c
> @@ -0,0 +1,711 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021 MediaTek Inc.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/printk.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <memory/mediatek/dramc.h>
> +
> +#define DRAMC_DRV_NAME	"mtk-dramc"
> +
> +struct mr_info_t {
> +	unsigned int mr_index;
> +	unsigned int mr_value;
> +};
> +
> +/*
> + * struct reg_ctrl_t - to describe the bits required in a register
> + * @offset: register address offset from a base
> + * @mask: bitmask of the target bits
> + * @shift: starting bit of the target bits
> + */
> +struct reg_ctrl_t {
> +	unsigned int offset;
> +	unsigned int mask;
> +	unsigned int shift;
> +};
> +
> +struct fmeter_dev_t {
> +	unsigned int crystal_freq;
> +	unsigned int shu_of;
> +	struct reg_ctrl_t shu_lv;
> +	struct reg_ctrl_t pll_id;
> +	struct reg_ctrl_t pll_md[2];
> +	struct reg_ctrl_t sdmpcw[2];
> +	struct reg_ctrl_t prediv[2];
> +	struct reg_ctrl_t posdiv[2];
> +	struct reg_ctrl_t ckdiv4[2];
> +	struct reg_ctrl_t cldiv2[2];
> +	struct reg_ctrl_t fbksel[2];
> +	struct reg_ctrl_t dqopen[2];
> +};
> +
> +struct mr4_dev_t {
> +	struct reg_ctrl_t mr4_rg;
> +};
> +
> +struct dramc_dev_t {
> +	unsigned int dram_type;
> +	unsigned int support_channel_cnt;
> +	unsigned int channel_cnt;
> +	unsigned int rank_cnt;
> +	unsigned int mr_cnt;
> +	unsigned int freq_cnt;
> +	unsigned int *rank_size;
> +	unsigned int *freq_step;
> +	struct mr_info_t *mr_info_ptr;
> +	void __iomem **dramc_chn_base_ao;
> +	void __iomem **dramc_chn_base_nao;
> +	void __iomem **ddrphy_chn_base_ao;
> +	void *mr4_dev_ptr;
> +	void *fmeter_dev_ptr;
> +};
> +
> +enum DRAM_TYPE {
> +	TYPE_NONE = 0,
> +	TYPE_DDR1,
> +	TYPE_LPDDR2,
> +	TYPE_LPDDR3,
> +	TYPE_PCDDR3,
> +	TYPE_LPDDR4,
> +	TYPE_LPDDR4X,
> +	TYPE_LPDDR4P
> +};

This doesn't appear to be used.

> +
> +static const struct fmeter_dev_t fmeter_v0_mt6779_t = {
> +	.crystal_freq = 52,
> +	.shu_of = 0x500,
> +	.shu_lv = { .offset = 0x00e4, .mask = 0x00000006, .shift = 1 },
> +	.pll_id = { .offset = 0x0510, .mask = 0x80000000, .shift = 31 },
> +	.pll_md = {
> +		{ .offset = 0x0d84, .mask = 0x00000100, .shift = 8 },
> +		{ .offset = 0x0d84, .mask = 0x00000100, .shift = 8 },
> +	},
> +	.sdmpcw = {
> +		{ .offset = 0x0d9c, .mask = 0xffff0000, .shift = 16 },
> +		{ .offset = 0x0d94, .mask = 0xffff0000, .shift = 16 },
> +	},
> +	.prediv = {
> +		{ .offset = 0x0da8, .mask = 0x000c0000, .shift = 18 },
> +		{ .offset = 0x0da0, .mask = 0x000c0000, .shift = 18 },
> +	},
> +	.posdiv = {
> +		{ .offset = 0x0da8, .mask = 0x00000007, .shift = 0 },
> +		{ .offset = 0x0da0, .mask = 0x00000007, .shift = 0 },
> +	},
> +	.ckdiv4 = {
> +		{ .offset = 0x0d18, .mask = 0x08000000, .shift = 27 },
> +		{ .offset = 0x0d18, .mask = 0x08000000, .shift = 27 },
> +	},
> +	.cldiv2 = {
> +		{ .offset = 0x0c38, .mask = 0x80000000, .shift = 31 },
> +		{ .offset = 0x0c38, .mask = 0x80000000, .shift = 31 },
> +	},
> +};
> +
> +static const struct mr4_dev_t mr4_v1_mt6779_t = {
> +	.mr4_rg = { .offset = 0x0090, .mask = 0x0000ffff, .shift = 0 },
> +};
> +
> +struct mtk_dramc_compatible {
> +	const struct fmeter_dev_t *fmeter;
> +	const struct mr4_dev_t *mr4;
> +};
> +
> +static const struct mtk_dramc_compatible mt6779_compat = {
> +	.fmeter = &fmeter_v0_mt6779_t,
> +	.mr4 = &mr4_v1_mt6779_t,
> +};
> +
> +static const struct of_device_id mtk_dramc_of_ids[] = {
> +	{ .compatible = "mediatek,mt6779-dramc", .data = &mt6779_compat },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, mtk_dramc_of_ids);
> +
> +/*
> + * mtk_dramc_get_drvdata_by_ids - get the dramc driver data
> + *
> + * Return the dramc driver data
> + */
> +static struct dramc_dev_t *mtk_dramc_get_drvdata_by_ids(void)
> +{
> +	struct device_node *np;
> +	struct platform_device *dramc_pdev;
> +
> +	np = of_find_matching_node_and_match(NULL, mtk_dramc_of_ids, NULL);
> +	dramc_pdev = of_find_device_by_node(np);
> +
> +	if (!dramc_pdev)
> +		return NULL;
> +
> +	return (struct dramc_dev_t *)platform_get_drvdata(dramc_pdev);
> +}
> +
> +static ssize_t mr_show(struct device_driver *driver, char *buf)
> +{
> +	struct dramc_dev_t *dramc_dev_ptr;
> +	struct mr_info_t *mr_info_ptr;
> +	unsigned int i;
> +	ssize_t ret;
> +
> +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> +
> +	if (!dramc_dev_ptr)
> +		return 0;
> +
> +	mr_info_ptr = dramc_dev_ptr->mr_info_ptr;
> +
> +	for (ret = 0, i = 0; i < dramc_dev_ptr->mr_cnt; i++) {
> +		ret += snprintf(buf + ret, PAGE_SIZE - ret,
> +				"mr%d: 0x%x\n",

The file name is 'mr' and what you read should be just the value.

Also, sysfs files require documentation too.

> +				mr_info_ptr[i].mr_index,
> +				mr_info_ptr[i].mr_value);
> +		if (ret >= PAGE_SIZE)
> +			return strlen(buf);
> +	}
> +
> +	return strlen(buf);
> +}
> +
> +static ssize_t mr4_show(struct device_driver *driver, char *buf)
> +{
> +	struct dramc_dev_t *dramc_dev_ptr;
> +	unsigned int i;
> +	ssize_t ret;
> +
> +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> +
> +	if (!dramc_dev_ptr)
> +		return 0;
> +
> +	for (ret = 0, i = 0; i < dramc_dev_ptr->channel_cnt; i++) {
> +		ret += snprintf(buf + ret, PAGE_SIZE - ret,
> +				"mr4: ch%d 0x%x\n",

Same issues here.

> +				i, mtk_dramc_get_mr4(i));
> +		if (ret >= PAGE_SIZE)
> +			return strlen(buf);
> +	}
> +
> +	return strlen(buf);
> +}
> +
> +static ssize_t dram_data_rate_show(struct device_driver *driver, char *buf)
> +{
> +	return snprintf(buf, PAGE_SIZE, "DRAM data rate = %d\n",
> +			mtk_dramc_get_data_rate());

And here.

> +}
> +
> +static DRIVER_ATTR_RO(mr);
> +static DRIVER_ATTR_RO(mr4);
> +static DRIVER_ATTR_RO(dram_data_rate);

A driver attr would be global. Shouldn't these be device attr's which 
are per instance.

You should also be using attribute groups.

> +
> +static int dramc_probe(struct platform_device *pdev)
> +{
> +	struct device_node *dramc_node = pdev->dev.of_node;
> +	struct dramc_dev_t *dramc_dev_ptr;
> +	const struct mtk_dramc_compatible *dev_comp;
> +	struct resource *res;
> +	unsigned int i, size;
> +	int ret;
> +
> +	pr_info("%s: module probe.\n", __func__);
> +
> +	dramc_dev_ptr = devm_kmalloc(&pdev->dev,

Probably want to use devm_kzalloc instead.

> +				     sizeof(struct dramc_dev_t),
> +				     GFP_KERNEL);
> +
> +	dev_comp =
> +		(struct mtk_dramc_compatible *)
> +			of_device_get_match_data(&pdev->dev);
> +
> +	if (!dramc_dev_ptr)
> +		return -ENOMEM;
> +
> +	ret = of_property_read_u32(dramc_node,
> +				   "mediatek,dram-type",
> +				   &dramc_dev_ptr->dram_type);
> +	if (ret) {
> +		pr_info("%s: get dram_type fail\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	ret = of_property_read_u32(dramc_node,
> +				   "mediatek,support-channel-cnt",
> +				   &dramc_dev_ptr->support_channel_cnt);
> +	if (ret) {
> +		pr_info("%s: get support_channel_cnt fail\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	ret = of_property_read_u32(dramc_node,
> +				   "mediatek,channel-cnt",
> +				   &dramc_dev_ptr->channel_cnt);
> +	if (ret) {
> +		pr_info("%s: get channel_cnt fail\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	ret = of_property_read_u32(dramc_node,
> +				   "mediatek,rank-cnt",
> +				   &dramc_dev_ptr->rank_cnt);
> +	if (ret) {
> +		pr_info("%s: get rank_cnt fail\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	ret = of_property_read_u32(dramc_node,
> +				   "mediatek,mr-cnt",
> +				   &dramc_dev_ptr->mr_cnt);
> +	if (ret) {
> +		pr_info("%s: get mr_cnt fail\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	ret = of_property_read_u32(dramc_node,
> +				   "mediatek,freq-cnt",
> +				   &dramc_dev_ptr->freq_cnt);
> +	if (ret) {
> +		pr_info("%s: get freq_cnt fail\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	dramc_dev_ptr->mr4_dev_ptr = (void *)dev_comp->mr4;
> +
> +	pr_info("%s: %s(%d),%s(%d),%s(%d),%s(%d),%s(%d),%s(%d),%s(%s)\n",
> +		__func__,
> +		"dram_type", dramc_dev_ptr->dram_type,
> +		"support_channel_cnt", dramc_dev_ptr->support_channel_cnt,
> +		"channel_cnt", dramc_dev_ptr->channel_cnt,
> +		"rank_cnt", dramc_dev_ptr->rank_cnt,
> +		"mr_cnt", dramc_dev_ptr->mr_cnt,
> +		"freq_cnt", dramc_dev_ptr->freq_cnt,
> +		"mr4", (dramc_dev_ptr->mr4_dev_ptr) ? "true" : "false");

Why do you need this? It's already in DT which is readable from 
userspace.

> +
> +	size = sizeof(unsigned int) * dramc_dev_ptr->rank_cnt;
> +	dramc_dev_ptr->rank_size = devm_kmalloc(&pdev->dev, size, GFP_KERNEL);
> +	if (!(dramc_dev_ptr->rank_size))
> +		return -ENOMEM;
> +	ret = of_property_read_u32_array(dramc_node,
> +					 "mediatek,rank-size",
> +					 dramc_dev_ptr->rank_size,
> +					 dramc_dev_ptr->rank_cnt);
> +	if (ret) {
> +		pr_info("%s: get rank_size fail\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	if (dramc_dev_ptr->mr_cnt) {
> +		size = sizeof(struct mr_info_t) * dramc_dev_ptr->mr_cnt;
> +		dramc_dev_ptr->mr_info_ptr = devm_kmalloc(&pdev->dev,
> +							  size,
> +							  GFP_KERNEL);
> +		if (!(dramc_dev_ptr->mr_info_ptr))
> +			return -ENOMEM;
> +		ret =
> +		    of_property_read_u32_array(dramc_node,
> +					       "mediatek,mr",
> +					       (unsigned int *)dramc_dev_ptr->mr_info_ptr,
> +					       size >> 2);
> +		if (ret) {
> +			pr_info("%s: get mr_info fail\n", __func__);
> +			return -EINVAL;
> +		}
> +		for (i = 0; i < dramc_dev_ptr->mr_cnt; i++)
> +			pr_info("%s: mr%d(%x)\n", __func__,
> +				dramc_dev_ptr->mr_info_ptr[i].mr_index,
> +				dramc_dev_ptr->mr_info_ptr[i].mr_value);
> +	}
> +
> +	if (dramc_dev_ptr->freq_cnt) {
> +		size = sizeof(unsigned int) * dramc_dev_ptr->freq_cnt * 2;
> +		dramc_dev_ptr->freq_step =
> +			devm_kmalloc(&pdev->dev, size, GFP_KERNEL);
> +		if (!(dramc_dev_ptr->freq_step))
> +			return -ENOMEM;
> +		ret = of_property_read_u32_array(dramc_node,
> +						 "mediatek,freq-step",
> +						 dramc_dev_ptr->freq_step,
> +						 dramc_dev_ptr->freq_cnt * 2);
> +		if (ret) {
> +			pr_info("%s: get freq_step fail\n", __func__);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	size = sizeof(phys_addr_t) * dramc_dev_ptr->support_channel_cnt;
> +	dramc_dev_ptr->dramc_chn_base_ao = devm_kmalloc(&pdev->dev,
> +							size, GFP_KERNEL);
> +	if (!(dramc_dev_ptr->dramc_chn_base_ao))
> +		return -ENOMEM;
> +	dramc_dev_ptr->dramc_chn_base_nao = devm_kmalloc(&pdev->dev,
> +							 size, GFP_KERNEL);
> +	if (!(dramc_dev_ptr->dramc_chn_base_nao))
> +		return -ENOMEM;
> +	dramc_dev_ptr->ddrphy_chn_base_ao = devm_kmalloc(&pdev->dev,
> +							 size, GFP_KERNEL);
> +	if (!(dramc_dev_ptr->ddrphy_chn_base_ao))
> +		return -ENOMEM;
> +
> +	for (i = 0; i < dramc_dev_ptr->support_channel_cnt; i++) {
> +		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
> +		dramc_dev_ptr->dramc_chn_base_ao[i] =
> +			devm_ioremap_resource(&pdev->dev, res);
> +		if (IS_ERR(dramc_dev_ptr->dramc_chn_base_ao[i])) {
> +			pr_info("%s: unable to map ch%d DRAMC AO base\n",
> +				__func__, i);
> +			return -EINVAL;
> +		}
> +
> +		res = platform_get_resource(pdev, IORESOURCE_MEM,
> +					    i + dramc_dev_ptr->support_channel_cnt);
> +		dramc_dev_ptr->dramc_chn_base_nao[i] =
> +			devm_ioremap_resource(&pdev->dev, res);
> +		if (IS_ERR(dramc_dev_ptr->dramc_chn_base_nao[i])) {
> +			pr_info("%s: unable to map ch%d DRAMC NAO base\n",
> +				__func__, i);
> +			return -EINVAL;
> +		}
> +
> +		res = platform_get_resource(pdev, IORESOURCE_MEM,
> +					    i + dramc_dev_ptr->support_channel_cnt * 2);
> +		dramc_dev_ptr->ddrphy_chn_base_ao[i] =
> +			devm_ioremap_resource(&pdev->dev, res);
> +		if (IS_ERR(dramc_dev_ptr->ddrphy_chn_base_ao[i])) {
> +			pr_info("%s: unable to map ch%d DDRPHY AO base\n",
> +				__func__, i);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	dramc_dev_ptr->fmeter_dev_ptr = (void *)dev_comp->fmeter;
> +
> +	ret = driver_create_file(pdev->dev.driver,
> +				 &driver_attr_dram_data_rate);
> +	if (ret) {
> +		pr_info("%s: fail to create dram_data_rate sysfs\n", __func__);
> +		return ret;
> +	}
> +
> +	ret = driver_create_file(pdev->dev.driver,
> +				 &driver_attr_mr);
> +	if (ret) {
> +		pr_info("%s: fail to create mr sysfs\n", __func__);
> +		return ret;
> +	}
> +
> +	if (dramc_dev_ptr->mr4_dev_ptr) {
> +		ret = driver_create_file(pdev->dev.driver,
> +					 &driver_attr_mr4);
> +		if (ret) {
> +			pr_info("%s: fail to create mr4 sysfs\n", __func__);
> +			return ret;
> +		}
> +	}
> +
> +	platform_set_drvdata(pdev, dramc_dev_ptr);
> +	pr_info("%s: DRAM data type = %d\n", __func__,
> +		mtk_dramc_get_ddr_type());
> +
> +	pr_info("%s: DRAM data clock rate = %d\n", __func__,
> +		mtk_dramc_get_data_rate());
> +
> +	return ret;
> +}
> +
> +/*
> + * mtk_dramc_get_steps_freq - get the data clock rate of target DVFS step
> + * @step: the step index of DVFS
> + *
> + * Return the DRAM spec data clock rate (MHz)
> + */
> +int mtk_dramc_get_steps_freq(unsigned int step)
> +{
> +	struct dramc_dev_t *dramc_dev_ptr;
> +
> +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> +
> +	if (!dramc_dev_ptr)
> +		return -ENODEV;
> +
> +	if (step < dramc_dev_ptr->freq_cnt)
> +		return dramc_dev_ptr->freq_step[step * 2 + 1];
> +
> +	return -EINVAL;
> +}
> +EXPORT_SYMBOL(mtk_dramc_get_steps_freq);
> +
> +/*
> + * decode_freq - decode the spec data clock rate
> + * @vco_freq: real data clock rate
> + *
> + * Return the DRAM spec data clock rate (MHz)
> + */
> +static unsigned int decode_freq(unsigned int vco_freq)
> +{
> +	int i;
> +	struct dramc_dev_t *dramc_dev_ptr;
> +
> +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> +
> +	if (!dramc_dev_ptr)
> +		return 0;
> +
> +	for (i = 0; i < dramc_dev_ptr->freq_cnt * 2; i += 2)
> +		if (vco_freq == dramc_dev_ptr->freq_step[i])
> +			return dramc_dev_ptr->freq_step[i + 1];
> +
> +	return vco_freq;
> +}
> +
> +static unsigned int fmeter_v0(struct dramc_dev_t *dramc_dev_ptr)
> +{
> +	struct fmeter_dev_t *fmeter_dev_ptr =
> +		(struct fmeter_dev_t *)dramc_dev_ptr->fmeter_dev_ptr;
> +	unsigned int shu_lv_val;
> +	unsigned int pll_id_val;
> +	unsigned int pll_md_val;
> +	unsigned int sdmpcw_val;
> +	unsigned int prediv_val;
> +	unsigned int posdiv_val;
> +	unsigned int ckdiv4_val;
> +	unsigned int cldiv2_val;
> +	unsigned int offset;
> +	unsigned int vco_freq;
> +
> +	shu_lv_val = (readl(dramc_dev_ptr->dramc_chn_base_ao[0] +
> +		fmeter_dev_ptr->shu_lv.offset) &
> +		fmeter_dev_ptr->shu_lv.mask) >>
> +		fmeter_dev_ptr->shu_lv.shift;
> +
> +	pll_id_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] +
> +		fmeter_dev_ptr->pll_id.offset) &
> +		fmeter_dev_ptr->pll_id.mask) >>
> +		fmeter_dev_ptr->pll_id.shift;
> +
> +	offset = fmeter_dev_ptr->pll_md[pll_id_val].offset +
> +		fmeter_dev_ptr->shu_of * shu_lv_val;
> +	pll_md_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> +		fmeter_dev_ptr->pll_md[pll_id_val].mask) >>
> +		fmeter_dev_ptr->pll_md[pll_id_val].shift;
> +
> +	offset = fmeter_dev_ptr->sdmpcw[pll_id_val].offset +
> +		fmeter_dev_ptr->shu_of * shu_lv_val;
> +	sdmpcw_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> +		fmeter_dev_ptr->sdmpcw[pll_id_val].mask) >>
> +		fmeter_dev_ptr->sdmpcw[pll_id_val].shift;
> +
> +	offset = fmeter_dev_ptr->prediv[pll_id_val].offset +
> +		fmeter_dev_ptr->shu_of * shu_lv_val;
> +	prediv_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> +		fmeter_dev_ptr->prediv[pll_id_val].mask) >>
> +		fmeter_dev_ptr->prediv[pll_id_val].shift;
> +
> +	offset = fmeter_dev_ptr->posdiv[pll_id_val].offset +
> +		fmeter_dev_ptr->shu_of * shu_lv_val;
> +	posdiv_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> +		fmeter_dev_ptr->posdiv[pll_id_val].mask) >>
> +		fmeter_dev_ptr->posdiv[pll_id_val].shift;
> +
> +	offset = fmeter_dev_ptr->ckdiv4[pll_id_val].offset +
> +		fmeter_dev_ptr->shu_of * shu_lv_val;
> +	ckdiv4_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> +		fmeter_dev_ptr->ckdiv4[pll_id_val].mask) >>
> +		fmeter_dev_ptr->ckdiv4[pll_id_val].shift;
> +
> +	offset = fmeter_dev_ptr->cldiv2[pll_id_val].offset +
> +		fmeter_dev_ptr->shu_of * shu_lv_val;
> +	cldiv2_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> +		fmeter_dev_ptr->cldiv2[pll_id_val].mask) >>
> +		fmeter_dev_ptr->cldiv2[pll_id_val].shift;
> +
> +	vco_freq = ((fmeter_dev_ptr->crystal_freq >> prediv_val) *
> +		(sdmpcw_val >> 8)) >>
> +		posdiv_val >> ckdiv4_val >> pll_md_val >> cldiv2_val;
> +
> +	return decode_freq(vco_freq);
> +}
> +
> +/*
> + * mtk_dramc_get_data_rate - calculate DRAM data rate
> + *
> + * Return DRAM data rate (MB/s)
> + */
> +unsigned int mtk_dramc_get_data_rate(void)
> +{
> +	struct dramc_dev_t *dramc_dev_ptr;
> +	struct fmeter_dev_t *fmeter_dev_ptr;
> +
> +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> +
> +	if (!dramc_dev_ptr)
> +		return 0;
> +
> +	fmeter_dev_ptr = (struct fmeter_dev_t *)dramc_dev_ptr->fmeter_dev_ptr;
> +	if (!fmeter_dev_ptr)
> +		return 0;
> +
> +	return fmeter_v0(dramc_dev_ptr);
> +}
> +EXPORT_SYMBOL(mtk_dramc_get_data_rate);
> +
> +static unsigned int mr4_v1(struct dramc_dev_t *dramc_dev_ptr, unsigned int ch)
> +{
> +	struct mr4_dev_t *mr4_dev_ptr =
> +		(struct mr4_dev_t *)dramc_dev_ptr->mr4_dev_ptr;
> +
> +	return (readl(dramc_dev_ptr->dramc_chn_base_nao[ch] +
> +		mr4_dev_ptr->mr4_rg.offset) & mr4_dev_ptr->mr4_rg.mask) >>
> +		mr4_dev_ptr->mr4_rg.shift;
> +}
> +
> +/*
> + * mtk_dramc_get_mr4 - get the DRAM MR4 value of specific DRAM channel
> + * @ch:	the channel index
> + *
> + * Return the MR4 value
> + */
> +unsigned int mtk_dramc_get_mr4(unsigned int ch)
> +{
> +	struct dramc_dev_t *dramc_dev_ptr;
> +	struct mr4_dev_t *mr4_dev_ptr;
> +
> +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> +
> +	if (!dramc_dev_ptr)
> +		return 0;
> +
> +	mr4_dev_ptr = (struct mr4_dev_t *)dramc_dev_ptr->mr4_dev_ptr;
> +	if (!mr4_dev_ptr)
> +		return 0;
> +
> +	if (ch >= dramc_dev_ptr->channel_cnt)
> +		return 0;
> +
> +	return mr4_v1(dramc_dev_ptr, ch);
> +}
> +EXPORT_SYMBOL(mtk_dramc_get_mr4);
> +
> +/*
> + * mtk_dramc_get_ddr_type - get DRAM type
> + *
> + * Return the DRAM type
> + */
> +unsigned int mtk_dramc_get_ddr_type(void)
> +{
> +	struct dramc_dev_t *dramc_dev_ptr;
> +
> +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> +
> +	if (!dramc_dev_ptr)
> +		return 0;
> +
> +	return dramc_dev_ptr->dram_type;
> +}
> +EXPORT_SYMBOL(mtk_dramc_get_ddr_type);
> +
> +/*
> + * mtk_dramc_get_channel_count - get DRAM channel count
> + *
> + * Return the DRAM channel count
> + */
> +unsigned int mtk_dramc_get_channel_count(void)
> +{
> +	struct dramc_dev_t *dramc_dev_ptr;
> +
> +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> +
> +	if (!dramc_dev_ptr)
> +		return 0;
> +
> +	return dramc_dev_ptr->channel_cnt;
> +}
> +EXPORT_SYMBOL(mtk_dramc_get_channel_count);
> +
> +/*
> + * mtk_dramc_get_rank_count - get DRAM rank count
> + *
> + * Return the DRAM rank count
> + */
> +unsigned int mtk_dramc_get_rank_count(void)
> +{
> +	struct dramc_dev_t *dramc_dev_ptr;
> +
> +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> +
> +	if (!dramc_dev_ptr)
> +		return 0;
> +
> +	return dramc_dev_ptr->rank_cnt;
> +}
> +EXPORT_SYMBOL(mtk_dramc_get_rank_count);
> +
> +/*
> + * mtk_dramc_get_rank_size - get size of DRAM rank
> + *
> + * Return the size of specific DRAM rank
> + */
> +unsigned int mtk_dramc_get_rank_size(unsigned int rank)
> +{
> +	struct dramc_dev_t *dramc_dev_ptr;
> +
> +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> +
> +	if (!dramc_dev_ptr)
> +		return 0;
> +
> +	if (rank < dramc_dev_ptr->rank_cnt)
> +		return dramc_dev_ptr->rank_size[rank];
> +	else
> +		return 0;
> +}
> +EXPORT_SYMBOL(mtk_dramc_get_rank_size);
> +
> +static int dramc_remove(struct platform_device *pdev)
> +{
> +	return 0;
> +}
> +
> +static struct platform_driver dramc_drv = {
> +	.probe = dramc_probe,
> +	.remove = dramc_remove,
> +	.driver = {
> +		.name = DRAMC_DRV_NAME,
> +		.owner = THIS_MODULE,
> +		.of_match_table = mtk_dramc_of_ids,
> +	},
> +};
> +
> +static int __init dramc_drv_init(void)
> +{
> +	int ret;
> +
> +	ret = platform_driver_register(&dramc_drv);
> +	if (ret) {
> +		pr_info("%s: init fail, ret 0x%x\n", __func__, ret);
> +		return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static void __exit dramc_drv_exit(void)
> +{
> +	platform_driver_unregister(&dramc_drv);
> +}
> +
> +module_init(dramc_drv_init);
> +module_exit(dramc_drv_exit);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("MediaTek DRAMC Driver");
> +MODULE_AUTHOR("Po-Kai Chi <pk.chi@mediatek.com>");
> diff --git a/include/memory/mediatek/dramc.h b/include/memory/mediatek/dramc.h
> new file mode 100644
> index 0000000..c8d200f
> --- /dev/null
> +++ b/include/memory/mediatek/dramc.h
> @@ -0,0 +1,18 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2021 MediaTek Inc.
> + */
> +
> +#ifndef __DRAMC_H__
> +#define __DRAMC_H__
> +
> +int mtk_dramc_get_steps_freq(unsigned int step);
> +unsigned int mtk_dramc_get_ddr_type(void);
> +unsigned int mtk_dramc_get_data_rate(void);
> +unsigned int mtk_dramc_get_mr4(unsigned int ch);
> +unsigned int mtk_dramc_get_channel_count(void);
> +unsigned int mtk_dramc_get_rank_count(void);
> +unsigned int mtk_dramc_get_rank_size(unsigned int rk);
> +
> +#endif /* __DRAMC_H__ */
> +
> -- 
> 1.7.9.5
> 

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v2 2/4] memory: mediatek: add DRAM controller driver
  2021-04-20 18:28     ` Rob Herring
@ 2021-04-27  5:13       ` Po-Kai Chi
  -1 siblings, 0 replies; 22+ messages in thread
From: Po-Kai Chi @ 2021-04-27  5:13 UTC (permalink / raw)
  To: Rob Herring
  Cc: Matthias Brugger, linux-kernel, linux-mediatek, devicetree,
	wsd_upstream, CC Hwang

Hello Rob,

Thanks for your comments.

Po-Kai Chi

On Tue, 2021-04-20 at 13:28 -0500, Rob Herring wrote:
> On Fri, Apr 16, 2021 at 05:32:16PM +0800, Po-Kai Chi wrote:
> > MediaTek DRAM controller (DRAMC) driver provides cross-platform features
> > as below:
> > 
> > 1. provide APIs for low power feature queries
> > 2. create sysfs to pass the DRAM information to user-space
> 
> I'm hesistant with having both DT and sysfs vendor specific memory 
> properties. I think we need something common here.
> 

Sysfs provides users with a unified interface for querying DRAM status
at runtime.

- DRIVER_ATTR_RO(mr)
The content is the same as DT passed from bootloader, but provides
better file path consistency.

- DRIVER_ATTR_RO(mr4)
Used to query the current DRAM chip refresh rate (by reading mode
register-4 at run time).
The naming can be misleading, I'll rename it for the next version.

- DRIVER_ATTR_RO(dram_data_rate)
Used to calculate the current DRAM data rate.

> > 
> > Signed-off-by: Po-Kai Chi <pk.chi@mediatek.com>
> > ---
> >  drivers/memory/Kconfig              |    1 +
> >  drivers/memory/Makefile             |    1 +
> >  drivers/memory/mediatek/Kconfig     |    9 +
> >  drivers/memory/mediatek/Makefile    |    3 +
> >  drivers/memory/mediatek/mtk-dramc.c |  711 +++++++++++++++++++++++++++++++++++
> >  include/memory/mediatek/dramc.h     |   18 +
> >  6 files changed, 743 insertions(+)
> >  create mode 100644 drivers/memory/mediatek/Kconfig
> >  create mode 100644 drivers/memory/mediatek/Makefile
> >  create mode 100644 drivers/memory/mediatek/mtk-dramc.c
> >  create mode 100644 include/memory/mediatek/dramc.h
> > 
> > diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
> > index 72c0df1..056e906 100644
> > --- a/drivers/memory/Kconfig
> > +++ b/drivers/memory/Kconfig
> > @@ -225,6 +225,7 @@ config STM32_FMC2_EBI
> >  	  devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on
> >  	  SOCs containing the FMC2 External Bus Interface.
> >  
> > +source "drivers/memory/mediatek/Kconfig"
> >  source "drivers/memory/samsung/Kconfig"
> >  source "drivers/memory/tegra/Kconfig"
> >  
> > diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
> > index bc7663e..cd4f8cf 100644
> > --- a/drivers/memory/Makefile
> > +++ b/drivers/memory/Makefile
> > @@ -25,6 +25,7 @@ obj-$(CONFIG_PL353_SMC)		+= pl353-smc.o
> >  obj-$(CONFIG_RENESAS_RPCIF)	+= renesas-rpc-if.o
> >  obj-$(CONFIG_STM32_FMC2_EBI)	+= stm32-fmc2-ebi.o
> >  
> > +obj-$(CONFIG_MTK_DRAMC)		+= mediatek/
> >  obj-$(CONFIG_SAMSUNG_MC)	+= samsung/
> >  obj-$(CONFIG_TEGRA_MC)		+= tegra/
> >  obj-$(CONFIG_TI_EMIF_SRAM)	+= ti-emif-sram.o
> > diff --git a/drivers/memory/mediatek/Kconfig b/drivers/memory/mediatek/Kconfig
> > new file mode 100644
> > index 0000000..a1618b0
> > --- /dev/null
> > +++ b/drivers/memory/mediatek/Kconfig
> > @@ -0,0 +1,9 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +config MTK_DRAMC
> > +	tristate "MediaTek DRAMC driver"
> > +	help
> > +	  This selects the MediaTek(R) DRAMC driver.
> > +	  Provide the API for DRAMC low power scenario, and the interface
> > +	  for reporting DRAM information, e.g. DRAM mode register (MR) for
> > +	  DRAM vendor ID, temperature, and density.
> > diff --git a/drivers/memory/mediatek/Makefile b/drivers/memory/mediatek/Makefile
> > new file mode 100644
> > index 0000000..632be48
> > --- /dev/null
> > +++ b/drivers/memory/mediatek/Makefile
> > @@ -0,0 +1,3 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +obj-$(CONFIG_MTK_DRAMC)	+= mtk-dramc.o
> > diff --git a/drivers/memory/mediatek/mtk-dramc.c b/drivers/memory/mediatek/mtk-dramc.c
> > new file mode 100644
> > index 0000000..155b3b7
> > --- /dev/null
> > +++ b/drivers/memory/mediatek/mtk-dramc.c
> > @@ -0,0 +1,711 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2021 MediaTek Inc.
> > + */
> > +
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/device.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_device.h>
> > +#include <linux/printk.h>
> > +#include <linux/slab.h>
> > +#include <linux/io.h>
> > +#include <memory/mediatek/dramc.h>
> > +
> > +#define DRAMC_DRV_NAME	"mtk-dramc"
> > +
> > +struct mr_info_t {
> > +	unsigned int mr_index;
> > +	unsigned int mr_value;
> > +};
> > +
> > +/*
> > + * struct reg_ctrl_t - to describe the bits required in a register
> > + * @offset: register address offset from a base
> > + * @mask: bitmask of the target bits
> > + * @shift: starting bit of the target bits
> > + */
> > +struct reg_ctrl_t {
> > +	unsigned int offset;
> > +	unsigned int mask;
> > +	unsigned int shift;
> > +};
> > +
> > +struct fmeter_dev_t {
> > +	unsigned int crystal_freq;
> > +	unsigned int shu_of;
> > +	struct reg_ctrl_t shu_lv;
> > +	struct reg_ctrl_t pll_id;
> > +	struct reg_ctrl_t pll_md[2];
> > +	struct reg_ctrl_t sdmpcw[2];
> > +	struct reg_ctrl_t prediv[2];
> > +	struct reg_ctrl_t posdiv[2];
> > +	struct reg_ctrl_t ckdiv4[2];
> > +	struct reg_ctrl_t cldiv2[2];
> > +	struct reg_ctrl_t fbksel[2];
> > +	struct reg_ctrl_t dqopen[2];
> > +};
> > +
> > +struct mr4_dev_t {
> > +	struct reg_ctrl_t mr4_rg;
> > +};
> > +
> > +struct dramc_dev_t {
> > +	unsigned int dram_type;
> > +	unsigned int support_channel_cnt;
> > +	unsigned int channel_cnt;
> > +	unsigned int rank_cnt;
> > +	unsigned int mr_cnt;
> > +	unsigned int freq_cnt;
> > +	unsigned int *rank_size;
> > +	unsigned int *freq_step;
> > +	struct mr_info_t *mr_info_ptr;
> > +	void __iomem **dramc_chn_base_ao;
> > +	void __iomem **dramc_chn_base_nao;
> > +	void __iomem **ddrphy_chn_base_ao;
> > +	void *mr4_dev_ptr;
> > +	void *fmeter_dev_ptr;
> > +};
> > +
> > +enum DRAM_TYPE {
> > +	TYPE_NONE = 0,
> > +	TYPE_DDR1,
> > +	TYPE_LPDDR2,
> > +	TYPE_LPDDR3,
> > +	TYPE_PCDDR3,
> > +	TYPE_LPDDR4,
> > +	TYPE_LPDDR4X,
> > +	TYPE_LPDDR4P
> > +};
> 
> This doesn't appear to be used.

Yes, it is not currently in use, I'll remove it.

> > +
> > +static const struct fmeter_dev_t fmeter_v0_mt6779_t = {
> > +	.crystal_freq = 52,
> > +	.shu_of = 0x500,
> > +	.shu_lv = { .offset = 0x00e4, .mask = 0x00000006, .shift = 1 },
> > +	.pll_id = { .offset = 0x0510, .mask = 0x80000000, .shift = 31 },
> > +	.pll_md = {
> > +		{ .offset = 0x0d84, .mask = 0x00000100, .shift = 8 },
> > +		{ .offset = 0x0d84, .mask = 0x00000100, .shift = 8 },
> > +	},
> > +	.sdmpcw = {
> > +		{ .offset = 0x0d9c, .mask = 0xffff0000, .shift = 16 },
> > +		{ .offset = 0x0d94, .mask = 0xffff0000, .shift = 16 },
> > +	},
> > +	.prediv = {
> > +		{ .offset = 0x0da8, .mask = 0x000c0000, .shift = 18 },
> > +		{ .offset = 0x0da0, .mask = 0x000c0000, .shift = 18 },
> > +	},
> > +	.posdiv = {
> > +		{ .offset = 0x0da8, .mask = 0x00000007, .shift = 0 },
> > +		{ .offset = 0x0da0, .mask = 0x00000007, .shift = 0 },
> > +	},
> > +	.ckdiv4 = {
> > +		{ .offset = 0x0d18, .mask = 0x08000000, .shift = 27 },
> > +		{ .offset = 0x0d18, .mask = 0x08000000, .shift = 27 },
> > +	},
> > +	.cldiv2 = {
> > +		{ .offset = 0x0c38, .mask = 0x80000000, .shift = 31 },
> > +		{ .offset = 0x0c38, .mask = 0x80000000, .shift = 31 },
> > +	},
> > +};
> > +
> > +static const struct mr4_dev_t mr4_v1_mt6779_t = {
> > +	.mr4_rg = { .offset = 0x0090, .mask = 0x0000ffff, .shift = 0 },
> > +};
> > +
> > +struct mtk_dramc_compatible {
> > +	const struct fmeter_dev_t *fmeter;
> > +	const struct mr4_dev_t *mr4;
> > +};
> > +
> > +static const struct mtk_dramc_compatible mt6779_compat = {
> > +	.fmeter = &fmeter_v0_mt6779_t,
> > +	.mr4 = &mr4_v1_mt6779_t,
> > +};
> > +
> > +static const struct of_device_id mtk_dramc_of_ids[] = {
> > +	{ .compatible = "mediatek,mt6779-dramc", .data = &mt6779_compat },
> > +	{}
> > +};
> > +MODULE_DEVICE_TABLE(of, mtk_dramc_of_ids);
> > +
> > +/*
> > + * mtk_dramc_get_drvdata_by_ids - get the dramc driver data
> > + *
> > + * Return the dramc driver data
> > + */
> > +static struct dramc_dev_t *mtk_dramc_get_drvdata_by_ids(void)
> > +{
> > +	struct device_node *np;
> > +	struct platform_device *dramc_pdev;
> > +
> > +	np = of_find_matching_node_and_match(NULL, mtk_dramc_of_ids, NULL);
> > +	dramc_pdev = of_find_device_by_node(np);
> > +
> > +	if (!dramc_pdev)
> > +		return NULL;
> > +
> > +	return (struct dramc_dev_t *)platform_get_drvdata(dramc_pdev);
> > +}
> > +
> > +static ssize_t mr_show(struct device_driver *driver, char *buf)
> > +{
> > +	struct dramc_dev_t *dramc_dev_ptr;
> > +	struct mr_info_t *mr_info_ptr;
> > +	unsigned int i;
> > +	ssize_t ret;
> > +
> > +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> > +
> > +	if (!dramc_dev_ptr)
> > +		return 0;
> > +
> > +	mr_info_ptr = dramc_dev_ptr->mr_info_ptr;
> > +
> > +	for (ret = 0, i = 0; i < dramc_dev_ptr->mr_cnt; i++) {
> > +		ret += snprintf(buf + ret, PAGE_SIZE - ret,
> > +				"mr%d: 0x%x\n",
> 
> The file name is 'mr' and what you read should be just the value.

'mr' is the abbreviation of 'Mode Register', which defined by JEDEC.

There are 64 mode registers in LPDDR4X but not all of them are
meaningful, so we need a <index, value> pair to describe some of them.

Which mode registers will be brought to the kernel are depends on the
bootloader.

> 
> Also, sysfs files require documentation too.

Okay, I'll fill in the requirements.

> 
> > +				mr_info_ptr[i].mr_index,
> > +				mr_info_ptr[i].mr_value);
> > +		if (ret >= PAGE_SIZE)
> > +			return strlen(buf);
> > +	}
> > +
> > +	return strlen(buf);
> > +}
> > +
> > +static ssize_t mr4_show(struct device_driver *driver, char *buf)
> > +{
> > +	struct dramc_dev_t *dramc_dev_ptr;
> > +	unsigned int i;
> > +	ssize_t ret;
> > +
> > +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> > +
> > +	if (!dramc_dev_ptr)
> > +		return 0;
> > +
> > +	for (ret = 0, i = 0; i < dramc_dev_ptr->channel_cnt; i++) {
> > +		ret += snprintf(buf + ret, PAGE_SIZE - ret,
> > +				"mr4: ch%d 0x%x\n",
> 
> Same issues here.
> 
> > +				i, mtk_dramc_get_mr4(i));
> > +		if (ret >= PAGE_SIZE)
> > +			return strlen(buf);
> > +	}
> > +
> > +	return strlen(buf);
> > +}
> > +
> > +static ssize_t dram_data_rate_show(struct device_driver *driver, char *buf)
> > +{
> > +	return snprintf(buf, PAGE_SIZE, "DRAM data rate = %d\n",
> > +			mtk_dramc_get_data_rate());
> 
> And here.
> 
> > +}
> > +
> > +static DRIVER_ATTR_RO(mr);
> > +static DRIVER_ATTR_RO(mr4);
> > +static DRIVER_ATTR_RO(dram_data_rate);
> 
> A driver attr would be global. Shouldn't these be device attr's which 
> are per instance.

There is always only one DRAMC instance in the MediaTek SoC, 

> 
> You should also be using attribute groups.

Okay.

> 
> > +
> > +static int dramc_probe(struct platform_device *pdev)
> > +{
> > +	struct device_node *dramc_node = pdev->dev.of_node;
> > +	struct dramc_dev_t *dramc_dev_ptr;
> > +	const struct mtk_dramc_compatible *dev_comp;
> > +	struct resource *res;
> > +	unsigned int i, size;
> > +	int ret;
> > +
> > +	pr_info("%s: module probe.\n", __func__);
> > +
> > +	dramc_dev_ptr = devm_kmalloc(&pdev->dev,
> 
> Probably want to use devm_kzalloc instead.

It would be better, I'll update it.

> 
> > +				     sizeof(struct dramc_dev_t),
> > +				     GFP_KERNEL);
> > +
> > +	dev_comp =
> > +		(struct mtk_dramc_compatible *)
> > +			of_device_get_match_data(&pdev->dev);
> > +
> > +	if (!dramc_dev_ptr)
> > +		return -ENOMEM;
> > +
> > +	ret = of_property_read_u32(dramc_node,
> > +				   "mediatek,dram-type",
> > +				   &dramc_dev_ptr->dram_type);
> > +	if (ret) {
> > +		pr_info("%s: get dram_type fail\n", __func__);
> > +		return -EINVAL;
> > +	}
> > +
> > +	ret = of_property_read_u32(dramc_node,
> > +				   "mediatek,support-channel-cnt",
> > +				   &dramc_dev_ptr->support_channel_cnt);
> > +	if (ret) {
> > +		pr_info("%s: get support_channel_cnt fail\n", __func__);
> > +		return -EINVAL;
> > +	}
> > +
> > +	ret = of_property_read_u32(dramc_node,
> > +				   "mediatek,channel-cnt",
> > +				   &dramc_dev_ptr->channel_cnt);
> > +	if (ret) {
> > +		pr_info("%s: get channel_cnt fail\n", __func__);
> > +		return -EINVAL;
> > +	}
> > +
> > +	ret = of_property_read_u32(dramc_node,
> > +				   "mediatek,rank-cnt",
> > +				   &dramc_dev_ptr->rank_cnt);
> > +	if (ret) {
> > +		pr_info("%s: get rank_cnt fail\n", __func__);
> > +		return -EINVAL;
> > +	}
> > +
> > +	ret = of_property_read_u32(dramc_node,
> > +				   "mediatek,mr-cnt",
> > +				   &dramc_dev_ptr->mr_cnt);
> > +	if (ret) {
> > +		pr_info("%s: get mr_cnt fail\n", __func__);
> > +		return -EINVAL;
> > +	}
> > +
> > +	ret = of_property_read_u32(dramc_node,
> > +				   "mediatek,freq-cnt",
> > +				   &dramc_dev_ptr->freq_cnt);
> > +	if (ret) {
> > +		pr_info("%s: get freq_cnt fail\n", __func__);
> > +		return -EINVAL;
> > +	}
> > +
> > +	dramc_dev_ptr->mr4_dev_ptr = (void *)dev_comp->mr4;
> > +
> > +	pr_info("%s: %s(%d),%s(%d),%s(%d),%s(%d),%s(%d),%s(%d),%s(%s)\n",
> > +		__func__,
> > +		"dram_type", dramc_dev_ptr->dram_type,
> > +		"support_channel_cnt", dramc_dev_ptr->support_channel_cnt,
> > +		"channel_cnt", dramc_dev_ptr->channel_cnt,
> > +		"rank_cnt", dramc_dev_ptr->rank_cnt,
> > +		"mr_cnt", dramc_dev_ptr->mr_cnt,
> > +		"freq_cnt", dramc_dev_ptr->freq_cnt,
> > +		"mr4", (dramc_dev_ptr->mr4_dev_ptr) ? "true" : "false");
> 
> Why do you need this? It's already in DT which is readable from 
> userspace.

To make the driver status easy to see when probing it.

> 
> > +
> > +	size = sizeof(unsigned int) * dramc_dev_ptr->rank_cnt;
> > +	dramc_dev_ptr->rank_size = devm_kmalloc(&pdev->dev, size, GFP_KERNEL);
> > +	if (!(dramc_dev_ptr->rank_size))
> > +		return -ENOMEM;
> > +	ret = of_property_read_u32_array(dramc_node,
> > +					 "mediatek,rank-size",
> > +					 dramc_dev_ptr->rank_size,
> > +					 dramc_dev_ptr->rank_cnt);
> > +	if (ret) {
> > +		pr_info("%s: get rank_size fail\n", __func__);
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (dramc_dev_ptr->mr_cnt) {
> > +		size = sizeof(struct mr_info_t) * dramc_dev_ptr->mr_cnt;
> > +		dramc_dev_ptr->mr_info_ptr = devm_kmalloc(&pdev->dev,
> > +							  size,
> > +							  GFP_KERNEL);
> > +		if (!(dramc_dev_ptr->mr_info_ptr))
> > +			return -ENOMEM;
> > +		ret =
> > +		    of_property_read_u32_array(dramc_node,
> > +					       "mediatek,mr",
> > +					       (unsigned int *)dramc_dev_ptr->mr_info_ptr,
> > +					       size >> 2);
> > +		if (ret) {
> > +			pr_info("%s: get mr_info fail\n", __func__);
> > +			return -EINVAL;
> > +		}
> > +		for (i = 0; i < dramc_dev_ptr->mr_cnt; i++)
> > +			pr_info("%s: mr%d(%x)\n", __func__,
> > +				dramc_dev_ptr->mr_info_ptr[i].mr_index,
> > +				dramc_dev_ptr->mr_info_ptr[i].mr_value);
> > +	}
> > +
> > +	if (dramc_dev_ptr->freq_cnt) {
> > +		size = sizeof(unsigned int) * dramc_dev_ptr->freq_cnt * 2;
> > +		dramc_dev_ptr->freq_step =
> > +			devm_kmalloc(&pdev->dev, size, GFP_KERNEL);
> > +		if (!(dramc_dev_ptr->freq_step))
> > +			return -ENOMEM;
> > +		ret = of_property_read_u32_array(dramc_node,
> > +						 "mediatek,freq-step",
> > +						 dramc_dev_ptr->freq_step,
> > +						 dramc_dev_ptr->freq_cnt * 2);
> > +		if (ret) {
> > +			pr_info("%s: get freq_step fail\n", __func__);
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	size = sizeof(phys_addr_t) * dramc_dev_ptr->support_channel_cnt;
> > +	dramc_dev_ptr->dramc_chn_base_ao = devm_kmalloc(&pdev->dev,
> > +							size, GFP_KERNEL);
> > +	if (!(dramc_dev_ptr->dramc_chn_base_ao))
> > +		return -ENOMEM;
> > +	dramc_dev_ptr->dramc_chn_base_nao = devm_kmalloc(&pdev->dev,
> > +							 size, GFP_KERNEL);
> > +	if (!(dramc_dev_ptr->dramc_chn_base_nao))
> > +		return -ENOMEM;
> > +	dramc_dev_ptr->ddrphy_chn_base_ao = devm_kmalloc(&pdev->dev,
> > +							 size, GFP_KERNEL);
> > +	if (!(dramc_dev_ptr->ddrphy_chn_base_ao))
> > +		return -ENOMEM;
> > +
> > +	for (i = 0; i < dramc_dev_ptr->support_channel_cnt; i++) {
> > +		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
> > +		dramc_dev_ptr->dramc_chn_base_ao[i] =
> > +			devm_ioremap_resource(&pdev->dev, res);
> > +		if (IS_ERR(dramc_dev_ptr->dramc_chn_base_ao[i])) {
> > +			pr_info("%s: unable to map ch%d DRAMC AO base\n",
> > +				__func__, i);
> > +			return -EINVAL;
> > +		}
> > +
> > +		res = platform_get_resource(pdev, IORESOURCE_MEM,
> > +					    i + dramc_dev_ptr->support_channel_cnt);
> > +		dramc_dev_ptr->dramc_chn_base_nao[i] =
> > +			devm_ioremap_resource(&pdev->dev, res);
> > +		if (IS_ERR(dramc_dev_ptr->dramc_chn_base_nao[i])) {
> > +			pr_info("%s: unable to map ch%d DRAMC NAO base\n",
> > +				__func__, i);
> > +			return -EINVAL;
> > +		}
> > +
> > +		res = platform_get_resource(pdev, IORESOURCE_MEM,
> > +					    i + dramc_dev_ptr->support_channel_cnt * 2);
> > +		dramc_dev_ptr->ddrphy_chn_base_ao[i] =
> > +			devm_ioremap_resource(&pdev->dev, res);
> > +		if (IS_ERR(dramc_dev_ptr->ddrphy_chn_base_ao[i])) {
> > +			pr_info("%s: unable to map ch%d DDRPHY AO base\n",
> > +				__func__, i);
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	dramc_dev_ptr->fmeter_dev_ptr = (void *)dev_comp->fmeter;
> > +
> > +	ret = driver_create_file(pdev->dev.driver,
> > +				 &driver_attr_dram_data_rate);
> > +	if (ret) {
> > +		pr_info("%s: fail to create dram_data_rate sysfs\n", __func__);
> > +		return ret;
> > +	}
> > +
> > +	ret = driver_create_file(pdev->dev.driver,
> > +				 &driver_attr_mr);
> > +	if (ret) {
> > +		pr_info("%s: fail to create mr sysfs\n", __func__);
> > +		return ret;
> > +	}
> > +
> > +	if (dramc_dev_ptr->mr4_dev_ptr) {
> > +		ret = driver_create_file(pdev->dev.driver,
> > +					 &driver_attr_mr4);
> > +		if (ret) {
> > +			pr_info("%s: fail to create mr4 sysfs\n", __func__);
> > +			return ret;
> > +		}
> > +	}
> > +
> > +	platform_set_drvdata(pdev, dramc_dev_ptr);
> > +	pr_info("%s: DRAM data type = %d\n", __func__,
> > +		mtk_dramc_get_ddr_type());
> > +
> > +	pr_info("%s: DRAM data clock rate = %d\n", __func__,
> > +		mtk_dramc_get_data_rate());
> > +
> > +	return ret;
> > +}
> > +
> > +/*
> > + * mtk_dramc_get_steps_freq - get the data clock rate of target DVFS step
> > + * @step: the step index of DVFS
> > + *
> > + * Return the DRAM spec data clock rate (MHz)
> > + */
> > +int mtk_dramc_get_steps_freq(unsigned int step)
> > +{
> > +	struct dramc_dev_t *dramc_dev_ptr;
> > +
> > +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> > +
> > +	if (!dramc_dev_ptr)
> > +		return -ENODEV;
> > +
> > +	if (step < dramc_dev_ptr->freq_cnt)
> > +		return dramc_dev_ptr->freq_step[step * 2 + 1];
> > +
> > +	return -EINVAL;
> > +}
> > +EXPORT_SYMBOL(mtk_dramc_get_steps_freq);
> > +
> > +/*
> > + * decode_freq - decode the spec data clock rate
> > + * @vco_freq: real data clock rate
> > + *
> > + * Return the DRAM spec data clock rate (MHz)
> > + */
> > +static unsigned int decode_freq(unsigned int vco_freq)
> > +{
> > +	int i;
> > +	struct dramc_dev_t *dramc_dev_ptr;
> > +
> > +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> > +
> > +	if (!dramc_dev_ptr)
> > +		return 0;
> > +
> > +	for (i = 0; i < dramc_dev_ptr->freq_cnt * 2; i += 2)
> > +		if (vco_freq == dramc_dev_ptr->freq_step[i])
> > +			return dramc_dev_ptr->freq_step[i + 1];
> > +
> > +	return vco_freq;
> > +}
> > +
> > +static unsigned int fmeter_v0(struct dramc_dev_t *dramc_dev_ptr)
> > +{
> > +	struct fmeter_dev_t *fmeter_dev_ptr =
> > +		(struct fmeter_dev_t *)dramc_dev_ptr->fmeter_dev_ptr;
> > +	unsigned int shu_lv_val;
> > +	unsigned int pll_id_val;
> > +	unsigned int pll_md_val;
> > +	unsigned int sdmpcw_val;
> > +	unsigned int prediv_val;
> > +	unsigned int posdiv_val;
> > +	unsigned int ckdiv4_val;
> > +	unsigned int cldiv2_val;
> > +	unsigned int offset;
> > +	unsigned int vco_freq;
> > +
> > +	shu_lv_val = (readl(dramc_dev_ptr->dramc_chn_base_ao[0] +
> > +		fmeter_dev_ptr->shu_lv.offset) &
> > +		fmeter_dev_ptr->shu_lv.mask) >>
> > +		fmeter_dev_ptr->shu_lv.shift;
> > +
> > +	pll_id_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] +
> > +		fmeter_dev_ptr->pll_id.offset) &
> > +		fmeter_dev_ptr->pll_id.mask) >>
> > +		fmeter_dev_ptr->pll_id.shift;
> > +
> > +	offset = fmeter_dev_ptr->pll_md[pll_id_val].offset +
> > +		fmeter_dev_ptr->shu_of * shu_lv_val;
> > +	pll_md_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> > +		fmeter_dev_ptr->pll_md[pll_id_val].mask) >>
> > +		fmeter_dev_ptr->pll_md[pll_id_val].shift;
> > +
> > +	offset = fmeter_dev_ptr->sdmpcw[pll_id_val].offset +
> > +		fmeter_dev_ptr->shu_of * shu_lv_val;
> > +	sdmpcw_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> > +		fmeter_dev_ptr->sdmpcw[pll_id_val].mask) >>
> > +		fmeter_dev_ptr->sdmpcw[pll_id_val].shift;
> > +
> > +	offset = fmeter_dev_ptr->prediv[pll_id_val].offset +
> > +		fmeter_dev_ptr->shu_of * shu_lv_val;
> > +	prediv_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> > +		fmeter_dev_ptr->prediv[pll_id_val].mask) >>
> > +		fmeter_dev_ptr->prediv[pll_id_val].shift;
> > +
> > +	offset = fmeter_dev_ptr->posdiv[pll_id_val].offset +
> > +		fmeter_dev_ptr->shu_of * shu_lv_val;
> > +	posdiv_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> > +		fmeter_dev_ptr->posdiv[pll_id_val].mask) >>
> > +		fmeter_dev_ptr->posdiv[pll_id_val].shift;
> > +
> > +	offset = fmeter_dev_ptr->ckdiv4[pll_id_val].offset +
> > +		fmeter_dev_ptr->shu_of * shu_lv_val;
> > +	ckdiv4_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> > +		fmeter_dev_ptr->ckdiv4[pll_id_val].mask) >>
> > +		fmeter_dev_ptr->ckdiv4[pll_id_val].shift;
> > +
> > +	offset = fmeter_dev_ptr->cldiv2[pll_id_val].offset +
> > +		fmeter_dev_ptr->shu_of * shu_lv_val;
> > +	cldiv2_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> > +		fmeter_dev_ptr->cldiv2[pll_id_val].mask) >>
> > +		fmeter_dev_ptr->cldiv2[pll_id_val].shift;
> > +
> > +	vco_freq = ((fmeter_dev_ptr->crystal_freq >> prediv_val) *
> > +		(sdmpcw_val >> 8)) >>
> > +		posdiv_val >> ckdiv4_val >> pll_md_val >> cldiv2_val;
> > +
> > +	return decode_freq(vco_freq);
> > +}
> > +
> > +/*
> > + * mtk_dramc_get_data_rate - calculate DRAM data rate
> > + *
> > + * Return DRAM data rate (MB/s)
> > + */
> > +unsigned int mtk_dramc_get_data_rate(void)
> > +{
> > +	struct dramc_dev_t *dramc_dev_ptr;
> > +	struct fmeter_dev_t *fmeter_dev_ptr;
> > +
> > +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> > +
> > +	if (!dramc_dev_ptr)
> > +		return 0;
> > +
> > +	fmeter_dev_ptr = (struct fmeter_dev_t *)dramc_dev_ptr->fmeter_dev_ptr;
> > +	if (!fmeter_dev_ptr)
> > +		return 0;
> > +
> > +	return fmeter_v0(dramc_dev_ptr);
> > +}
> > +EXPORT_SYMBOL(mtk_dramc_get_data_rate);
> > +
> > +static unsigned int mr4_v1(struct dramc_dev_t *dramc_dev_ptr, unsigned int ch)
> > +{
> > +	struct mr4_dev_t *mr4_dev_ptr =
> > +		(struct mr4_dev_t *)dramc_dev_ptr->mr4_dev_ptr;
> > +
> > +	return (readl(dramc_dev_ptr->dramc_chn_base_nao[ch] +
> > +		mr4_dev_ptr->mr4_rg.offset) & mr4_dev_ptr->mr4_rg.mask) >>
> > +		mr4_dev_ptr->mr4_rg.shift;
> > +}
> > +
> > +/*
> > + * mtk_dramc_get_mr4 - get the DRAM MR4 value of specific DRAM channel
> > + * @ch:	the channel index
> > + *
> > + * Return the MR4 value
> > + */
> > +unsigned int mtk_dramc_get_mr4(unsigned int ch)
> > +{
> > +	struct dramc_dev_t *dramc_dev_ptr;
> > +	struct mr4_dev_t *mr4_dev_ptr;
> > +
> > +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> > +
> > +	if (!dramc_dev_ptr)
> > +		return 0;
> > +
> > +	mr4_dev_ptr = (struct mr4_dev_t *)dramc_dev_ptr->mr4_dev_ptr;
> > +	if (!mr4_dev_ptr)
> > +		return 0;
> > +
> > +	if (ch >= dramc_dev_ptr->channel_cnt)
> > +		return 0;
> > +
> > +	return mr4_v1(dramc_dev_ptr, ch);
> > +}
> > +EXPORT_SYMBOL(mtk_dramc_get_mr4);
> > +
> > +/*
> > + * mtk_dramc_get_ddr_type - get DRAM type
> > + *
> > + * Return the DRAM type
> > + */
> > +unsigned int mtk_dramc_get_ddr_type(void)
> > +{
> > +	struct dramc_dev_t *dramc_dev_ptr;
> > +
> > +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> > +
> > +	if (!dramc_dev_ptr)
> > +		return 0;
> > +
> > +	return dramc_dev_ptr->dram_type;
> > +}
> > +EXPORT_SYMBOL(mtk_dramc_get_ddr_type);
> > +
> > +/*
> > + * mtk_dramc_get_channel_count - get DRAM channel count
> > + *
> > + * Return the DRAM channel count
> > + */
> > +unsigned int mtk_dramc_get_channel_count(void)
> > +{
> > +	struct dramc_dev_t *dramc_dev_ptr;
> > +
> > +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> > +
> > +	if (!dramc_dev_ptr)
> > +		return 0;
> > +
> > +	return dramc_dev_ptr->channel_cnt;
> > +}
> > +EXPORT_SYMBOL(mtk_dramc_get_channel_count);
> > +
> > +/*
> > + * mtk_dramc_get_rank_count - get DRAM rank count
> > + *
> > + * Return the DRAM rank count
> > + */
> > +unsigned int mtk_dramc_get_rank_count(void)
> > +{
> > +	struct dramc_dev_t *dramc_dev_ptr;
> > +
> > +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> > +
> > +	if (!dramc_dev_ptr)
> > +		return 0;
> > +
> > +	return dramc_dev_ptr->rank_cnt;
> > +}
> > +EXPORT_SYMBOL(mtk_dramc_get_rank_count);
> > +
> > +/*
> > + * mtk_dramc_get_rank_size - get size of DRAM rank
> > + *
> > + * Return the size of specific DRAM rank
> > + */
> > +unsigned int mtk_dramc_get_rank_size(unsigned int rank)
> > +{
> > +	struct dramc_dev_t *dramc_dev_ptr;
> > +
> > +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> > +
> > +	if (!dramc_dev_ptr)
> > +		return 0;
> > +
> > +	if (rank < dramc_dev_ptr->rank_cnt)
> > +		return dramc_dev_ptr->rank_size[rank];
> > +	else
> > +		return 0;
> > +}
> > +EXPORT_SYMBOL(mtk_dramc_get_rank_size);
> > +
> > +static int dramc_remove(struct platform_device *pdev)
> > +{
> > +	return 0;
> > +}
> > +
> > +static struct platform_driver dramc_drv = {
> > +	.probe = dramc_probe,
> > +	.remove = dramc_remove,
> > +	.driver = {
> > +		.name = DRAMC_DRV_NAME,
> > +		.owner = THIS_MODULE,
> > +		.of_match_table = mtk_dramc_of_ids,
> > +	},
> > +};
> > +
> > +static int __init dramc_drv_init(void)
> > +{
> > +	int ret;
> > +
> > +	ret = platform_driver_register(&dramc_drv);
> > +	if (ret) {
> > +		pr_info("%s: init fail, ret 0x%x\n", __func__, ret);
> > +		return ret;
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +static void __exit dramc_drv_exit(void)
> > +{
> > +	platform_driver_unregister(&dramc_drv);
> > +}
> > +
> > +module_init(dramc_drv_init);
> > +module_exit(dramc_drv_exit);
> > +
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_DESCRIPTION("MediaTek DRAMC Driver");
> > +MODULE_AUTHOR("Po-Kai Chi <pk.chi@mediatek.com>");
> > diff --git a/include/memory/mediatek/dramc.h b/include/memory/mediatek/dramc.h
> > new file mode 100644
> > index 0000000..c8d200f
> > --- /dev/null
> > +++ b/include/memory/mediatek/dramc.h
> > @@ -0,0 +1,18 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (c) 2021 MediaTek Inc.
> > + */
> > +
> > +#ifndef __DRAMC_H__
> > +#define __DRAMC_H__
> > +
> > +int mtk_dramc_get_steps_freq(unsigned int step);
> > +unsigned int mtk_dramc_get_ddr_type(void);
> > +unsigned int mtk_dramc_get_data_rate(void);
> > +unsigned int mtk_dramc_get_mr4(unsigned int ch);
> > +unsigned int mtk_dramc_get_channel_count(void);
> > +unsigned int mtk_dramc_get_rank_count(void);
> > +unsigned int mtk_dramc_get_rank_size(unsigned int rk);
> > +
> > +#endif /* __DRAMC_H__ */
> > +
> > -- 
> > 1.7.9.5
> > 


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v2 2/4] memory: mediatek: add DRAM controller driver
@ 2021-04-27  5:13       ` Po-Kai Chi
  0 siblings, 0 replies; 22+ messages in thread
From: Po-Kai Chi @ 2021-04-27  5:13 UTC (permalink / raw)
  To: Rob Herring
  Cc: Matthias Brugger, linux-kernel, linux-mediatek, devicetree,
	wsd_upstream, CC Hwang

Hello Rob,

Thanks for your comments.

Po-Kai Chi

On Tue, 2021-04-20 at 13:28 -0500, Rob Herring wrote:
> On Fri, Apr 16, 2021 at 05:32:16PM +0800, Po-Kai Chi wrote:
> > MediaTek DRAM controller (DRAMC) driver provides cross-platform features
> > as below:
> > 
> > 1. provide APIs for low power feature queries
> > 2. create sysfs to pass the DRAM information to user-space
> 
> I'm hesistant with having both DT and sysfs vendor specific memory 
> properties. I think we need something common here.
> 

Sysfs provides users with a unified interface for querying DRAM status
at runtime.

- DRIVER_ATTR_RO(mr)
The content is the same as DT passed from bootloader, but provides
better file path consistency.

- DRIVER_ATTR_RO(mr4)
Used to query the current DRAM chip refresh rate (by reading mode
register-4 at run time).
The naming can be misleading, I'll rename it for the next version.

- DRIVER_ATTR_RO(dram_data_rate)
Used to calculate the current DRAM data rate.

> > 
> > Signed-off-by: Po-Kai Chi <pk.chi@mediatek.com>
> > ---
> >  drivers/memory/Kconfig              |    1 +
> >  drivers/memory/Makefile             |    1 +
> >  drivers/memory/mediatek/Kconfig     |    9 +
> >  drivers/memory/mediatek/Makefile    |    3 +
> >  drivers/memory/mediatek/mtk-dramc.c |  711 +++++++++++++++++++++++++++++++++++
> >  include/memory/mediatek/dramc.h     |   18 +
> >  6 files changed, 743 insertions(+)
> >  create mode 100644 drivers/memory/mediatek/Kconfig
> >  create mode 100644 drivers/memory/mediatek/Makefile
> >  create mode 100644 drivers/memory/mediatek/mtk-dramc.c
> >  create mode 100644 include/memory/mediatek/dramc.h
> > 
> > diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
> > index 72c0df1..056e906 100644
> > --- a/drivers/memory/Kconfig
> > +++ b/drivers/memory/Kconfig
> > @@ -225,6 +225,7 @@ config STM32_FMC2_EBI
> >  	  devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on
> >  	  SOCs containing the FMC2 External Bus Interface.
> >  
> > +source "drivers/memory/mediatek/Kconfig"
> >  source "drivers/memory/samsung/Kconfig"
> >  source "drivers/memory/tegra/Kconfig"
> >  
> > diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
> > index bc7663e..cd4f8cf 100644
> > --- a/drivers/memory/Makefile
> > +++ b/drivers/memory/Makefile
> > @@ -25,6 +25,7 @@ obj-$(CONFIG_PL353_SMC)		+= pl353-smc.o
> >  obj-$(CONFIG_RENESAS_RPCIF)	+= renesas-rpc-if.o
> >  obj-$(CONFIG_STM32_FMC2_EBI)	+= stm32-fmc2-ebi.o
> >  
> > +obj-$(CONFIG_MTK_DRAMC)		+= mediatek/
> >  obj-$(CONFIG_SAMSUNG_MC)	+= samsung/
> >  obj-$(CONFIG_TEGRA_MC)		+= tegra/
> >  obj-$(CONFIG_TI_EMIF_SRAM)	+= ti-emif-sram.o
> > diff --git a/drivers/memory/mediatek/Kconfig b/drivers/memory/mediatek/Kconfig
> > new file mode 100644
> > index 0000000..a1618b0
> > --- /dev/null
> > +++ b/drivers/memory/mediatek/Kconfig
> > @@ -0,0 +1,9 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +config MTK_DRAMC
> > +	tristate "MediaTek DRAMC driver"
> > +	help
> > +	  This selects the MediaTek(R) DRAMC driver.
> > +	  Provide the API for DRAMC low power scenario, and the interface
> > +	  for reporting DRAM information, e.g. DRAM mode register (MR) for
> > +	  DRAM vendor ID, temperature, and density.
> > diff --git a/drivers/memory/mediatek/Makefile b/drivers/memory/mediatek/Makefile
> > new file mode 100644
> > index 0000000..632be48
> > --- /dev/null
> > +++ b/drivers/memory/mediatek/Makefile
> > @@ -0,0 +1,3 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +obj-$(CONFIG_MTK_DRAMC)	+= mtk-dramc.o
> > diff --git a/drivers/memory/mediatek/mtk-dramc.c b/drivers/memory/mediatek/mtk-dramc.c
> > new file mode 100644
> > index 0000000..155b3b7
> > --- /dev/null
> > +++ b/drivers/memory/mediatek/mtk-dramc.c
> > @@ -0,0 +1,711 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2021 MediaTek Inc.
> > + */
> > +
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/device.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_device.h>
> > +#include <linux/printk.h>
> > +#include <linux/slab.h>
> > +#include <linux/io.h>
> > +#include <memory/mediatek/dramc.h>
> > +
> > +#define DRAMC_DRV_NAME	"mtk-dramc"
> > +
> > +struct mr_info_t {
> > +	unsigned int mr_index;
> > +	unsigned int mr_value;
> > +};
> > +
> > +/*
> > + * struct reg_ctrl_t - to describe the bits required in a register
> > + * @offset: register address offset from a base
> > + * @mask: bitmask of the target bits
> > + * @shift: starting bit of the target bits
> > + */
> > +struct reg_ctrl_t {
> > +	unsigned int offset;
> > +	unsigned int mask;
> > +	unsigned int shift;
> > +};
> > +
> > +struct fmeter_dev_t {
> > +	unsigned int crystal_freq;
> > +	unsigned int shu_of;
> > +	struct reg_ctrl_t shu_lv;
> > +	struct reg_ctrl_t pll_id;
> > +	struct reg_ctrl_t pll_md[2];
> > +	struct reg_ctrl_t sdmpcw[2];
> > +	struct reg_ctrl_t prediv[2];
> > +	struct reg_ctrl_t posdiv[2];
> > +	struct reg_ctrl_t ckdiv4[2];
> > +	struct reg_ctrl_t cldiv2[2];
> > +	struct reg_ctrl_t fbksel[2];
> > +	struct reg_ctrl_t dqopen[2];
> > +};
> > +
> > +struct mr4_dev_t {
> > +	struct reg_ctrl_t mr4_rg;
> > +};
> > +
> > +struct dramc_dev_t {
> > +	unsigned int dram_type;
> > +	unsigned int support_channel_cnt;
> > +	unsigned int channel_cnt;
> > +	unsigned int rank_cnt;
> > +	unsigned int mr_cnt;
> > +	unsigned int freq_cnt;
> > +	unsigned int *rank_size;
> > +	unsigned int *freq_step;
> > +	struct mr_info_t *mr_info_ptr;
> > +	void __iomem **dramc_chn_base_ao;
> > +	void __iomem **dramc_chn_base_nao;
> > +	void __iomem **ddrphy_chn_base_ao;
> > +	void *mr4_dev_ptr;
> > +	void *fmeter_dev_ptr;
> > +};
> > +
> > +enum DRAM_TYPE {
> > +	TYPE_NONE = 0,
> > +	TYPE_DDR1,
> > +	TYPE_LPDDR2,
> > +	TYPE_LPDDR3,
> > +	TYPE_PCDDR3,
> > +	TYPE_LPDDR4,
> > +	TYPE_LPDDR4X,
> > +	TYPE_LPDDR4P
> > +};
> 
> This doesn't appear to be used.

Yes, it is not currently in use, I'll remove it.

> > +
> > +static const struct fmeter_dev_t fmeter_v0_mt6779_t = {
> > +	.crystal_freq = 52,
> > +	.shu_of = 0x500,
> > +	.shu_lv = { .offset = 0x00e4, .mask = 0x00000006, .shift = 1 },
> > +	.pll_id = { .offset = 0x0510, .mask = 0x80000000, .shift = 31 },
> > +	.pll_md = {
> > +		{ .offset = 0x0d84, .mask = 0x00000100, .shift = 8 },
> > +		{ .offset = 0x0d84, .mask = 0x00000100, .shift = 8 },
> > +	},
> > +	.sdmpcw = {
> > +		{ .offset = 0x0d9c, .mask = 0xffff0000, .shift = 16 },
> > +		{ .offset = 0x0d94, .mask = 0xffff0000, .shift = 16 },
> > +	},
> > +	.prediv = {
> > +		{ .offset = 0x0da8, .mask = 0x000c0000, .shift = 18 },
> > +		{ .offset = 0x0da0, .mask = 0x000c0000, .shift = 18 },
> > +	},
> > +	.posdiv = {
> > +		{ .offset = 0x0da8, .mask = 0x00000007, .shift = 0 },
> > +		{ .offset = 0x0da0, .mask = 0x00000007, .shift = 0 },
> > +	},
> > +	.ckdiv4 = {
> > +		{ .offset = 0x0d18, .mask = 0x08000000, .shift = 27 },
> > +		{ .offset = 0x0d18, .mask = 0x08000000, .shift = 27 },
> > +	},
> > +	.cldiv2 = {
> > +		{ .offset = 0x0c38, .mask = 0x80000000, .shift = 31 },
> > +		{ .offset = 0x0c38, .mask = 0x80000000, .shift = 31 },
> > +	},
> > +};
> > +
> > +static const struct mr4_dev_t mr4_v1_mt6779_t = {
> > +	.mr4_rg = { .offset = 0x0090, .mask = 0x0000ffff, .shift = 0 },
> > +};
> > +
> > +struct mtk_dramc_compatible {
> > +	const struct fmeter_dev_t *fmeter;
> > +	const struct mr4_dev_t *mr4;
> > +};
> > +
> > +static const struct mtk_dramc_compatible mt6779_compat = {
> > +	.fmeter = &fmeter_v0_mt6779_t,
> > +	.mr4 = &mr4_v1_mt6779_t,
> > +};
> > +
> > +static const struct of_device_id mtk_dramc_of_ids[] = {
> > +	{ .compatible = "mediatek,mt6779-dramc", .data = &mt6779_compat },
> > +	{}
> > +};
> > +MODULE_DEVICE_TABLE(of, mtk_dramc_of_ids);
> > +
> > +/*
> > + * mtk_dramc_get_drvdata_by_ids - get the dramc driver data
> > + *
> > + * Return the dramc driver data
> > + */
> > +static struct dramc_dev_t *mtk_dramc_get_drvdata_by_ids(void)
> > +{
> > +	struct device_node *np;
> > +	struct platform_device *dramc_pdev;
> > +
> > +	np = of_find_matching_node_and_match(NULL, mtk_dramc_of_ids, NULL);
> > +	dramc_pdev = of_find_device_by_node(np);
> > +
> > +	if (!dramc_pdev)
> > +		return NULL;
> > +
> > +	return (struct dramc_dev_t *)platform_get_drvdata(dramc_pdev);
> > +}
> > +
> > +static ssize_t mr_show(struct device_driver *driver, char *buf)
> > +{
> > +	struct dramc_dev_t *dramc_dev_ptr;
> > +	struct mr_info_t *mr_info_ptr;
> > +	unsigned int i;
> > +	ssize_t ret;
> > +
> > +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> > +
> > +	if (!dramc_dev_ptr)
> > +		return 0;
> > +
> > +	mr_info_ptr = dramc_dev_ptr->mr_info_ptr;
> > +
> > +	for (ret = 0, i = 0; i < dramc_dev_ptr->mr_cnt; i++) {
> > +		ret += snprintf(buf + ret, PAGE_SIZE - ret,
> > +				"mr%d: 0x%x\n",
> 
> The file name is 'mr' and what you read should be just the value.

'mr' is the abbreviation of 'Mode Register', which defined by JEDEC.

There are 64 mode registers in LPDDR4X but not all of them are
meaningful, so we need a <index, value> pair to describe some of them.

Which mode registers will be brought to the kernel are depends on the
bootloader.

> 
> Also, sysfs files require documentation too.

Okay, I'll fill in the requirements.

> 
> > +				mr_info_ptr[i].mr_index,
> > +				mr_info_ptr[i].mr_value);
> > +		if (ret >= PAGE_SIZE)
> > +			return strlen(buf);
> > +	}
> > +
> > +	return strlen(buf);
> > +}
> > +
> > +static ssize_t mr4_show(struct device_driver *driver, char *buf)
> > +{
> > +	struct dramc_dev_t *dramc_dev_ptr;
> > +	unsigned int i;
> > +	ssize_t ret;
> > +
> > +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> > +
> > +	if (!dramc_dev_ptr)
> > +		return 0;
> > +
> > +	for (ret = 0, i = 0; i < dramc_dev_ptr->channel_cnt; i++) {
> > +		ret += snprintf(buf + ret, PAGE_SIZE - ret,
> > +				"mr4: ch%d 0x%x\n",
> 
> Same issues here.
> 
> > +				i, mtk_dramc_get_mr4(i));
> > +		if (ret >= PAGE_SIZE)
> > +			return strlen(buf);
> > +	}
> > +
> > +	return strlen(buf);
> > +}
> > +
> > +static ssize_t dram_data_rate_show(struct device_driver *driver, char *buf)
> > +{
> > +	return snprintf(buf, PAGE_SIZE, "DRAM data rate = %d\n",
> > +			mtk_dramc_get_data_rate());
> 
> And here.
> 
> > +}
> > +
> > +static DRIVER_ATTR_RO(mr);
> > +static DRIVER_ATTR_RO(mr4);
> > +static DRIVER_ATTR_RO(dram_data_rate);
> 
> A driver attr would be global. Shouldn't these be device attr's which 
> are per instance.

There is always only one DRAMC instance in the MediaTek SoC, 

> 
> You should also be using attribute groups.

Okay.

> 
> > +
> > +static int dramc_probe(struct platform_device *pdev)
> > +{
> > +	struct device_node *dramc_node = pdev->dev.of_node;
> > +	struct dramc_dev_t *dramc_dev_ptr;
> > +	const struct mtk_dramc_compatible *dev_comp;
> > +	struct resource *res;
> > +	unsigned int i, size;
> > +	int ret;
> > +
> > +	pr_info("%s: module probe.\n", __func__);
> > +
> > +	dramc_dev_ptr = devm_kmalloc(&pdev->dev,
> 
> Probably want to use devm_kzalloc instead.

It would be better, I'll update it.

> 
> > +				     sizeof(struct dramc_dev_t),
> > +				     GFP_KERNEL);
> > +
> > +	dev_comp =
> > +		(struct mtk_dramc_compatible *)
> > +			of_device_get_match_data(&pdev->dev);
> > +
> > +	if (!dramc_dev_ptr)
> > +		return -ENOMEM;
> > +
> > +	ret = of_property_read_u32(dramc_node,
> > +				   "mediatek,dram-type",
> > +				   &dramc_dev_ptr->dram_type);
> > +	if (ret) {
> > +		pr_info("%s: get dram_type fail\n", __func__);
> > +		return -EINVAL;
> > +	}
> > +
> > +	ret = of_property_read_u32(dramc_node,
> > +				   "mediatek,support-channel-cnt",
> > +				   &dramc_dev_ptr->support_channel_cnt);
> > +	if (ret) {
> > +		pr_info("%s: get support_channel_cnt fail\n", __func__);
> > +		return -EINVAL;
> > +	}
> > +
> > +	ret = of_property_read_u32(dramc_node,
> > +				   "mediatek,channel-cnt",
> > +				   &dramc_dev_ptr->channel_cnt);
> > +	if (ret) {
> > +		pr_info("%s: get channel_cnt fail\n", __func__);
> > +		return -EINVAL;
> > +	}
> > +
> > +	ret = of_property_read_u32(dramc_node,
> > +				   "mediatek,rank-cnt",
> > +				   &dramc_dev_ptr->rank_cnt);
> > +	if (ret) {
> > +		pr_info("%s: get rank_cnt fail\n", __func__);
> > +		return -EINVAL;
> > +	}
> > +
> > +	ret = of_property_read_u32(dramc_node,
> > +				   "mediatek,mr-cnt",
> > +				   &dramc_dev_ptr->mr_cnt);
> > +	if (ret) {
> > +		pr_info("%s: get mr_cnt fail\n", __func__);
> > +		return -EINVAL;
> > +	}
> > +
> > +	ret = of_property_read_u32(dramc_node,
> > +				   "mediatek,freq-cnt",
> > +				   &dramc_dev_ptr->freq_cnt);
> > +	if (ret) {
> > +		pr_info("%s: get freq_cnt fail\n", __func__);
> > +		return -EINVAL;
> > +	}
> > +
> > +	dramc_dev_ptr->mr4_dev_ptr = (void *)dev_comp->mr4;
> > +
> > +	pr_info("%s: %s(%d),%s(%d),%s(%d),%s(%d),%s(%d),%s(%d),%s(%s)\n",
> > +		__func__,
> > +		"dram_type", dramc_dev_ptr->dram_type,
> > +		"support_channel_cnt", dramc_dev_ptr->support_channel_cnt,
> > +		"channel_cnt", dramc_dev_ptr->channel_cnt,
> > +		"rank_cnt", dramc_dev_ptr->rank_cnt,
> > +		"mr_cnt", dramc_dev_ptr->mr_cnt,
> > +		"freq_cnt", dramc_dev_ptr->freq_cnt,
> > +		"mr4", (dramc_dev_ptr->mr4_dev_ptr) ? "true" : "false");
> 
> Why do you need this? It's already in DT which is readable from 
> userspace.

To make the driver status easy to see when probing it.

> 
> > +
> > +	size = sizeof(unsigned int) * dramc_dev_ptr->rank_cnt;
> > +	dramc_dev_ptr->rank_size = devm_kmalloc(&pdev->dev, size, GFP_KERNEL);
> > +	if (!(dramc_dev_ptr->rank_size))
> > +		return -ENOMEM;
> > +	ret = of_property_read_u32_array(dramc_node,
> > +					 "mediatek,rank-size",
> > +					 dramc_dev_ptr->rank_size,
> > +					 dramc_dev_ptr->rank_cnt);
> > +	if (ret) {
> > +		pr_info("%s: get rank_size fail\n", __func__);
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (dramc_dev_ptr->mr_cnt) {
> > +		size = sizeof(struct mr_info_t) * dramc_dev_ptr->mr_cnt;
> > +		dramc_dev_ptr->mr_info_ptr = devm_kmalloc(&pdev->dev,
> > +							  size,
> > +							  GFP_KERNEL);
> > +		if (!(dramc_dev_ptr->mr_info_ptr))
> > +			return -ENOMEM;
> > +		ret =
> > +		    of_property_read_u32_array(dramc_node,
> > +					       "mediatek,mr",
> > +					       (unsigned int *)dramc_dev_ptr->mr_info_ptr,
> > +					       size >> 2);
> > +		if (ret) {
> > +			pr_info("%s: get mr_info fail\n", __func__);
> > +			return -EINVAL;
> > +		}
> > +		for (i = 0; i < dramc_dev_ptr->mr_cnt; i++)
> > +			pr_info("%s: mr%d(%x)\n", __func__,
> > +				dramc_dev_ptr->mr_info_ptr[i].mr_index,
> > +				dramc_dev_ptr->mr_info_ptr[i].mr_value);
> > +	}
> > +
> > +	if (dramc_dev_ptr->freq_cnt) {
> > +		size = sizeof(unsigned int) * dramc_dev_ptr->freq_cnt * 2;
> > +		dramc_dev_ptr->freq_step =
> > +			devm_kmalloc(&pdev->dev, size, GFP_KERNEL);
> > +		if (!(dramc_dev_ptr->freq_step))
> > +			return -ENOMEM;
> > +		ret = of_property_read_u32_array(dramc_node,
> > +						 "mediatek,freq-step",
> > +						 dramc_dev_ptr->freq_step,
> > +						 dramc_dev_ptr->freq_cnt * 2);
> > +		if (ret) {
> > +			pr_info("%s: get freq_step fail\n", __func__);
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	size = sizeof(phys_addr_t) * dramc_dev_ptr->support_channel_cnt;
> > +	dramc_dev_ptr->dramc_chn_base_ao = devm_kmalloc(&pdev->dev,
> > +							size, GFP_KERNEL);
> > +	if (!(dramc_dev_ptr->dramc_chn_base_ao))
> > +		return -ENOMEM;
> > +	dramc_dev_ptr->dramc_chn_base_nao = devm_kmalloc(&pdev->dev,
> > +							 size, GFP_KERNEL);
> > +	if (!(dramc_dev_ptr->dramc_chn_base_nao))
> > +		return -ENOMEM;
> > +	dramc_dev_ptr->ddrphy_chn_base_ao = devm_kmalloc(&pdev->dev,
> > +							 size, GFP_KERNEL);
> > +	if (!(dramc_dev_ptr->ddrphy_chn_base_ao))
> > +		return -ENOMEM;
> > +
> > +	for (i = 0; i < dramc_dev_ptr->support_channel_cnt; i++) {
> > +		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
> > +		dramc_dev_ptr->dramc_chn_base_ao[i] =
> > +			devm_ioremap_resource(&pdev->dev, res);
> > +		if (IS_ERR(dramc_dev_ptr->dramc_chn_base_ao[i])) {
> > +			pr_info("%s: unable to map ch%d DRAMC AO base\n",
> > +				__func__, i);
> > +			return -EINVAL;
> > +		}
> > +
> > +		res = platform_get_resource(pdev, IORESOURCE_MEM,
> > +					    i + dramc_dev_ptr->support_channel_cnt);
> > +		dramc_dev_ptr->dramc_chn_base_nao[i] =
> > +			devm_ioremap_resource(&pdev->dev, res);
> > +		if (IS_ERR(dramc_dev_ptr->dramc_chn_base_nao[i])) {
> > +			pr_info("%s: unable to map ch%d DRAMC NAO base\n",
> > +				__func__, i);
> > +			return -EINVAL;
> > +		}
> > +
> > +		res = platform_get_resource(pdev, IORESOURCE_MEM,
> > +					    i + dramc_dev_ptr->support_channel_cnt * 2);
> > +		dramc_dev_ptr->ddrphy_chn_base_ao[i] =
> > +			devm_ioremap_resource(&pdev->dev, res);
> > +		if (IS_ERR(dramc_dev_ptr->ddrphy_chn_base_ao[i])) {
> > +			pr_info("%s: unable to map ch%d DDRPHY AO base\n",
> > +				__func__, i);
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	dramc_dev_ptr->fmeter_dev_ptr = (void *)dev_comp->fmeter;
> > +
> > +	ret = driver_create_file(pdev->dev.driver,
> > +				 &driver_attr_dram_data_rate);
> > +	if (ret) {
> > +		pr_info("%s: fail to create dram_data_rate sysfs\n", __func__);
> > +		return ret;
> > +	}
> > +
> > +	ret = driver_create_file(pdev->dev.driver,
> > +				 &driver_attr_mr);
> > +	if (ret) {
> > +		pr_info("%s: fail to create mr sysfs\n", __func__);
> > +		return ret;
> > +	}
> > +
> > +	if (dramc_dev_ptr->mr4_dev_ptr) {
> > +		ret = driver_create_file(pdev->dev.driver,
> > +					 &driver_attr_mr4);
> > +		if (ret) {
> > +			pr_info("%s: fail to create mr4 sysfs\n", __func__);
> > +			return ret;
> > +		}
> > +	}
> > +
> > +	platform_set_drvdata(pdev, dramc_dev_ptr);
> > +	pr_info("%s: DRAM data type = %d\n", __func__,
> > +		mtk_dramc_get_ddr_type());
> > +
> > +	pr_info("%s: DRAM data clock rate = %d\n", __func__,
> > +		mtk_dramc_get_data_rate());
> > +
> > +	return ret;
> > +}
> > +
> > +/*
> > + * mtk_dramc_get_steps_freq - get the data clock rate of target DVFS step
> > + * @step: the step index of DVFS
> > + *
> > + * Return the DRAM spec data clock rate (MHz)
> > + */
> > +int mtk_dramc_get_steps_freq(unsigned int step)
> > +{
> > +	struct dramc_dev_t *dramc_dev_ptr;
> > +
> > +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> > +
> > +	if (!dramc_dev_ptr)
> > +		return -ENODEV;
> > +
> > +	if (step < dramc_dev_ptr->freq_cnt)
> > +		return dramc_dev_ptr->freq_step[step * 2 + 1];
> > +
> > +	return -EINVAL;
> > +}
> > +EXPORT_SYMBOL(mtk_dramc_get_steps_freq);
> > +
> > +/*
> > + * decode_freq - decode the spec data clock rate
> > + * @vco_freq: real data clock rate
> > + *
> > + * Return the DRAM spec data clock rate (MHz)
> > + */
> > +static unsigned int decode_freq(unsigned int vco_freq)
> > +{
> > +	int i;
> > +	struct dramc_dev_t *dramc_dev_ptr;
> > +
> > +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> > +
> > +	if (!dramc_dev_ptr)
> > +		return 0;
> > +
> > +	for (i = 0; i < dramc_dev_ptr->freq_cnt * 2; i += 2)
> > +		if (vco_freq == dramc_dev_ptr->freq_step[i])
> > +			return dramc_dev_ptr->freq_step[i + 1];
> > +
> > +	return vco_freq;
> > +}
> > +
> > +static unsigned int fmeter_v0(struct dramc_dev_t *dramc_dev_ptr)
> > +{
> > +	struct fmeter_dev_t *fmeter_dev_ptr =
> > +		(struct fmeter_dev_t *)dramc_dev_ptr->fmeter_dev_ptr;
> > +	unsigned int shu_lv_val;
> > +	unsigned int pll_id_val;
> > +	unsigned int pll_md_val;
> > +	unsigned int sdmpcw_val;
> > +	unsigned int prediv_val;
> > +	unsigned int posdiv_val;
> > +	unsigned int ckdiv4_val;
> > +	unsigned int cldiv2_val;
> > +	unsigned int offset;
> > +	unsigned int vco_freq;
> > +
> > +	shu_lv_val = (readl(dramc_dev_ptr->dramc_chn_base_ao[0] +
> > +		fmeter_dev_ptr->shu_lv.offset) &
> > +		fmeter_dev_ptr->shu_lv.mask) >>
> > +		fmeter_dev_ptr->shu_lv.shift;
> > +
> > +	pll_id_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] +
> > +		fmeter_dev_ptr->pll_id.offset) &
> > +		fmeter_dev_ptr->pll_id.mask) >>
> > +		fmeter_dev_ptr->pll_id.shift;
> > +
> > +	offset = fmeter_dev_ptr->pll_md[pll_id_val].offset +
> > +		fmeter_dev_ptr->shu_of * shu_lv_val;
> > +	pll_md_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> > +		fmeter_dev_ptr->pll_md[pll_id_val].mask) >>
> > +		fmeter_dev_ptr->pll_md[pll_id_val].shift;
> > +
> > +	offset = fmeter_dev_ptr->sdmpcw[pll_id_val].offset +
> > +		fmeter_dev_ptr->shu_of * shu_lv_val;
> > +	sdmpcw_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> > +		fmeter_dev_ptr->sdmpcw[pll_id_val].mask) >>
> > +		fmeter_dev_ptr->sdmpcw[pll_id_val].shift;
> > +
> > +	offset = fmeter_dev_ptr->prediv[pll_id_val].offset +
> > +		fmeter_dev_ptr->shu_of * shu_lv_val;
> > +	prediv_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> > +		fmeter_dev_ptr->prediv[pll_id_val].mask) >>
> > +		fmeter_dev_ptr->prediv[pll_id_val].shift;
> > +
> > +	offset = fmeter_dev_ptr->posdiv[pll_id_val].offset +
> > +		fmeter_dev_ptr->shu_of * shu_lv_val;
> > +	posdiv_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> > +		fmeter_dev_ptr->posdiv[pll_id_val].mask) >>
> > +		fmeter_dev_ptr->posdiv[pll_id_val].shift;
> > +
> > +	offset = fmeter_dev_ptr->ckdiv4[pll_id_val].offset +
> > +		fmeter_dev_ptr->shu_of * shu_lv_val;
> > +	ckdiv4_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> > +		fmeter_dev_ptr->ckdiv4[pll_id_val].mask) >>
> > +		fmeter_dev_ptr->ckdiv4[pll_id_val].shift;
> > +
> > +	offset = fmeter_dev_ptr->cldiv2[pll_id_val].offset +
> > +		fmeter_dev_ptr->shu_of * shu_lv_val;
> > +	cldiv2_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
> > +		fmeter_dev_ptr->cldiv2[pll_id_val].mask) >>
> > +		fmeter_dev_ptr->cldiv2[pll_id_val].shift;
> > +
> > +	vco_freq = ((fmeter_dev_ptr->crystal_freq >> prediv_val) *
> > +		(sdmpcw_val >> 8)) >>
> > +		posdiv_val >> ckdiv4_val >> pll_md_val >> cldiv2_val;
> > +
> > +	return decode_freq(vco_freq);
> > +}
> > +
> > +/*
> > + * mtk_dramc_get_data_rate - calculate DRAM data rate
> > + *
> > + * Return DRAM data rate (MB/s)
> > + */
> > +unsigned int mtk_dramc_get_data_rate(void)
> > +{
> > +	struct dramc_dev_t *dramc_dev_ptr;
> > +	struct fmeter_dev_t *fmeter_dev_ptr;
> > +
> > +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> > +
> > +	if (!dramc_dev_ptr)
> > +		return 0;
> > +
> > +	fmeter_dev_ptr = (struct fmeter_dev_t *)dramc_dev_ptr->fmeter_dev_ptr;
> > +	if (!fmeter_dev_ptr)
> > +		return 0;
> > +
> > +	return fmeter_v0(dramc_dev_ptr);
> > +}
> > +EXPORT_SYMBOL(mtk_dramc_get_data_rate);
> > +
> > +static unsigned int mr4_v1(struct dramc_dev_t *dramc_dev_ptr, unsigned int ch)
> > +{
> > +	struct mr4_dev_t *mr4_dev_ptr =
> > +		(struct mr4_dev_t *)dramc_dev_ptr->mr4_dev_ptr;
> > +
> > +	return (readl(dramc_dev_ptr->dramc_chn_base_nao[ch] +
> > +		mr4_dev_ptr->mr4_rg.offset) & mr4_dev_ptr->mr4_rg.mask) >>
> > +		mr4_dev_ptr->mr4_rg.shift;
> > +}
> > +
> > +/*
> > + * mtk_dramc_get_mr4 - get the DRAM MR4 value of specific DRAM channel
> > + * @ch:	the channel index
> > + *
> > + * Return the MR4 value
> > + */
> > +unsigned int mtk_dramc_get_mr4(unsigned int ch)
> > +{
> > +	struct dramc_dev_t *dramc_dev_ptr;
> > +	struct mr4_dev_t *mr4_dev_ptr;
> > +
> > +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> > +
> > +	if (!dramc_dev_ptr)
> > +		return 0;
> > +
> > +	mr4_dev_ptr = (struct mr4_dev_t *)dramc_dev_ptr->mr4_dev_ptr;
> > +	if (!mr4_dev_ptr)
> > +		return 0;
> > +
> > +	if (ch >= dramc_dev_ptr->channel_cnt)
> > +		return 0;
> > +
> > +	return mr4_v1(dramc_dev_ptr, ch);
> > +}
> > +EXPORT_SYMBOL(mtk_dramc_get_mr4);
> > +
> > +/*
> > + * mtk_dramc_get_ddr_type - get DRAM type
> > + *
> > + * Return the DRAM type
> > + */
> > +unsigned int mtk_dramc_get_ddr_type(void)
> > +{
> > +	struct dramc_dev_t *dramc_dev_ptr;
> > +
> > +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> > +
> > +	if (!dramc_dev_ptr)
> > +		return 0;
> > +
> > +	return dramc_dev_ptr->dram_type;
> > +}
> > +EXPORT_SYMBOL(mtk_dramc_get_ddr_type);
> > +
> > +/*
> > + * mtk_dramc_get_channel_count - get DRAM channel count
> > + *
> > + * Return the DRAM channel count
> > + */
> > +unsigned int mtk_dramc_get_channel_count(void)
> > +{
> > +	struct dramc_dev_t *dramc_dev_ptr;
> > +
> > +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> > +
> > +	if (!dramc_dev_ptr)
> > +		return 0;
> > +
> > +	return dramc_dev_ptr->channel_cnt;
> > +}
> > +EXPORT_SYMBOL(mtk_dramc_get_channel_count);
> > +
> > +/*
> > + * mtk_dramc_get_rank_count - get DRAM rank count
> > + *
> > + * Return the DRAM rank count
> > + */
> > +unsigned int mtk_dramc_get_rank_count(void)
> > +{
> > +	struct dramc_dev_t *dramc_dev_ptr;
> > +
> > +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> > +
> > +	if (!dramc_dev_ptr)
> > +		return 0;
> > +
> > +	return dramc_dev_ptr->rank_cnt;
> > +}
> > +EXPORT_SYMBOL(mtk_dramc_get_rank_count);
> > +
> > +/*
> > + * mtk_dramc_get_rank_size - get size of DRAM rank
> > + *
> > + * Return the size of specific DRAM rank
> > + */
> > +unsigned int mtk_dramc_get_rank_size(unsigned int rank)
> > +{
> > +	struct dramc_dev_t *dramc_dev_ptr;
> > +
> > +	dramc_dev_ptr = mtk_dramc_get_drvdata_by_ids();
> > +
> > +	if (!dramc_dev_ptr)
> > +		return 0;
> > +
> > +	if (rank < dramc_dev_ptr->rank_cnt)
> > +		return dramc_dev_ptr->rank_size[rank];
> > +	else
> > +		return 0;
> > +}
> > +EXPORT_SYMBOL(mtk_dramc_get_rank_size);
> > +
> > +static int dramc_remove(struct platform_device *pdev)
> > +{
> > +	return 0;
> > +}
> > +
> > +static struct platform_driver dramc_drv = {
> > +	.probe = dramc_probe,
> > +	.remove = dramc_remove,
> > +	.driver = {
> > +		.name = DRAMC_DRV_NAME,
> > +		.owner = THIS_MODULE,
> > +		.of_match_table = mtk_dramc_of_ids,
> > +	},
> > +};
> > +
> > +static int __init dramc_drv_init(void)
> > +{
> > +	int ret;
> > +
> > +	ret = platform_driver_register(&dramc_drv);
> > +	if (ret) {
> > +		pr_info("%s: init fail, ret 0x%x\n", __func__, ret);
> > +		return ret;
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +static void __exit dramc_drv_exit(void)
> > +{
> > +	platform_driver_unregister(&dramc_drv);
> > +}
> > +
> > +module_init(dramc_drv_init);
> > +module_exit(dramc_drv_exit);
> > +
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_DESCRIPTION("MediaTek DRAMC Driver");
> > +MODULE_AUTHOR("Po-Kai Chi <pk.chi@mediatek.com>");
> > diff --git a/include/memory/mediatek/dramc.h b/include/memory/mediatek/dramc.h
> > new file mode 100644
> > index 0000000..c8d200f
> > --- /dev/null
> > +++ b/include/memory/mediatek/dramc.h
> > @@ -0,0 +1,18 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (c) 2021 MediaTek Inc.
> > + */
> > +
> > +#ifndef __DRAMC_H__
> > +#define __DRAMC_H__
> > +
> > +int mtk_dramc_get_steps_freq(unsigned int step);
> > +unsigned int mtk_dramc_get_ddr_type(void);
> > +unsigned int mtk_dramc_get_data_rate(void);
> > +unsigned int mtk_dramc_get_mr4(unsigned int ch);
> > +unsigned int mtk_dramc_get_channel_count(void);
> > +unsigned int mtk_dramc_get_rank_count(void);
> > +unsigned int mtk_dramc_get_rank_size(unsigned int rk);
> > +
> > +#endif /* __DRAMC_H__ */
> > +
> > -- 
> > 1.7.9.5
> > 

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v2] memory: mediatek: add DRAM controller driver
  2021-04-16  9:32 ` Po-Kai Chi
@ 2021-05-04 13:56   ` Krzysztof Kozlowski
  -1 siblings, 0 replies; 22+ messages in thread
From: Krzysztof Kozlowski @ 2021-05-04 13:56 UTC (permalink / raw)
  To: Po-Kai Chi
  Cc: Matthias Brugger, linux-kernel, linux-mediatek, devicetree,
	wsd_upstream, CC Hwang

On Fri, 16 Apr 2021 at 06:30, Po-Kai Chi <pk.chi@mediatek.com> wrote:
>
> These patch series introduces the MediaTek DRAM controller driver (DRAMC)
> on MT6779 SoC, and enables to be built as a module by default for the
> ARM64 builds.
>
> MediaTek DRAMC driver provides cross-platform features as below:
>   - API provided to other kernel modules for querying DRAM type,
>     rank count, rank size, channel count and mode register settings.
>   - Sysfs interface used to pass DRAM mode register settings and current
>     DRAM data rate to user-space for MediaTek ecosystem.
>
> The API user includes MediaTek External Memory Interface (EMI) and
> DVFS Resource Control (DVFSRC), which will be sent to mainline later.
>
> Changes since v1:
> - add prefix to vendor properties in device tree, dt-binding and drivers
> - fix dt-binding check fail
>
> Po-Kai Chi (4):
>   dt-bindings: memory: Add binding for MediaTek DRAM Controller
>   memory: mediatek: add DRAM controller driver
>   arm64: dts: add DRAMC node for MT6779
>   arm64: defconfig: Enable MediaTek DRAMC common driver
>
>  .../memory-controllers/mediatek,dramc.yaml    | 162 ++++
>  arch/arm64/boot/dts/mediatek/mt6779-evb.dts   |   9 +
>  arch/arm64/boot/dts/mediatek/mt6779.dtsi      |  18 +
>  arch/arm64/configs/defconfig                  |   1 +
>  drivers/memory/Kconfig                        |   1 +
>  drivers/memory/Makefile                       |   1 +
>  drivers/memory/mediatek/Kconfig               |   9 +
>  drivers/memory/mediatek/Makefile              |   3 +
>  drivers/memory/mediatek/mtk-dramc.c           | 711 ++++++++++++++++++
>  include/memory/mediatek/dramc.h               |  18 +

I found this patchset by coincidence as you never cc-ed me. Please use
scripts/get_maintainer.pl to get the list of people you need to Cc.

All this should go via memory drivers tree.


Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v2] memory: mediatek: add DRAM controller driver
@ 2021-05-04 13:56   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 22+ messages in thread
From: Krzysztof Kozlowski @ 2021-05-04 13:56 UTC (permalink / raw)
  To: Po-Kai Chi
  Cc: Matthias Brugger, linux-kernel, linux-mediatek, devicetree,
	wsd_upstream, CC Hwang

On Fri, 16 Apr 2021 at 06:30, Po-Kai Chi <pk.chi@mediatek.com> wrote:
>
> These patch series introduces the MediaTek DRAM controller driver (DRAMC)
> on MT6779 SoC, and enables to be built as a module by default for the
> ARM64 builds.
>
> MediaTek DRAMC driver provides cross-platform features as below:
>   - API provided to other kernel modules for querying DRAM type,
>     rank count, rank size, channel count and mode register settings.
>   - Sysfs interface used to pass DRAM mode register settings and current
>     DRAM data rate to user-space for MediaTek ecosystem.
>
> The API user includes MediaTek External Memory Interface (EMI) and
> DVFS Resource Control (DVFSRC), which will be sent to mainline later.
>
> Changes since v1:
> - add prefix to vendor properties in device tree, dt-binding and drivers
> - fix dt-binding check fail
>
> Po-Kai Chi (4):
>   dt-bindings: memory: Add binding for MediaTek DRAM Controller
>   memory: mediatek: add DRAM controller driver
>   arm64: dts: add DRAMC node for MT6779
>   arm64: defconfig: Enable MediaTek DRAMC common driver
>
>  .../memory-controllers/mediatek,dramc.yaml    | 162 ++++
>  arch/arm64/boot/dts/mediatek/mt6779-evb.dts   |   9 +
>  arch/arm64/boot/dts/mediatek/mt6779.dtsi      |  18 +
>  arch/arm64/configs/defconfig                  |   1 +
>  drivers/memory/Kconfig                        |   1 +
>  drivers/memory/Makefile                       |   1 +
>  drivers/memory/mediatek/Kconfig               |   9 +
>  drivers/memory/mediatek/Makefile              |   3 +
>  drivers/memory/mediatek/mtk-dramc.c           | 711 ++++++++++++++++++
>  include/memory/mediatek/dramc.h               |  18 +

I found this patchset by coincidence as you never cc-ed me. Please use
scripts/get_maintainer.pl to get the list of people you need to Cc.

All this should go via memory drivers tree.


Best regards,
Krzysztof

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v2 1/4] dt-bindings: memory: Add binding for MediaTek DRAM Controller
  2021-04-16  9:32   ` Po-Kai Chi
@ 2021-05-04 14:00     ` Krzysztof Kozlowski
  -1 siblings, 0 replies; 22+ messages in thread
From: Krzysztof Kozlowski @ 2021-05-04 14:00 UTC (permalink / raw)
  To: Po-Kai Chi, Matthias Brugger
  Cc: linux-kernel, linux-mediatek, devicetree, wsd_upstream, CC Hwang

On 16/04/2021 05:32, Po-Kai Chi wrote:
> This patch adds the documentation of the device-tree binding for
> MediaTek DRAM Controller.
> 
> Signed-off-by: Po-Kai Chi <pk.chi@mediatek.com>
> ---
>  .../memory-controllers/mediatek,dramc.yaml         |  162 ++++++++++++++++++++
>  1 file changed, 162 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/memory-controllers/mediatek,dramc.yaml
> 
> diff --git a/Documentation/devicetree/bindings/memory-controllers/mediatek,dramc.yaml b/Documentation/devicetree/bindings/memory-controllers/mediatek,dramc.yaml
> new file mode 100644
> index 0000000..18f77c2
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/memory-controllers/mediatek,dramc.yaml
> @@ -0,0 +1,162 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +# Copyright (c) 2021 MediaTek Inc.
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/memory-controllers/mediatek,dramc.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: MediaTek DRAM Controller
> +
> +maintainers:
> +  - Po-Kai Chi <pk.chi@mediatek.com>
> +
> +description: |
> +  MediaTek DRAM controller (DRAMC) provides an interface to query information
> +  about DRAM which collected from bootloader and device tree.
> +  This is mainly used by MediaTek Extended Memory Interface (EMI) and DVFS Resource
> +  Control (DVFSRC).
> +
> +properties:
> +  compatible:
> +    items:
> +      - enum:

Not an enum.

> +          - mediatek,mt6779-dramc

NAK.

You missed to Cc people who should review this (e.g. Rob). Please start
using get_maintainers script.


Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v2 1/4] dt-bindings: memory: Add binding for MediaTek DRAM Controller
@ 2021-05-04 14:00     ` Krzysztof Kozlowski
  0 siblings, 0 replies; 22+ messages in thread
From: Krzysztof Kozlowski @ 2021-05-04 14:00 UTC (permalink / raw)
  To: Po-Kai Chi, Matthias Brugger
  Cc: linux-kernel, linux-mediatek, devicetree, wsd_upstream, CC Hwang

On 16/04/2021 05:32, Po-Kai Chi wrote:
> This patch adds the documentation of the device-tree binding for
> MediaTek DRAM Controller.
> 
> Signed-off-by: Po-Kai Chi <pk.chi@mediatek.com>
> ---
>  .../memory-controllers/mediatek,dramc.yaml         |  162 ++++++++++++++++++++
>  1 file changed, 162 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/memory-controllers/mediatek,dramc.yaml
> 
> diff --git a/Documentation/devicetree/bindings/memory-controllers/mediatek,dramc.yaml b/Documentation/devicetree/bindings/memory-controllers/mediatek,dramc.yaml
> new file mode 100644
> index 0000000..18f77c2
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/memory-controllers/mediatek,dramc.yaml
> @@ -0,0 +1,162 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +# Copyright (c) 2021 MediaTek Inc.
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/memory-controllers/mediatek,dramc.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: MediaTek DRAM Controller
> +
> +maintainers:
> +  - Po-Kai Chi <pk.chi@mediatek.com>
> +
> +description: |
> +  MediaTek DRAM controller (DRAMC) provides an interface to query information
> +  about DRAM which collected from bootloader and device tree.
> +  This is mainly used by MediaTek Extended Memory Interface (EMI) and DVFS Resource
> +  Control (DVFSRC).
> +
> +properties:
> +  compatible:
> +    items:
> +      - enum:

Not an enum.

> +          - mediatek,mt6779-dramc

NAK.

You missed to Cc people who should review this (e.g. Rob). Please start
using get_maintainers script.


Best regards,
Krzysztof

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v2 2/4] memory: mediatek: add DRAM controller driver
  2021-04-16  9:32   ` Po-Kai Chi
@ 2021-05-04 14:05     ` Krzysztof Kozlowski
  -1 siblings, 0 replies; 22+ messages in thread
From: Krzysztof Kozlowski @ 2021-05-04 14:05 UTC (permalink / raw)
  To: Po-Kai Chi, Matthias Brugger
  Cc: linux-kernel, linux-mediatek, devicetree, wsd_upstream, CC Hwang

On 16/04/2021 05:32, Po-Kai Chi wrote:
> MediaTek DRAM controller (DRAMC) driver provides cross-platform features
> as below:
> 
> 1. provide APIs for low power feature queries
> 2. create sysfs to pass the DRAM information to user-space
> 
> Signed-off-by: Po-Kai Chi <pk.chi@mediatek.com>
> ---
>  drivers/memory/Kconfig              |    1 +
>  drivers/memory/Makefile             |    1 +
>  drivers/memory/mediatek/Kconfig     |    9 +
>  drivers/memory/mediatek/Makefile    |    3 +
>  drivers/memory/mediatek/mtk-dramc.c |  711 +++++++++++++++++++++++++++++++++++
>  include/memory/mediatek/dramc.h     |   18 +
>  6 files changed, 743 insertions(+)
>  create mode 100644 drivers/memory/mediatek/Kconfig
>  create mode 100644 drivers/memory/mediatek/Makefile
>  create mode 100644 drivers/memory/mediatek/mtk-dramc.c
>  create mode 100644 include/memory/mediatek/dramc.h
> 
> diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
> index 72c0df1..056e906 100644
> --- a/drivers/memory/Kconfig
> +++ b/drivers/memory/Kconfig
> @@ -225,6 +225,7 @@ config STM32_FMC2_EBI
>  	  devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on
>  	  SOCs containing the FMC2 External Bus Interface.
>  
> +source "drivers/memory/mediatek/Kconfig"

Please first group existing Mediatek driver there. It's messy.

>  source "drivers/memory/samsung/Kconfig"
>  source "drivers/memory/tegra/Kconfig"
>  
> diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
> index bc7663e..cd4f8cf 100644
> --- a/drivers/memory/Makefile
> +++ b/drivers/memory/Makefile
> @@ -25,6 +25,7 @@ obj-$(CONFIG_PL353_SMC)		+= pl353-smc.o
>  obj-$(CONFIG_RENESAS_RPCIF)	+= renesas-rpc-if.o
>  obj-$(CONFIG_STM32_FMC2_EBI)	+= stm32-fmc2-ebi.o
>  
> +obj-$(CONFIG_MTK_DRAMC)		+= mediatek/
>  obj-$(CONFIG_SAMSUNG_MC)	+= samsung/
>  obj-$(CONFIG_TEGRA_MC)		+= tegra/
>  obj-$(CONFIG_TI_EMIF_SRAM)	+= ti-emif-sram.o
> diff --git a/drivers/memory/mediatek/Kconfig b/drivers/memory/mediatek/Kconfig
> new file mode 100644
> index 0000000..a1618b0
> --- /dev/null
> +++ b/drivers/memory/mediatek/Kconfig
> @@ -0,0 +1,9 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +config MTK_DRAMC
> +	tristate "MediaTek DRAMC driver"
> +	help
> +	  This selects the MediaTek(R) DRAMC driver.
> +	  Provide the API for DRAMC low power scenario, and the interface
> +	  for reporting DRAM information, e.g. DRAM mode register (MR) for
> +	  DRAM vendor ID, temperature, and density.
> diff --git a/drivers/memory/mediatek/Makefile b/drivers/memory/mediatek/Makefile
> new file mode 100644
> index 0000000..632be48
> --- /dev/null
> +++ b/drivers/memory/mediatek/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +obj-$(CONFIG_MTK_DRAMC)	+= mtk-dramc.o
> diff --git a/drivers/memory/mediatek/mtk-dramc.c b/drivers/memory/mediatek/mtk-dramc.c
> new file mode 100644
> index 0000000..155b3b7
> --- /dev/null
> +++ b/drivers/memory/mediatek/mtk-dramc.c
> @@ -0,0 +1,711 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021 MediaTek Inc.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/printk.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <memory/mediatek/dramc.h>
> +
> +#define DRAMC_DRV_NAME	"mtk-dramc"

What does this define bring? What's the benefit?

> +
> +struct mr_info_t {
> +	unsigned int mr_index;
> +	unsigned int mr_value;
> +};
> +
> +/*

(...)

> +
> +static struct platform_driver dramc_drv = {
> +	.probe = dramc_probe,
> +	.remove = dramc_remove,
> +	.driver = {
> +		.name = DRAMC_DRV_NAME,
> +		.owner = THIS_MODULE,

NAK, this is so old mistake... No point to review - run Smatch, sparse,
checkpatch and coccinelle. Fix all the errors and then resubmit.

> +		.of_match_table = mtk_dramc_of_ids,
> +	},
> +};
> +
> +static int __init dramc_drv_init(void)
> +{
> +	int ret;
> +
> +	ret = platform_driver_register(&dramc_drv);
> +	if (ret) {
> +		pr_info("%s: init fail, ret 0x%x\n", __func__, ret);
> +		return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static void __exit dramc_drv_exit(void)
> +{
> +	platform_driver_unregister(&dramc_drv);
> +}
> +
> +module_init(dramc_drv_init);
> +module_exit(dramc_drv_exit);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("MediaTek DRAMC Driver");
> +MODULE_AUTHOR("Po-Kai Chi <pk.chi@mediatek.com>");
> diff --git a/include/memory/mediatek/dramc.h b/include/memory/mediatek/dramc.h
> new file mode 100644
> index 0000000..c8d200f
> --- /dev/null
> +++ b/include/memory/mediatek/dramc.h
> @@ -0,0 +1,18 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2021 MediaTek Inc.
> + */
> +
> +#ifndef __DRAMC_H__

Extend the header guard - MEMORY_MEDIATEK_DRAMC

> +#define __DRAMC_H__



Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v2 2/4] memory: mediatek: add DRAM controller driver
@ 2021-05-04 14:05     ` Krzysztof Kozlowski
  0 siblings, 0 replies; 22+ messages in thread
From: Krzysztof Kozlowski @ 2021-05-04 14:05 UTC (permalink / raw)
  To: Po-Kai Chi, Matthias Brugger
  Cc: linux-kernel, linux-mediatek, devicetree, wsd_upstream, CC Hwang

On 16/04/2021 05:32, Po-Kai Chi wrote:
> MediaTek DRAM controller (DRAMC) driver provides cross-platform features
> as below:
> 
> 1. provide APIs for low power feature queries
> 2. create sysfs to pass the DRAM information to user-space
> 
> Signed-off-by: Po-Kai Chi <pk.chi@mediatek.com>
> ---
>  drivers/memory/Kconfig              |    1 +
>  drivers/memory/Makefile             |    1 +
>  drivers/memory/mediatek/Kconfig     |    9 +
>  drivers/memory/mediatek/Makefile    |    3 +
>  drivers/memory/mediatek/mtk-dramc.c |  711 +++++++++++++++++++++++++++++++++++
>  include/memory/mediatek/dramc.h     |   18 +
>  6 files changed, 743 insertions(+)
>  create mode 100644 drivers/memory/mediatek/Kconfig
>  create mode 100644 drivers/memory/mediatek/Makefile
>  create mode 100644 drivers/memory/mediatek/mtk-dramc.c
>  create mode 100644 include/memory/mediatek/dramc.h
> 
> diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
> index 72c0df1..056e906 100644
> --- a/drivers/memory/Kconfig
> +++ b/drivers/memory/Kconfig
> @@ -225,6 +225,7 @@ config STM32_FMC2_EBI
>  	  devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on
>  	  SOCs containing the FMC2 External Bus Interface.
>  
> +source "drivers/memory/mediatek/Kconfig"

Please first group existing Mediatek driver there. It's messy.

>  source "drivers/memory/samsung/Kconfig"
>  source "drivers/memory/tegra/Kconfig"
>  
> diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
> index bc7663e..cd4f8cf 100644
> --- a/drivers/memory/Makefile
> +++ b/drivers/memory/Makefile
> @@ -25,6 +25,7 @@ obj-$(CONFIG_PL353_SMC)		+= pl353-smc.o
>  obj-$(CONFIG_RENESAS_RPCIF)	+= renesas-rpc-if.o
>  obj-$(CONFIG_STM32_FMC2_EBI)	+= stm32-fmc2-ebi.o
>  
> +obj-$(CONFIG_MTK_DRAMC)		+= mediatek/
>  obj-$(CONFIG_SAMSUNG_MC)	+= samsung/
>  obj-$(CONFIG_TEGRA_MC)		+= tegra/
>  obj-$(CONFIG_TI_EMIF_SRAM)	+= ti-emif-sram.o
> diff --git a/drivers/memory/mediatek/Kconfig b/drivers/memory/mediatek/Kconfig
> new file mode 100644
> index 0000000..a1618b0
> --- /dev/null
> +++ b/drivers/memory/mediatek/Kconfig
> @@ -0,0 +1,9 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +config MTK_DRAMC
> +	tristate "MediaTek DRAMC driver"
> +	help
> +	  This selects the MediaTek(R) DRAMC driver.
> +	  Provide the API for DRAMC low power scenario, and the interface
> +	  for reporting DRAM information, e.g. DRAM mode register (MR) for
> +	  DRAM vendor ID, temperature, and density.
> diff --git a/drivers/memory/mediatek/Makefile b/drivers/memory/mediatek/Makefile
> new file mode 100644
> index 0000000..632be48
> --- /dev/null
> +++ b/drivers/memory/mediatek/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +obj-$(CONFIG_MTK_DRAMC)	+= mtk-dramc.o
> diff --git a/drivers/memory/mediatek/mtk-dramc.c b/drivers/memory/mediatek/mtk-dramc.c
> new file mode 100644
> index 0000000..155b3b7
> --- /dev/null
> +++ b/drivers/memory/mediatek/mtk-dramc.c
> @@ -0,0 +1,711 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021 MediaTek Inc.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/printk.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <memory/mediatek/dramc.h>
> +
> +#define DRAMC_DRV_NAME	"mtk-dramc"

What does this define bring? What's the benefit?

> +
> +struct mr_info_t {
> +	unsigned int mr_index;
> +	unsigned int mr_value;
> +};
> +
> +/*

(...)

> +
> +static struct platform_driver dramc_drv = {
> +	.probe = dramc_probe,
> +	.remove = dramc_remove,
> +	.driver = {
> +		.name = DRAMC_DRV_NAME,
> +		.owner = THIS_MODULE,

NAK, this is so old mistake... No point to review - run Smatch, sparse,
checkpatch and coccinelle. Fix all the errors and then resubmit.

> +		.of_match_table = mtk_dramc_of_ids,
> +	},
> +};
> +
> +static int __init dramc_drv_init(void)
> +{
> +	int ret;
> +
> +	ret = platform_driver_register(&dramc_drv);
> +	if (ret) {
> +		pr_info("%s: init fail, ret 0x%x\n", __func__, ret);
> +		return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static void __exit dramc_drv_exit(void)
> +{
> +	platform_driver_unregister(&dramc_drv);
> +}
> +
> +module_init(dramc_drv_init);
> +module_exit(dramc_drv_exit);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("MediaTek DRAMC Driver");
> +MODULE_AUTHOR("Po-Kai Chi <pk.chi@mediatek.com>");
> diff --git a/include/memory/mediatek/dramc.h b/include/memory/mediatek/dramc.h
> new file mode 100644
> index 0000000..c8d200f
> --- /dev/null
> +++ b/include/memory/mediatek/dramc.h
> @@ -0,0 +1,18 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2021 MediaTek Inc.
> + */
> +
> +#ifndef __DRAMC_H__

Extend the header guard - MEMORY_MEDIATEK_DRAMC

> +#define __DRAMC_H__



Best regards,
Krzysztof

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v2 4/4] arm64: defconfig: Enable MediaTek DRAMC common driver
  2021-04-16  9:32   ` Po-Kai Chi
@ 2021-05-04 14:06     ` Krzysztof Kozlowski
  -1 siblings, 0 replies; 22+ messages in thread
From: Krzysztof Kozlowski @ 2021-05-04 14:06 UTC (permalink / raw)
  To: Po-Kai Chi, Matthias Brugger
  Cc: linux-kernel, linux-mediatek, devicetree, wsd_upstream, CC Hwang

On 16/04/2021 05:32, Po-Kai Chi wrote:
> This commit enables MediaTek DRAMC common driver to be built
> as a module by default for the ARM64 builds.

Don't write "This commit".
https://elixir.bootlin.com/linux/latest/source/Documentation/process/submitting-patches.rst#L89

Please read entire doc above.

Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v2 4/4] arm64: defconfig: Enable MediaTek DRAMC common driver
@ 2021-05-04 14:06     ` Krzysztof Kozlowski
  0 siblings, 0 replies; 22+ messages in thread
From: Krzysztof Kozlowski @ 2021-05-04 14:06 UTC (permalink / raw)
  To: Po-Kai Chi, Matthias Brugger
  Cc: linux-kernel, linux-mediatek, devicetree, wsd_upstream, CC Hwang

On 16/04/2021 05:32, Po-Kai Chi wrote:
> This commit enables MediaTek DRAMC common driver to be built
> as a module by default for the ARM64 builds.

Don't write "This commit".
https://elixir.bootlin.com/linux/latest/source/Documentation/process/submitting-patches.rst#L89

Please read entire doc above.

Best regards,
Krzysztof

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2021-05-04 14:07 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-16  9:32 [PATCH v2] memory: mediatek: add DRAM controller driver Po-Kai Chi
2021-04-16  9:32 ` Po-Kai Chi
2021-04-16  9:32 ` [PATCH v2 1/4] dt-bindings: memory: Add binding for MediaTek DRAM Controller Po-Kai Chi
2021-04-16  9:32   ` Po-Kai Chi
2021-05-04 14:00   ` Krzysztof Kozlowski
2021-05-04 14:00     ` Krzysztof Kozlowski
2021-04-16  9:32 ` [PATCH v2 2/4] memory: mediatek: add DRAM controller driver Po-Kai Chi
2021-04-16  9:32   ` Po-Kai Chi
2021-04-20 18:28   ` Rob Herring
2021-04-20 18:28     ` Rob Herring
2021-04-27  5:13     ` Po-Kai Chi
2021-04-27  5:13       ` Po-Kai Chi
2021-05-04 14:05   ` Krzysztof Kozlowski
2021-05-04 14:05     ` Krzysztof Kozlowski
2021-04-16  9:32 ` [PATCH v2 3/4] arm64: dts: add DRAMC node for MT6779 Po-Kai Chi
2021-04-16  9:32   ` Po-Kai Chi
2021-04-16  9:32 ` [PATCH v2 4/4] arm64: defconfig: Enable MediaTek DRAMC common driver Po-Kai Chi
2021-04-16  9:32   ` Po-Kai Chi
2021-05-04 14:06   ` Krzysztof Kozlowski
2021-05-04 14:06     ` Krzysztof Kozlowski
2021-05-04 13:56 ` [PATCH v2] memory: mediatek: add DRAM controller driver Krzysztof Kozlowski
2021-05-04 13:56   ` Krzysztof Kozlowski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.