[PATCH] mmc: sdhci-cadence: Add support for Cadence sdmmc v6
Jaehoon Chung
jh80.chung at gmail.com
Wed Nov 15 13:16:44 CET 2023
On 11/15/23 12:36, KuanLim.Lee wrote:
> Hi Jaehoon,
> If there are no more feedback, I will send out version2 patch soon.
>
> On 11/7/23 14:24, Kuan Lim Lee wrote:
>>
>> Hi Jaehoon,
>>
>> On 11/1/23 08:20, Jaehoon Chung wrote:
>>> From: Jaehoon Chung <jh80.chung at samsung.com> Hi
>>>
>>> On 10/3/23 16:22, Kuan Lim Lee wrote:
>>>> From: Kuan Lim Lee <kuanlim.lee at linux.starfivetech.com>
>>>>
>>>> Cadence SDMMC v6 controller has a lot of changes on initialize
>>>> compared to v4 controller. PHY is needed by v6 controller.
>>>>
>>>> Signed-off-by: Kuan Lim Lee <kuanlim.lee at starfivetech.com>
>>>> Reviewed-by: Alex Soo <yuklin.soo at starfivetech.com>
>>>> Reviewed-by: Wei Liang Lim <weiliang.lim at starfivetech.com>
>>>
>>> I didn't see their Reviewed-by tag in mailing list.
>> They are my colleagues who collaborated develop code with me.
>> I think I should them in Signed-off-by, and put your name in Reviewed-by
>>
>>>
>>>> ---
>>>> drivers/mmc/Kconfig | 13 ++
>>>> drivers/mmc/Makefile | 1 +
>>>> drivers/mmc/sdhci-cadence.c | 63 ++-----
>>>> drivers/mmc/sdhci-cadence.h | 68 +++++++
>>>> drivers/mmc/sdhci-cadence6-phy.c | 302
>>>> +++++++++++++++++++++++++++++++
>>>> 5 files changed, 396 insertions(+), 51 deletions(-) create mode
>>>> 100644 drivers/mmc/sdhci-cadence.h create mode 100644
>>>> drivers/mmc/sdhci-cadence6-phy.c
>>>>
>>>> diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index
>>>> de01b9687b..cec881d862 100644
>>>> --- a/drivers/mmc/Kconfig
>>>> +++ b/drivers/mmc/Kconfig
>>>> @@ -573,6 +573,19 @@ config MMC_SDHCI_CADENCE
>>>>
>>>> If unsure, say N.
>>>>
>>>> +config MMC_SDHCI_CADENCE_V6
>>>> + bool "SDHCI support for the Cadence SD/SDIO/eMMC controller &
>>> driver version 6"
>>>> + depends on BLK && DM_MMC
>>>> + depends on MMC_SDHCI
>>>> + depends on OF_CONTROL
>>>> + select MMC_SDHCI_CADENCE
>>>> + help
>>>> + This selects the Cadence SD/SDIO/eMMC driver version 6.
>>>> +
>>>> + If you have a controller with this interface, say Y here.
>>>> +
>>>> + If unsure, say N.
>>>> +
>>>> config MMC_SDHCI_AM654
>>>> bool "SDHCI Controller on TI's Am654 devices"
>>>> depends on ARCH_K3
>>>> diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index
>>>> 2c65c4765a..cdcce55b8b 100644
>>>> --- a/drivers/mmc/Makefile
>>>> +++ b/drivers/mmc/Makefile
>>>> @@ -61,6 +61,7 @@ obj-$(CONFIG_MMC_SDHCI_ATMEL) +=
>>> atmel_sdhci.o
>>>> obj-$(CONFIG_MMC_SDHCI_BCM2835) += bcm2835_sdhci.o
>>>> obj-$(CONFIG_MMC_SDHCI_BCMSTB) += bcmstb_sdhci.o
>>>> obj-$(CONFIG_MMC_SDHCI_CADENCE) += sdhci-cadence.o
>>>> +obj-$(CONFIG_MMC_SDHCI_CADENCE_V6) += sdhci-cadence6-phy.o
>>>> obj-$(CONFIG_MMC_SDHCI_AM654) += am654_sdhci.o
>>>> obj-$(CONFIG_MMC_SDHCI_IPROC) += iproc_sdhci.o
>>>> obj-$(CONFIG_MMC_SDHCI_KONA) += kona_sdhci.o
>>>> diff --git a/drivers/mmc/sdhci-cadence.c
>>>> b/drivers/mmc/sdhci-cadence.c index 327a05ad11..d7a270e74c 100644
>>>> --- a/drivers/mmc/sdhci-cadence.c
>>>> +++ b/drivers/mmc/sdhci-cadence.c
>>>> @@ -17,56 +17,7 @@
>>>> #include <linux/libfdt.h>
>>>> #include <mmc.h>
>>>> #include <sdhci.h>
>>>> -
>>>> -/* HRS - Host Register Set (specific to Cadence) */
>>>> -#define SDHCI_CDNS_HRS04 0x10 /* PHY access
>> port */
>>>> -#define SDHCI_CDNS_HRS04_ACK BIT(26)
>>>> -#define SDHCI_CDNS_HRS04_RD BIT(25)
>>>> -#define SDHCI_CDNS_HRS04_WR BIT(24)
>>>> -#define SDHCI_CDNS_HRS04_RDATA GENMASK(23, 16)
>>>> -#define SDHCI_CDNS_HRS04_WDATA GENMASK(15, 8)
>>>> -#define SDHCI_CDNS_HRS04_ADDR GENMASK(5,
>> 0)
>>>> -
>>>> -#define SDHCI_CDNS_HRS06 0x18 /* eMMC
>> control */
>>>> -#define SDHCI_CDNS_HRS06_TUNE_UP BIT(15)
>>>> -#define SDHCI_CDNS_HRS06_TUNE GENMASK(13,
>> 8)
>>>> -#define SDHCI_CDNS_HRS06_MODE GENMASK(2,
>> 0)
>>>> -#define SDHCI_CDNS_HRS06_MODE_SD 0x0
>>>> -#define SDHCI_CDNS_HRS06_MODE_MMC_SDR 0x2
>>>> -#define SDHCI_CDNS_HRS06_MODE_MMC_DDR 0x3
>>>> -#define SDHCI_CDNS_HRS06_MODE_MMC_HS200 0x4
>>>> -#define SDHCI_CDNS_HRS06_MODE_MMC_HS400 0x5
>>>> -#define SDHCI_CDNS_HRS06_MODE_MMC_HS400ES 0x6
>>>> -
>>>> -/* SRS - Slot Register Set (SDHCI-compatible) */
>>>> -#define SDHCI_CDNS_SRS_BASE 0x200
>>>> -
>>>> -/* PHY */
>>>> -#define SDHCI_CDNS_PHY_DLY_SD_HS 0x00
>>>> -#define SDHCI_CDNS_PHY_DLY_SD_DEFAULT 0x01
>>>> -#define SDHCI_CDNS_PHY_DLY_UHS_SDR12 0x02
>>>> -#define SDHCI_CDNS_PHY_DLY_UHS_SDR25 0x03
>>>> -#define SDHCI_CDNS_PHY_DLY_UHS_SDR50 0x04
>>>> -#define SDHCI_CDNS_PHY_DLY_UHS_DDR50 0x05
>>>> -#define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY 0x06
>>>> -#define SDHCI_CDNS_PHY_DLY_EMMC_SDR 0x07
>>>> -#define SDHCI_CDNS_PHY_DLY_EMMC_DDR 0x08
>>>> -#define SDHCI_CDNS_PHY_DLY_SDCLK 0x0b
>>>> -#define SDHCI_CDNS_PHY_DLY_HSMMC 0x0c
>>>> -#define SDHCI_CDNS_PHY_DLY_STROBE 0x0d
>>>> -
>>>> -/*
>>>> - * The tuned val register is 6 bit-wide, but not the whole of the
>>>> range is
>>>> - * available. The range 0-42 seems to be available (then 43 wraps
>>>> around to 0)
>>>> - * but I am not quite sure if it is official. Use only 0 to 39 for safety.
>>>> - */
>>>> -#define SDHCI_CDNS_MAX_TUNING_LOOP 40
>>>> -
>>>> -struct sdhci_cdns_plat {
>>>> - struct mmc_config cfg;
>>>> - struct mmc mmc;
>>>> - void __iomem *hrs_addr;
>>>> -};
>>>> +#include "sdhci-cadence.h"
>>>>
>>>> struct sdhci_cdns_phy_cfg {
>>>> const char *property;
>>>> @@ -112,8 +63,11 @@ static int sdhci_cdns_write_phy_reg(struct
>>>> sdhci_cdns_plat *plat, }
>>>>
>>>> static int sdhci_cdns_phy_init(struct sdhci_cdns_plat *plat,
>>>> - const void *fdt, int nodeoffset)
>>>> + const void *fdt, int nodeoffset)
>>>
>>> Is there any reason to touch this?
>> I thought the space key is misaligned. I think I better not to touch it.
>>>
>>>
>>>> {
>>>> + if (IS_ENABLED(CONFIG_MMC_SDHCI_CADENCE_V6))
>>>> + return sdhci_cdns6_phy_init(plat,
>>> SDHCI_CDNS_HRS06_MODE_SD);
>>>> +
>>>> const fdt32_t *prop;
>>>> int ret, i;
>>>>
>>>> @@ -163,6 +117,9 @@ static void sdhci_cdns_set_control_reg(struct
>>> sdhci_host *host)
>>>> tmp &= ~SDHCI_CDNS_HRS06_MODE;
>>>> tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_MODE, mode);
>>>> writel(tmp, plat->hrs_addr + SDHCI_CDNS_HRS06);
>>>> +
>>>> + if (IS_ENABLED(CONFIG_MMC_SDHCI_CADENCE_V6))
>>>> + sdhci_cdns6_phy_init(plat, mode);
>>>> }
>>>>
>>>> static const struct sdhci_ops sdhci_cdns_ops = { @@ -172,6 +129,9
>>>> @@ static const struct sdhci_ops sdhci_cdns_ops = { static int
>>>> sdhci_cdns_set_tune_val(struct sdhci_cdns_plat *plat,
>>>> unsigned int val)
>>>> {
>>>> + if (IS_ENABLED(CONFIG_MMC_SDHCI_CADENCE_V6))
>>>> + return sdhci_cdns6_set_tune_val(plat, val);
>>>> +
>>>> void __iomem *reg = plat->hrs_addr + SDHCI_CDNS_HRS06;
>>>> u32 tmp;
>>>> int i, ret;
>>>> @@ -301,6 +261,7 @@ static int sdhci_cdns_probe(struct udevice *dev)
>>>> static const struct udevice_id sdhci_cdns_match[] = {
>>>> { .compatible = "socionext,uniphier-sd4hc" },
>>>> { .compatible = "cdns,sd4hc" },
>>>> + { .compatible = "cdns,sd6hc" },
>>>> { /* sentinel */ }
>>>> };
>>>>
>>>> diff --git a/drivers/mmc/sdhci-cadence.h
>>>> b/drivers/mmc/sdhci-cadence.h new file mode 100644 index
>>>> 0000000000..2c42cff2f3
>>>> --- /dev/null
>>>> +++ b/drivers/mmc/sdhci-cadence.h
>>>> @@ -0,0 +1,68 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0-only */
>>>> +/*
>>>> + * Copyright (C) 2022 Starfive.
>>>> + * Author: Kuan Lim Lee <kuanlim.lee at starfivetech.com>
>>>
>>>
>>> Does it right Copyright and author? the below codes are taken from
>>> sdhci- cadence.c.
>> Some parts of #define are taken from sdchi-cadence.c, some parts of #define
>> are written by me. I think I still better put back the original Copyright and
>> author.
>>
>>>
>>>> + */
>>>> +
>>>> +#ifndef SDHCI_CADENCE_H_
>>>> +#define SDHCI_CADENCE_H_
>>>> +
>>>> +/* HRS - Host Register Set (specific to Cadence) */
>>>> +/* PHY access port */
>>>> +#define SDHCI_CDNS_HRS04 0x10
>>>> +/* Cadence V4 HRS04 Description*/
>>>> +#define SDHCI_CDNS_HRS04_ACK BIT(26)
>>>> +#define SDHCI_CDNS_HRS04_RD BIT(25)
>>>> +#define SDHCI_CDNS_HRS04_WR BIT(24)
>>>> +#define SDHCI_CDNS_HRS04_RDATA GENMASK(23, 16)
>>>> +#define SDHCI_CDNS_HRS04_WDATA GENMASK(15, 8)
>>>> +#define SDHCI_CDNS_HRS04_ADDR GENMASK(5,
>> 0)
>>>> +
>>>> +#define SDHCI_CDNS_HRS05 0x14
>>>> +
>>>> +/* eMMC control */
>>>> +#define SDHCI_CDNS_HRS06 0x18
>>>> +#define SDHCI_CDNS_HRS06_TUNE_UP BIT(15)
>>>> +#define SDHCI_CDNS_HRS06_TUNE GENMASK(13,
>> 8)
>>>> +#define SDHCI_CDNS_HRS06_MODE GENMASK(2,
>>> 0)
>>>> +#define SDHCI_CDNS_HRS06_MODE_SD 0x0
>>>> +#define SDHCI_CDNS_HRS06_MODE_MMC_SDR 0x2
>>>> +#define SDHCI_CDNS_HRS06_MODE_MMC_DDR 0x3
>>>> +#define SDHCI_CDNS_HRS06_MODE_MMC_HS200 0x4
>>>> +#define SDHCI_CDNS_HRS06_MODE_MMC_HS400 0x5
>>>> +#define SDHCI_CDNS_HRS06_MODE_MMC_HS400ES 0x6
>>>> +
>>>> +/* SRS - Slot Register Set (SDHCI-compatible) */
>>>> +#define SDHCI_CDNS_SRS_BASE 0x200
>>>> +
>>>> +/* Cadence V4 PHY Setting*/
>>>> +#define SDHCI_CDNS_PHY_DLY_SD_HS 0x00
>>>> +#define SDHCI_CDNS_PHY_DLY_SD_DEFAULT 0x01
>>>> +#define SDHCI_CDNS_PHY_DLY_UHS_SDR12 0x02
>>>> +#define SDHCI_CDNS_PHY_DLY_UHS_SDR25 0x03
>>>> +#define SDHCI_CDNS_PHY_DLY_UHS_SDR50 0x04
>>>> +#define SDHCI_CDNS_PHY_DLY_UHS_DDR50 0x05
>>>> +#define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY 0x06
>>>> +#define SDHCI_CDNS_PHY_DLY_EMMC_SDR 0x07
>>>> +#define SDHCI_CDNS_PHY_DLY_EMMC_DDR 0x08
>>>> +#define SDHCI_CDNS_PHY_DLY_SDCLK 0x0b
>>>> +#define SDHCI_CDNS_PHY_DLY_HSMMC 0x0c
>>>> +#define SDHCI_CDNS_PHY_DLY_STROBE 0x0d
>>>> +
>>>> +/*
>>>> + * The tuned val register is 6 bit-wide, but not the whole of the
>>>> +range is
>>>> + * available. The range 0-42 seems to be available (then 43 wraps
>>>> +around to 0)
>>>> + * but I am not quite sure if it is official. Use only 0 to 39 for safety.
>>>> + */
>>>> +#define SDHCI_CDNS_MAX_TUNING_LOOP 40
>>>> +
>>>> +struct sdhci_cdns_plat {
>>>> + struct mmc_config cfg;
>>>> + struct mmc mmc;
>>>> + void __iomem *hrs_addr;
>>>> +};
>>>> +
>>>> +int sdhci_cdns6_phy_init(struct sdhci_cdns_plat *plat, u32 mode);
>>>> +int sdhci_cdns6_set_tune_val(struct sdhci_cdns_plat *plat, unsigned
>>>> +int val);
>>>> +
>>>> +#endif
>>>> diff --git a/drivers/mmc/sdhci-cadence6-phy.c
>>>> b/drivers/mmc/sdhci-cadence6-phy.c
>>>> new file mode 100644
>>>> index 0000000000..dd3df27dc8
>>>> --- /dev/null
>>>> +++ b/drivers/mmc/sdhci-cadence6-phy.c
>>>> @@ -0,0 +1,302 @@
>>>> +// SPDX-License-Identifier: GPL-2.0-or-platform_driver
>>>> +/*
>>>> + * Copyright (C) 2022 Starfive.
>>>
>>> s/2022/2023?
>> I will put 2023.
>>
>>>
>>>> + * Author: Kuan Lim Lee <kuanlim.lee at starfivetech.com>
>>>> + */
>>>> +
>>>> +#include <common.h>
>>>> +#include <dm.h>
>>>> +#include <asm/global_data.h>
>>>> +#include <dm/device_compat.h>
>>>> +#include <linux/bitfield.h>
>>>> +#include <linux/bitops.h>
>>>> +#include <linux/bug.h>
>>>> +#include <linux/io.h>
>>>> +#include <linux/iopoll.h>
>>>> +#include <linux/sizes.h>
>>>> +#include <linux/libfdt.h>
>>>> +#include <mmc.h>
>>>> +#include <sdhci.h>
>>>> +#include "sdhci-cadence.h"
>>>> +
>>>> +/* IO Delay Information */
>>>> +#define SDHCI_CDNS_HRS07 0X1C
>>>> +#define SDHCI_CDNS_HRS07_RW_COMPENSATE GENMASK(20,
>>> 16)
>>>> +#define SDHCI_CDNS_HRS07_IDELAY_VAL GENMASK(4,
>>> 0)
>>>> +
>>>> +#define SDHCI_CDNS_HRS09 0x24 /* PHY Control
>> and
>>> Status */
>>>> +#define SDHCI_CDNS_HRS09_RDDATA_EN BIT(16)
>>>> +#define SDHCI_CDNS_HRS09_RDCMD_EN BIT(15)
>>>> +#define SDHCI_CDNS_HRS09_EXTENDED_WR_MODE BIT(3)
>>>> +#define SDHCI_CDNS_HRS09_EXTENDED_RD_MODE BIT(2)
>>>> +#define SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE BIT(1)
>>>> +#define SDHCI_CDNS_HRS09_PHY_SW_RESET BIT(0)
>>>> +
>>>> +#define SDHCI_CDNS_HRS10 0x28 /* SDCLK
>> adjustment
>>> */
>>>> +#define SDHCI_CDNS_HRS10_HCSDCLKADJ GENMASK(19,
>> 16)
>>>> +
>>>> +#define SDHCI_CDNS_HRS16 0x40 /* CMD/DAT
>> output
>>> delay */
>>>> +
>>>> +/* PHY Special Function Registers */
>>>> +//#define DLL_PHY_REG_BASE 0x2000
>>>> +
>>>> +/* register to control the DQ related timing */
>>>> +#define PHY_DQ_TIMING_REG_ADDR 0x2000
>>>> +
>>>> +/* register to control the DQS related timing */
>>>> +#define PHY_DQS_TIMING_REG_ADDR 0x2004
>>>> +
>>>> +/* register to control the gate and loopback control related timing */
>>>> +#define PHY_GATE_LPBK_CTRL_REG_ADDR 0x2008
>>>> +
>>>> +/* register to control the Master DLL logic */
>>>> +#define PHY_DLL_MASTER_CTRL_REG_ADDR 0x200C
>>>> +
>>>> +/* register to control the Slave DLL logic */
>>>> +#define PHY_DLL_SLAVE_CTRL_REG_ADDR 0x2010
>>>> +#define PHY_DLL_SLAVE_CTRL_REG_READ_DQS_CMD_DELAY
>>> GENMASK(31, 24)
>>>> +#define PHY_DLL_SLAVE_CTRL_REG_READ_DQS_DELAY
>>> GENMASK(7, 0)
>>>> +
>>>> +/* register to control the global settings for PHY */
>>>> +#define PHY_CTRL_REG_ADDR 0x2080
>>>> +
>>>> +struct phy_reg {
>>>> + u32 phy_dqs_timing;
>>>> + u32 phy_gate_lpbk_ctrl;
>>>> + //u32 phy_dll_master_ctrl;
>>>
>>> Remove unused code.
>> Noted.
>>>
>>>> + u32 phy_dll_slave_ctrl;
>>>> + //u32 phy_ctrl;
>>>
>>> ditto
>> Noted.
>>>
>>>> + u32 phy_dq_timing;
>>>> + //cp_sw_half_cycle_shift; ASIC
>>>
>>> Ditto
>> Noted.
>>>
>>>> +};
>>>> +
>>>> +struct controller_reg {
>>>> + u32 hrs07;
>>>> + u32 hrs09;
>>>> + u32 hrs10;
>>>> + u32 hrs16;
>>>> +};
>>>
>>>
>>> As some reg values can be re-used. I don't know what its value means.
>>> e.g) 0x00380004 is sued sd_ds/emmc_sdr/emmc_ddr/emmc_hs200.
>> This value is generated by script provided by Cadence.
>> e.g.) phy_dqs_timing_reg (0x2004) is a 32 bits register, inside the register has
>> few component: use_ext_lpbk_dqs, use_lpbk_dqs, use_phony_dqs,
>> use_phony_dqs_cmd, phony_dqs_sel, dqs_select_tsel_start and
>> dqs_select_tsel_end.
>>
>> Each components value is generated by script and I combined the value in a 32
>> bits value, 0x00380004.
>>
>>>
>>> I don't want to use a magic code. If you can add some macros, it's
>>> more readable.
>> Should I change the struct in way below, so that it is more reable?
>> static struct sdhci_cdns_phy_reg_pairs sd_ds_mode_setting[] = {
>> {0x00380000, PHY_DQS_TIMING_REG_ADDR},
>> {0x01A00040, PHY_GATE_LPBK_CTRL_REG_ADDR},
>> {0x00000000, PHY_DLL_SLAVE_CTRL_REG_ADDR},
>> {0x00000180, PHY_CTRL_REG_ADDR},
>> {0x00000001, PHY_DQ_TIMING_REG_ADDR},
>> };
>>
>> Or I have to put each value of components and put a convertion function in the
>> code to change this to 32 bits value and program inti register,
>> phy_dqs_timing_reg (0x2004)?
>>
>>>
>>>> +
>>>> +static struct phy_reg sd_ds_phy_cfg = {
>>>> + 0x00380004,
>>>> + 0x01A00040,
>>>> + 0x00000000,
>>>> + 0x00000001,
>>>> +};
>>>> +
>>>> +static struct phy_reg emmc_sdr_phy_cfg = {
>>>> + 0x00380004,
>>>> + 0x01A00040,
>>>> + 0x00000000,
>>>> + 0x00000001,
>>>> +};
>>>> +
>>>> +static struct phy_reg emmc_ddr_phy_cfg = {
>>>> + 0x00380004,
>>>> + 0x01A00040,
>>>> + 0x00000000,
>>>> + 0x10000001,
>>>> +};
>>>> +
>>>> +static struct phy_reg emmc_hs200_phy_cfg = {
>>>> + 0x00380004,
>>>> + 0x01A00040,
>>>> + 0x00DADA00,
>>>> + 0x00000001,
>>>> +};
>>>> +
>>>> +static struct phy_reg emmc_hs400_phy_cfg = {
>>>> + 0x00280004,
>>>> + 0x01A00040,
>>>> + 0x00DAD800,
>>>> + 0x00000001,
>>>> +};
>>>> +
>>>> +static struct controller_reg sd_ds_ctrl_cfg = {
>>>> + 0x00080000,
>>>> + 0x0001800C,
>>>> + 0x00020000,
>>>> + 0x00000000,
>>>> +};
>>>> +
>>>> +static struct controller_reg emmc_sdr_ctrl_cfg = {
>>>> + 0x00080000,
>>>> + 0x0001800C,
>>>> + 0x00030000,
>>>> + 0x00000000,
>>>> +};
>>>> +
>>>> +static struct controller_reg emmc_ddr_ctrl_cfg = {
>>>> + 0x00090001,
>>>> + 0x0001800C,
>>>> + 0x00020000,
>>>> + 0x11000001,
>>>> +};
>>>> +
>>>> +static struct controller_reg emmc_hs200_ctrl_cfg = {
>>>> + 0x00090000,
>>>> + 0x00018000,
>>>> + 0x00080000,
>>>> + 0x00000000,
>>>> +};
>>>> +
>>>> +static struct controller_reg emmc_hs400_ctrl_cfg = {
>>>> + 0x00080000,
>>>> + 0x00018000,
>>>> + 0x00080000,
>>>> + 0x11000000,
>>>> +};
>>>> +
>>>> +static unsigned int sdhci_cdns6_read_phy_reg(struct sdhci_cdns_plat *plat,
>>>> + u32 addr)
>>>> +{
>>>> + u32 data = 0;
>>>> +
>>>> + writel(addr, plat->hrs_addr + SDHCI_CDNS_HRS04);
>>>> + data = readl(plat->hrs_addr + SDHCI_CDNS_HRS05);
>>>> + return data;
>>>> +}
>>>> +
>>>> +static void sdhci_cdns6_write_phy_reg(struct sdhci_cdns_plat *plat,
>>>> + u32 addr, u32 data)
>>>> +{
>>>> + u32 readdat = 0;
>>>> +
>>>> + writel(addr, plat->hrs_addr + SDHCI_CDNS_HRS04);
>>>> + writel(data, plat->hrs_addr + SDHCI_CDNS_HRS05);
>>>> + readdat = readl(plat->hrs_addr + SDHCI_CDNS_HRS05);
>>>> +
>>>> + if (readdat != data) {
>>>> + pr_err("Error, %s: Writing failed!: address: 0x%x, value : 0x%x,
>>>> + readval: 0x%x\n", __func__, addr, data, readdat);
>>>
>>>
>>> Doesn't need to return error value?
>> Yes, I think it will be changed to return error value.
>>>
>>>
>>>> + }
>>>> +}
>>>> +
>>>> +static int sdhci_cdns6_reset_phy_dll(struct sdhci_cdns_plat *plat,
>>>> + unsigned int reset)
>>>> +{
>>>> + void __iomem *reg = plat->hrs_addr + SDHCI_CDNS_HRS09;
>>>> + u32 tmp;
>>>> + int ret;
>>>> +
>>>> + tmp = readl(reg);
>>>> + tmp &= ~SDHCI_CDNS_HRS09_PHY_SW_RESET;
>>>> +
>>>> + if (reset) /* Switch On DLL Reset */
>>>
>>>
>>> /* Swithc On DLL Reset */
>> Noted, thanks for telling.
>>
>>> if (reset)
>>> ...
>>> else
>>> ...
>>>
>>>
>>>> + tmp |= FIELD_PREP(SDHCI_CDNS_HRS09_PHY_SW_RESET, 0);
>>>> + else /* Switch Off DLL Reset */
>>>> + tmp |= FIELD_PREP(SDHCI_CDNS_HRS09_PHY_SW_RESET, 1);
>>>> +
>>>> + writel(tmp, reg);
>>>> +
>>>> + if (!reset) {
>>>> + ret = readl_poll_timeout(reg, tmp,
>>>> + (tmp &
>>> SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE),
>>>> + 3000);
>>>
>>> Add a comment why it needs to poll during 3000.
>> Actually 3000 us is a safe range. User manual doesn’t specify, I put myself.
>> Comment will be like below:
>> /* After reset, wait for PHY_INIT_COMPLETE in HRS09 register until it is set to 1
>> within 3000us*/
>>
>>>
>>>> + }
>>>> +
>>>> + return ret;
>>>> +}
>>>> +
>>>> +int sdhci_cdns6_phy_init(struct sdhci_cdns_plat *plat, u32 mode) {
>>>> + struct phy_reg *phy_cfgs;
>>>> + struct controller_reg *ctrl_cfgs;
>>>> + void __iomem *reg = NULL;
>>>> + u32 tmp;
>>>> + int ret;
>>>> +
>>>> + switch (mode) {
>>>> + case SDHCI_CDNS_HRS06_MODE_SD:
>>>> + phy_cfgs = &sd_ds_phy_cfg;
>>>> + ctrl_cfgs = &sd_ds_ctrl_cfg;
>>>> + break;
>>>> +
>>>> + case SDHCI_CDNS_HRS06_MODE_MMC_SDR:
>>>> + phy_cfgs = &emmc_sdr_phy_cfg;
>>>> + ctrl_cfgs = &emmc_sdr_ctrl_cfg;
>>>> + break;
>>>> +
>>>> + case SDHCI_CDNS_HRS06_MODE_MMC_DDR:
>>>> + phy_cfgs = &emmc_ddr_phy_cfg;
>>>> + ctrl_cfgs = &emmc_ddr_ctrl_cfg;
>>>> + break;
>>>> +
>>>> + case SDHCI_CDNS_HRS06_MODE_MMC_HS200:
>>>> + phy_cfgs = &emmc_hs200_phy_cfg;
>>>> + ctrl_cfgs = &emmc_hs200_ctrl_cfg;
>>>> + break;
>>>> +
>>>> + case SDHCI_CDNS_HRS06_MODE_MMC_HS400:
>>>> + phy_cfgs = &emmc_hs400_phy_cfg;
>>>> + ctrl_cfgs = &emmc_hs400_ctrl_cfg;
>>>> + break;
>>>
>>>
>>> If there is no matched mod, phy_cfgs and ctrl_cfgs should be NULL.
>> Noted.
>>
>>>
>>>> + }
>>>> +
>>>> + /* Switch On the DLL Reset */
>>>> + sdhci_cdns6_reset_phy_dll(plat, 1);
>>>> +
>>>> + sdhci_cdns6_write_phy_reg(plat, PHY_DQS_TIMING_REG_ADDR,
>>>> + phy_cfgs->phy_dqs_timing);
>>>> + sdhci_cdns6_write_phy_reg(plat, PHY_GATE_LPBK_CTRL_REG_ADDR,
>>>> + phy_cfgs->phy_gate_lpbk_ctrl);
>>>> + sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR,
>>>> + phy_cfgs->phy_dll_slave_ctrl);
>>>> +
>>>> + /* Switch Off the DLL Reset */
>>>> + ret = sdhci_cdns6_reset_phy_dll(plat, 0);
>>>> + if (ret) {
>>>> + printf("sdhci_cdns6_reset_phy is not completed\n");
>>>> + return ret;
>>>> + }
>>>> +
>>>> + /* Set PHY DQ TIMING control register */
>>>> + sdhci_cdns6_write_phy_reg(plat, PHY_DQ_TIMING_REG_ADDR,
>>>> + phy_cfgs->phy_dq_timing);
>>>> +
>>>> + /* Set HRS09 register */
>>>> + reg = plat->hrs_addr + SDHCI_CDNS_HRS09;
>>>> + tmp = readl(reg);
>>>
>>>
>>> I'm not suer why plat-hrs_addr assigned to reg.
>>> How about using the below?
>>>
>>> hrs_base_addr = plat->hrs_addr;
>>>
>>> readl(hrs_base_addr + SDHCI_CDNS_HRS10);
>>>
>>> Then you can change the below code like
>>>
>>> readl(hrs_base_addr + SDHCI_CDNS_HRS16); readl(hrs_base_addr +
>>> SDHCI_CDNS_HRS07);
>>>
>>> Otherwise, readl(plat->hrs_addr + SDHCI_xxx);
>> Noted, this looked simpler.
>>
>>>
>>>
>>>> + tmp &= ~(SDHCI_CDNS_HRS09_EXTENDED_WR_MODE |
>>>> + SDHCI_CDNS_HRS09_EXTENDED_RD_MODE |
>>>> + SDHCI_CDNS_HRS09_RDDATA_EN |
>>>> + SDHCI_CDNS_HRS09_RDCMD_EN);
>>>> + tmp |= ctrl_cfgs->hrs09;
>>>> + writel(tmp, reg);
>>>> +
>>>> + /* Set HRS10 register */
>>>> + reg = plat->hrs_addr + SDHCI_CDNS_HRS10;
>>>> + tmp = readl(reg);
>>>> + tmp &= ~SDHCI_CDNS_HRS10_HCSDCLKADJ;
>>>> + tmp |= ctrl_cfgs->hrs10;
>>>> + writel(tmp, reg);
>>>> +
>>>> + /* Set HRS16 register */
>>>> + reg = plat->hrs_addr + SDHCI_CDNS_HRS16;
>>>> + tmp = ctrl_cfgs->hrs16;
>>>> + writel(tmp, reg);
>>>> +
>>>> + /* Set HRS07 register */
>>>> + reg = plat->hrs_addr + SDHCI_CDNS_HRS07;
>>>> + tmp = ctrl_cfgs->hrs07;
>>>> + writel(tmp, reg);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +int sdhci_cdns6_set_tune_val(struct sdhci_cdns_plat *plat,
>>>> + unsigned int val)
>>>> +{
>>>> + u32 tmp, tuneval;
>>>
>>> Ditto.
>> Any problem with this? I think current way looked simple.
Ah, My opinion is the using a val or other meaningful name instead of tmp.
Best Regards,
Jaehoon Chung
>>
>>>
>>> Best Regards,
>>> Jaehoon Chung
>>>
>>>> +
>>>> + tuneval = (val * 256) / SDHCI_CDNS_MAX_TUNING_LOOP;
>>>> +
>>>> + tmp = sdhci_cdns6_read_phy_reg(plat,
>>> PHY_DLL_SLAVE_CTRL_REG_ADDR);
>>>> + tmp &= ~(PHY_DLL_SLAVE_CTRL_REG_READ_DQS_CMD_DELAY |
>>>> + PHY_DLL_SLAVE_CTRL_REG_READ_DQS_DELAY);
>>>> + tmp |=
>>> FIELD_PREP(PHY_DLL_SLAVE_CTRL_REG_READ_DQS_CMD_DELAY, tuneval) |
>>>> + FIELD_PREP(PHY_DLL_SLAVE_CTRL_REG_READ_DQS_DELAY,
>>> tuneval);
>>>> + sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR,
>>> tmp);
>>>> +
>>>> + return 0;
>>>> +}
>>>
>
More information about the U-Boot
mailing list