[PATCH v2 2/4] memory: mediatek: add DRAM controller driver
Po-Kai Chi
pk.chi at mediatek.com
Tue Apr 27 06:13:21 BST 2021
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 at 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 at 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
> >
More information about the Linux-mediatek
mailing list