Re: [PATCH v2] mmc: sdhci-pci: add Support of Synopsys DWC_MSHC IP

From: Jaehoon Chung
Date: Wed Apr 20 2016 - 03:36:33 EST


Hi Pradu,

On 04/20/2016 04:00 PM, Prabu Thangamuthu wrote:
> Hi Jaehoon Chung,
>
> Thank you for detailed review comments.
>
> On 04/19/2016 04:31 PM, Jaehoon Chung wrote:
>> On 04/19/2016 04:18 PM, Prabu Thangamuthu wrote:
>>> Synopsys DWC_MSHC is compliant with SD Host Specifications. This patch
>>> is to support DWC_MSHC controller on PCI interface.
>>
>> There is dwmmc controller for Synopsys DWC_MSHC.
>> According to commit message, dwmmc controller also is compliant with SD
>> host specifications.
>>
>> I think it's enough to use "sdhci-dwc".(To prevent confusion.)
>
> For Information, Synopsys has two different host controllers,
> 1. dw_mmc - It's old controller and not compliant with SDHCI specification.
> 2. dwc_mshc - It's new controller and compliant with SDHCI specification.

Thanks for this information.
Actually, it should be difficult to distinguish between dw_mmc and dwc_mshc.

As you know, DesignWare Cores Mobile Storage Host Databook is also dwmmc controller's TRM.
mshc is Mobile Storage Host controller, right? it's confusion.

>
> This patch is to support second controller (dwc_mshc).
>
> As you mentioned, we will use "sdhci-dwc" instead of Synopsys DWC_MSHC
> to avoid confusion.

Thanks for accepting my suggestion. :)

>
>>
>>>
>>> Signed-off-by: Prabu Thangamuthu <prabu.t@xxxxxxxxxxxx>
>>> ---
>>> Change log v2:
>>> -Removed Synopsys specific PCI device ID's from pci_ids.h.
>>> -Updated the PCI device ID's in sdhci-pci-core.c.
>>>
>>> MAINTAINERS | 7 +
>>> drivers/mmc/host/Makefile | 3 +-
>>> drivers/mmc/host/sdhci-dwc-mshc-pci.c | 298
>>> ++++++++++++++++++++++++++++++++++
>>> drivers/mmc/host/sdhci-dwc-mshc-pci.h | 58 +++++++
>>> drivers/mmc/host/sdhci-pci-core.c | 14 ++
>>> 5 files changed, 379 insertions(+), 1 deletion(-) create mode 100644
>>> drivers/mmc/host/sdhci-dwc-mshc-pci.c
>>> create mode 100644 drivers/mmc/host/sdhci-dwc-mshc-pci.h
>>>
>>> diff --git a/MAINTAINERS b/MAINTAINERS index 1d5b4be..b260b97 100644
>>> --- a/MAINTAINERS
>>> +++ b/MAINTAINERS
>>> @@ -9751,6 +9751,13 @@ S: Maintained
>>> F: include/linux/mmc/dw_mmc.h
>>> F: drivers/mmc/host/dw_mmc*
>>>
>>> +SYNOPSYS SDHCI COMPLIANT DWC MSHC DRIVER
>>> +M: Prabu Thangamuthu <prabu.t@xxxxxxxxxxxx>
>>> +M: Manjunath M B <manjumb@xxxxxxxxxxxx>
>>> +L: linux-mmc@xxxxxxxxxxxxxxx
>>> +S: Maintained
>>> +F: drivers/mmc/host/sdhci-dwc-mshc*
>>> +
>>> SYSTEM TRACE MODULE CLASS
>>> M: Alexander Shishkin <alexander.shishkin@xxxxxxxxxxxxxxx>
>>> S: Maintained
>>> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
>>> index af918d2..1d2a3cf 100644
>>> --- a/drivers/mmc/host/Makefile
>>> +++ b/drivers/mmc/host/Makefile
>>> @@ -9,7 +9,8 @@ obj-$(CONFIG_MMC_MXC) += mxcmmc.o
>>> obj-$(CONFIG_MMC_MXS) += mxs-mmc.o
>>> obj-$(CONFIG_MMC_SDHCI) += sdhci.o
>>> obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
>>> -sdhci-pci-y += sdhci-pci-core.o sdhci-pci-o2micro.o
>>> +sdhci-pci-y += sdhci-pci-core.o sdhci-pci-o2micro.o \
>>> + sdhci-dwc-mshc-pci.o
>>
>> how about using sdhci-pci-XXX?
>
> OK, We will update it accordingly.
>
>
>>> obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-data.o
>>> obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o
>>> obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o
>>> diff --git a/drivers/mmc/host/sdhci-dwc-mshc-pci.c
>>> b/drivers/mmc/host/sdhci-dwc-mshc-pci.c
>>> new file mode 100644
>>> index 0000000..e934af4
>>> --- /dev/null
>>> +++ b/drivers/mmc/host/sdhci-dwc-mshc-pci.c
>>> @@ -0,0 +1,298 @@
>>> +/*
>>> + * Copyright (C) 2016 Synopsys, Inc.
>>> + *
>>> + * Author: Manjunath M B <manjumb@xxxxxxxxxxxx>
>>> + *
>>> + * This software is licensed under the terms of the GNU General
>>> +Public
>>> + * License version 2, as published by the Free Software Foundation,
>>> +and
>>> + * may be copied, distributed, and modified under those terms.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>> + * GNU General Public License for more details.
>>> + *
>>> + */
>>> +
>>> +#include <linux/pci.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/module.h>
>>> +#include <linux/moduleparam.h>
>>> +
>>> +#include "sdhci.h"
>>> +#include "sdhci-pci.h"
>>> +#include "sdhci-dwc-mshc-pci.h"
>>> +
>>>
>> +/*********************************************************
>> ********************\
>>> + * *
>>> + * Hardware specific clock handling *
>>> + * *
>>>
>> +\*********************************************************
>> ********************/
>>> +static const struct sdhci_ops *sdhci_ops; /* Low level hw interface */
>>> +
>>> +static void sdhci_set_clock_snps(struct sdhci_host *host,
>>> + unsigned int clock)
>>> +{
>>> + int div = 0;
>>> + int mul = 0;
>>> + int div_val = 0;
>>> + int mul_val = 0;
>>> + int mul_div_val = 0;
>>> + int reg = 0;
>>> + u16 clk = 0;
>>> + u32 vendor_ptr;
>>> + unsigned long timeout;
>>> + u32 tx_clk_phase_val = SDHC_DEF_TX_CLK_PH_VAL;
>>> + u32 rx_clk_phase_val = SDHC_DEF_RX_CLK_PH_VAL;
>>> +
>>> + /* DWC MSHC Specific clock settings */
>>
>> I think this comment don't need..because this file already is IP specific.
>>
>
> OK, We will update it.
>
>>> +
>>> + /* if clock is less than 25MHz, divided clock is used.
>>> + * For divided clock, we can use the standard sdhci_set_clock().
>>> + * For clock above 25MHz, DRP clock is used
>>> + * Here, we cannot use sdhci_set_clock(), we need to program
>>> + * TX RX CLOCK DCM DRP for appropriate clock
>>> + */
>>> +
>>> + vendor_ptr = sdhci_readw(host, SDHCI_UHS2_VENDOR);
>>> +
>>> + if (clock <= 25000000) {
>>> + /* Then call generic set_clock */
>>> + sdhci_ops->set_clock(host, clock);
>>> + } else {
>>> +
>>> + host->mmc->actual_clock = 0;
>>> +
>>> + /* Select un-phase shifted clock before reset Tx Tuning
>> DCM*/
>>> + reg = sdhci_readl(host, (SDHC_GPIO_OUT + vendor_ptr));
>>> + reg &= ~SDHC_TX_CLK_SEL_TUNED;
>>> + sdhci_writew(host, reg, (SDHC_GPIO_OUT + vendor_ptr));
>>> + mdelay(10);
>>> +
>>> + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
>>> +
>>> + if (clock == 0)
>>> + return;
>>
>> Why return here? Can this locate above?
>>
>
> We will remove it as it's dead code.
>
>>> +
>>> + /* Lets chose the Mulitplier value to be 0x2 */
>>> + mul = 0x2;
>>> + for (div = 1; div <= 32; div++) {
>>> + if (((host->max_clk * mul) / div)
>>> + <= clock)
>>> + break;
>>> + }
>>> + /*
>>> + * Set Programmable Clock Mode in the Clock
>>> + * Control register.
>>> + */
>>> + div_val = div - 1;
>>> + mul_val = mul - 1;
>>> +
>>> + host->mmc->actual_clock = (host->max_clk * mul) / div;
>>> + /* Program the DCM DRP */
>>> + /* Step 1: Assert DCM Reset */
>>
>> I think more readable that you write the all steps on here.
>> /*
>> * Step1 :...
>> * Step2 :...
>>
>
> OK, We will update it.
>
>>> + reg = sdhci_readl(host, (SDHC_GPIO_OUT + vendor_ptr));
>>> + reg = reg | SDHC_CARD_TX_CLK_DCM_RST;
>>
>> reg |= SDHC_CARD_...
>>
>
> OK, We will update it.
>
>>> + sdhci_writew(host, reg, (SDHC_GPIO_OUT + vendor_ptr));
>>> +
>>> + /* Step 2: Program the mul and div values in DRP */
>>> + mul_div_val = (mul_val << 8) | div_val;
>>> + sdhci_writew(host, mul_div_val,
>> TXRX_CLK_DCM_MUL_DIV_DRP);
>>> +
>>> + /* Step 3: issue a dummy read from DRP base 0x00 as per
>>> + *
>> www.xilinx.com/support/documentation/user_guides/ug191.pdf
>>> + */
>>> + reg = sdhci_readw(host, TXRX_CLK_DCM_DRP_BASE_51);
>>
>> This code is workaroud for issue?
>>
>
> This is not an issue.
> We use Dynamic Reconfigurable Port(DRP) of Xilinx Digital Clock Manager(DCM).
> To restore DCM status output, read from DRP base 0x00 is needed as per document
> mentioned.
>
>
>>> +
>>> + /* Step 4: De-Assert reset to DCM */
>>> + reg = sdhci_readl(host, (SDHC_GPIO_OUT + vendor_ptr));
>>> + reg &= ~SDHC_CARD_TX_CLK_DCM_RST;
>>> + sdhci_writew(host, reg, (SDHC_GPIO_OUT + vendor_ptr));
>>> +
>>> + clk |= SDHCI_PROG_CLOCK_MODE | SDHCI_CLOCK_INT_EN;
>>> + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
>>> +
>>> + /* Wait max 20 ms */
>>> + timeout = 20;
>>> + while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
>>> + & SDHCI_CLOCK_INT_STABLE)) {
>>> + if (timeout == 0) {
>>> + pr_err("%s: Internal clock never stabilised\n",
>>> + mmc_hostname(host->mmc));
>>> + return;
>>> + }
>>> + timeout--;
>>> + mdelay(1);
>>> + }
>>> +
>>> + clk |= SDHCI_CLOCK_CARD_EN;
>>> + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
>>> +
>>> + /* For some bit-files we may have to do phase shifting for
>>> + * Tx Clock; Let's do it here
>>> + */
>>> + reg = sdhci_readl(host, (SDHC_GPIO_OUT + vendor_ptr));
>>> + reg = reg | SDHC_TUNING_TX_CLK_DCM_RST;
>>
>> Ditto.
>>
>
> OK, We will update it.
>
>>> + sdhci_writew(host, reg, (SDHC_GPIO_OUT + vendor_ptr));
>>> + mdelay(10);
>>> + reg = sdhci_readl(host, (SDHC_GPIO_OUT + vendor_ptr));
>>> + reg &= ~SDHC_TUNING_TX_CLK_DCM_RST;
>>> + sdhci_writew(host, reg, (SDHC_GPIO_OUT + vendor_ptr));
>>> +
>>> + /* Select working phase value if clock is <= 50MHz */
>>> + if (clock <= 50000000) {
>>> + /*Change the Phase value here */
>>> + reg = sdhci_readl(host, (SDHC_GPIO_OUT +
>> vendor_ptr));
>>> + reg = reg | (SDHC_TUNING_TX_CLK_SEL_MASK &
>>> + (tx_clk_phase_val <<
>> SDHC_TUNING_TX_CLK_SEL_SHIFT));
>>> + sdhci_writew(host, reg, (SDHC_GPIO_OUT +
>> vendor_ptr));
>>> + mdelay(10);
>>> +
>>> + /* Program to select phase shifted clock */
>>> + reg = sdhci_readl(host, (SDHC_GPIO_OUT +
>> vendor_ptr));
>>> + reg = reg | SDHC_TX_CLK_SEL_TUNED;
>>> + sdhci_writew(host, reg, (SDHC_GPIO_OUT +
>> vendor_ptr));
>>> + mdelay(10);
>>> + }
>>> +
>>> + /* This Clock might have affected the RX CLOCK DCM
>>> + * used for Phase control; Reset this DCM for proper clock
>>> + */
>>> +
>>> + /* Program the DCM DRP */
>>> + /* Step 1: Reset the DCM */
>>> + reg = sdhci_readl(host, (SDHC_GPIO_OUT + vendor_ptr));
>>> + reg = reg | SDHC_TUNING_RX_CLK_DCM_RST;
>>> + sdhci_writew(host, reg, (SDHC_GPIO_OUT + vendor_ptr));
>>> + mdelay(10);
>>> + /* Step 2: De-Assert reset to DCM */
>>> + reg = sdhci_readl(host, (SDHC_GPIO_OUT + vendor_ptr));
>>> + reg &= ~SDHC_TUNING_RX_CLK_DCM_RST;
>>> + sdhci_writew(host, reg, (SDHC_GPIO_OUT + vendor_ptr));
>>
>> Can these codes reuse?
>
> We will modify the code to reuse it.
>
>> If clock is upper than 50MHz, then only this code is running twice..is it
>> correct?
>>
>
> No, We are handling Transmit clock control and Receive clock control here.
> The code under (clock<=50000000) is shifting the transmit clock phase as per board requirement.

Ok. i see.
I read something wrong. :) RX - TX.

Best Regards,
Jaehoon Chung

>
>
>>> +
>>> + /* For 50Mhz, tuning is not possible.. Lets fix the sampling
>>> + * Phase of Rx Clock here
>>> + */
>>> + if (clock <= 50000000) {
>>> + /*Change the Phase value here */
>>> + reg = sdhci_readl(host, (SDHC_DBOUNCE +
>> vendor_ptr));
>>> + reg &= ~SDHC_TUNING_RX_CLK_SEL_MASK;
>>> + reg = reg | (SDHC_TUNING_RX_CLK_SEL_MASK &
>>> + rx_clk_phase_val);
>>> + sdhci_writew(host, reg, (SDHC_DBOUNCE +
>> vendor_ptr));
>>> + }
>>> + mdelay(10);
>>> + }
>>> +}
>>> +
>>> +static int sdhci_pci_enable_dma_snps(struct sdhci_host *host) {
>>> + /* DWC MSHC Specific Dma Enabling */
>>
>> Ditto.. this file is already DWC specific.
>
> OK, We will update it.
>
>
>>> +
>>> + /* Call generic emable_dma */
>>> + return sdhci_ops->enable_dma(host);
>>> +}
>>> +
>>> +static void sdhci_pci_set_bus_width_snps(struct sdhci_host *host, int
>>> +width) {
>>> + /* DWC MSHC Specific Bus Width Setting */
>>
>> Ditto.
>
> OK, We will update it.
>
>>> +
>>> + /* Call generic set_bus_width */
>>> + sdhci_ops->set_bus_width(host, width); }
>>> +
>>> +static void sdhci_reset_snps(struct sdhci_host *host, u8 mask) {
>>> + /* DWC MSHC Specific hci reset */
>>> +
>>> + /* Call generic reset */
>>> + sdhci_ops->reset(host, mask);
>>> +}
>>> +static void sdhci_set_uhs_signaling_snps(struct sdhci_host *host,
>>> + unsigned int timing)
>>> +{
>>> + /* DWC MSHC Specific UHS-I Signaling */
>>> +
>>> + /* Call generic UHS-I signaling */
>>> + sdhci_ops->set_uhs_signaling(host, timing); } static void
>>> +sdhci_pci_hw_reset_snps(struct sdhci_host *host) {
>>> + /* DWC MSHC Specific hw reset */
>>> +
>>> + /* Call generic hw_reset */
>>> + if (host->ops && host->ops->hw_reset)
>>
>> Above other host->ops-> didn't check..do you think what's correct?
>>
>
> We missed it. We will modify the code considering Ludovic's comments.
>
>>> + sdhci_ops->hw_reset(host);
>>> +}
>>> +static const struct sdhci_ops sdhci_pci_ops_snps = {
>>> + .set_clock = sdhci_set_clock_snps,
>>> + .enable_dma = sdhci_pci_enable_dma_snps,
>>> + .set_bus_width = sdhci_pci_set_bus_width_snps,
>>> + .reset = sdhci_reset_snps,
>>> + .set_uhs_signaling = sdhci_set_uhs_signaling_snps,
>>> + .hw_reset = sdhci_pci_hw_reset_snps,
>>> +};
>>> +
>>> +static int snps_init_clock(struct sdhci_host *host) {
>>> + int div = 0;
>>> + int mul = 0;
>>> + int div_val = 0;
>>> + int mul_val = 0;
>>> + int mul_div_val = 0;
>>> + int reg = 0;
>>> + u32 vendor_ptr;
>>> +
>>> + vendor_ptr = sdhci_readw(host, SDHCI_UHS2_VENDOR);
>>> +
>>> + /* Configure the BCLK DRP to get 100 MHZ Clock */
>>> +
>>> + /* To get 100MHz from 100MHz input freq,
>>> + * mul=2 and div=2
>>> + * Formula: output_clock = (input clock * mul) / div
>>> + */
>>> + mul = 2;
>>> + div = 2;
>>> + mul_val = mul - 1;
>>> + div_val = div - 1;
>>> + /* Program the DCM DRP */
>>> + /* Step 1: Reset the DCM */
>>> + reg = sdhci_readl(host, (SDHC_GPIO_OUT + vendor_ptr));
>>> + reg = reg | SDHC_BCLK_DCM_RST;
>>> + sdhci_writew(host, reg, (SDHC_GPIO_OUT + vendor_ptr));
>>> + /* Step 2: Program the mul and div values in DRP */
>>> + mul_div_val = (mul_val << 8) | div_val;
>>> + sdhci_writew(host, mul_div_val, BCLK_DCM_MUL_DIV_DRP);
>>> + /* Step 3: issue a dummy read from DRP base 0x00 as per
>>> + *
>> http://www.xilinx.com/support/documentation/user_guides/ug191.pdf
>>> + */
>>> + reg = sdhci_readw(host, BCLK_DCM_DRP_BASE_51);
>>> + /* Step 4: De-Assert reset to DCM */
>>> + /* de assert reset*/
>>> + reg = sdhci_readl(host, (SDHC_GPIO_OUT + vendor_ptr));
>>> + reg &= ~SDHC_BCLK_DCM_RST;
>>> + sdhci_writew(host, reg, (SDHC_GPIO_OUT + vendor_ptr));
>>
>> I'm not sure but it seems these codes are also simliar to above codes.
>> If i'm correct, you can reduce the code lines.
>>
>
> It's to program the DCM which is used for base clock.
> We will try to optimize/reuse the code.
>
>>> +
>>> + /* By Default Clocks to MSHC are off..
>>> + * Before stack applies reset; we need to turn on the clock
>>> + */
>>> + sdhci_writew(host, SDHCI_CLOCK_INT_EN,
>> SDHCI_CLOCK_CONTROL);
>>> +
>>> + return 0;
>>> +
>>> +}
>>> +
>>> +int sdhci_pci_probe_slot_snps(struct sdhci_pci_slot *slot) {
>>> + int ret = 0;
>>> + struct sdhci_host *host;
>>> +
>>> + host = slot->host;
>>> + sdhci_ops = host->ops;
>>> + host->ops = &sdhci_pci_ops_snps;
>>> +
>>> + /* Board specific clock initialization */
>>> + ret = snps_init_clock(host);
>>> + return ret;
>>> +}
>>> +EXPORT_SYMBOL_GPL(sdhci_pci_probe_slot_snps);
>>> diff --git a/drivers/mmc/host/sdhci-dwc-mshc-pci.h
>>> b/drivers/mmc/host/sdhci-dwc-mshc-pci.h
>>> new file mode 100644
>>> index 0000000..1635bf2
>>> --- /dev/null
>>> +++ b/drivers/mmc/host/sdhci-dwc-mshc-pci.h
>>> @@ -0,0 +1,58 @@
>>> +/*
>>> + * Copyright (C) 2016 Synopsys, Inc.
>>> + *
>>> + * Author: Manjunath M B <manjumb@xxxxxxxxxxxx>
>>> + *
>>> + * This software is licensed under the terms of the GNU General
>>> +Public
>>> + * License version 2, as published by the Free Software Foundation,
>>> +and
>>> + * may be copied, distributed, and modified under those terms.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>> + * GNU General Public License for more details.
>>> + *
>>> + */
>>> +
>>> +#ifndef __SDHCI_DWC_MSHC_PCI_H__
>>> +#define __SDHCI_DWC_MSHC_PCI_H__
>>> +
>>> +#include "sdhci-pci.h"
>>> +
>>> +#define SDHCI_UHS2_VENDOR 0xE8
>>> +
>>> +#define DRIVER_NAME "sdhci_snps"
>>
>> What do you use .."snps" or "dwc_mshc"?
>> As i mentioned, i want not to use "*mshc*" in these patches.
>>
>
> We will use "sdhci-pci-dwc" to make it uniform.
>
>> Best Regards,
>> Jaehoon Chung
>>
>>> +#define SDHC_DEF_TX_CLK_PH_VAL 4
>>> +#define SDHC_DEF_RX_CLK_PH_VAL 4
>
> Thanks & Regards,
> Prabu Thangamuthu.
>
>
>