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. > > > -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html