diff mbox

mmc:host:sdhci-pci:V2-Addition of Arasan PCI controller with integrated phy.

Message ID 1507657369-2657-1-git-send-email-agarg@arasan.com (mailing list archive)
State New, archived
Headers show

Commit Message

Atul Garg Oct. 10, 2017, 5:42 p.m. UTC
The Arasan controller is based on a FPGA platform and has integrated phy
with specific phy registers used during the initialization and
management of different modes. The phy and the controller are integrated
and registers are very specific to Arasan.

Arasan being an IP provider, licenses these IPs to various companies for
integration of IP in custom SOCs. The custom SOCs define own register
map depending on how bits are tied inside the SOC for phy registers,
depending on SOC memory plan and hence will require own platform drivers.

If more details on phy registers are required, an interface document is
hosted at https://arasan.com/NF/eMMC5.1 PHY Programming in Linux.pdf.

Signed-off-by: Atul Garg <agarg@arasan.com>
---
 drivers/mmc/host/Makefile           |  18 +-
 drivers/mmc/host/sdhci-pci-arasan.c | 325 ++++++++++++++++++++++++++++++++++++
 drivers/mmc/host/sdhci-pci-arasan.h |  80 +++++++++
 drivers/mmc/host/sdhci-pci-core.c   |  16 ++
 drivers/mmc/host/sdhci-pci.h        |   4 +
 5 files changed, 431 insertions(+), 12 deletions(-)
 create mode 100644 drivers/mmc/host/sdhci-pci-arasan.c
 create mode 100644 drivers/mmc/host/sdhci-pci-arasan.h

Comments

Sekhar Nori Oct. 11, 2017, 2:23 p.m. UTC | #1
Hi Atul,

On Tuesday 10 October 2017 11:12 PM, Atul Garg wrote:
> The Arasan controller is based on a FPGA platform and has integrated phy
> with specific phy registers used during the initialization and
> management of different modes. The phy and the controller are integrated
> and registers are very specific to Arasan.
> 
> Arasan being an IP provider, licenses these IPs to various companies for
> integration of IP in custom SOCs. The custom SOCs define own register
> map depending on how bits are tied inside the SOC for phy registers,
> depending on SOC memory plan and hence will require own platform drivers.
> 
> If more details on phy registers are required, an interface document is
> hosted at https://arasan.com/NF/eMMC5.1 PHY Programming in Linux.pdf.
> 
> Signed-off-by: Atul Garg <agarg@arasan.com>
> ---
>  drivers/mmc/host/Makefile           |  18 +-
>  drivers/mmc/host/sdhci-pci-arasan.c | 325 ++++++++++++++++++++++++++++++++++++
>  drivers/mmc/host/sdhci-pci-arasan.h |  80 +++++++++
>  drivers/mmc/host/sdhci-pci-core.c   |  16 ++
>  drivers/mmc/host/sdhci-pci.h        |   4 +
>  5 files changed, 431 insertions(+), 12 deletions(-)
>  create mode 100644 drivers/mmc/host/sdhci-pci-arasan.c
>  create mode 100644 drivers/mmc/host/sdhci-pci-arasan.h
> 
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index 2b5a813..b9b2c6c 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -2,15 +2,14 @@
>  # Makefile for MMC/SD host controller drivers
>  #
>  
> -obj-$(CONFIG_MMC_ARMMMCI) += armmmci.o
> -armmmci-y := mmci.o
> -armmmci-$(CONFIG_MMC_QCOM_DML) += mmci_qcom_dml.o
> +obj-$(CONFIG_MMC_ARMMMCI)	+= mmci.o
> +obj-$(CONFIG_MMC_QCOM_DML)	+= mmci_qcom_dml.o

These look like stray changes unrelated to this patch?

>  obj-$(CONFIG_MMC_PXA)		+= pxamci.o
>  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-pci-arasan.o
>  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
> @@ -37,13 +36,9 @@ obj-$(CONFIG_MMC_S3C)   	+= s3cmci.o
>  obj-$(CONFIG_MMC_SDRICOH_CS)	+= sdricoh_cs.o
>  obj-$(CONFIG_MMC_TMIO)		+= tmio_mmc.o
>  obj-$(CONFIG_MMC_TMIO_CORE)	+= tmio_mmc_core.o
> -obj-$(CONFIG_MMC_SDHI)		+= renesas_sdhi_core.o
> -ifeq ($(subst m,y,$(CONFIG_MMC_SDHI_SYS_DMAC)),y)
> -obj-$(CONFIG_MMC_SDHI)		+= renesas_sdhi_sys_dmac.o
> -endif
> -ifeq ($(subst m,y,$(CONFIG_MMC_SDHI_INTERNAL_DMAC)),y)
> -obj-$(CONFIG_MMC_SDHI)		+= renesas_sdhi_internal_dmac.o
> -endif
> +tmio_mmc_core-y			:= tmio_mmc_pio.o
> +tmio_mmc_core-$(subst m,y,$(CONFIG_MMC_SDHI))	+= tmio_mmc_dma.o
> +obj-$(CONFIG_MMC_SDHI)		+= sh_mobile_sdhi.o

These too look unrelated to $SUBJECT

>  obj-$(CONFIG_MMC_CB710)		+= cb710-mmc.o
>  obj-$(CONFIG_MMC_VIA_SDMMC)	+= via-sdmmc.o
>  obj-$(CONFIG_SDH_BFIN)		+= bfin_sdh.o
> @@ -89,7 +84,6 @@ obj-$(CONFIG_MMC_SDHCI_MSM)		+= sdhci-msm.o
>  obj-$(CONFIG_MMC_SDHCI_ST)		+= sdhci-st.o
>  obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32)	+= sdhci-pic32.o
>  obj-$(CONFIG_MMC_SDHCI_BRCMSTB)		+= sdhci-brcmstb.o
> -obj-$(CONFIG_MMC_SDHCI_OMAP)		+= sdhci-omap.o

This too is unrelated.

>  
>  ifeq ($(CONFIG_CB710_DEBUG),y)
>  	CFLAGS-cb710-mmc	+= -DDEBUG
> diff --git a/drivers/mmc/host/sdhci-pci-arasan.c b/drivers/mmc/host/sdhci-pci-arasan.c
> new file mode 100644
> index 0000000..57fbf8f
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-pci-arasan.c
> @@ -0,0 +1,325 @@
> +/*
> + * Copyright (C) 2017 Arasan Chip Systems Inc.,
> + *
> + * Authors: Atul Garg <agarg@arasan.com>
> + *
> + * 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 "sdhci.h"
> +#include "sdhci-pci.h"
> +#include "sdhci-pci-arasan.h"
> +
> +static int arasan_phy_write(struct sdhci_host *host, u8 data, u8 offset)
> +{
> +	u32 timeout;
> +	u8 busy;
> +
> +	sdhci_writew(host, data, HOST_PHY_DATA_REG);
> +	sdhci_writew(host, ((1<<8) | offset), HOST_PHY_ADDR_REG);
> +	timeout = 20;
> +	do {
> +		busy = sdhci_readw(host, HOST_PHY_ADDR_REG) & (1<<9);
> +		if (!busy)
> +			break;
> +		mdelay(1);
> +	} while (timeout--);
> +	if (!timeout)
> +		return -ENODEV;
> +	return 0;
> +}
> +
> +static int arasan_phy_read(struct sdhci_host *host, u8 offset, u8 *data)
> +{
> +	u32 timeout;
> +	u8 busy;
> +
> +	sdhci_writew(host, 0, HOST_PHY_DATA_REG);
> +	*data = sdhci_readw(host, HOST_PHY_DATA_REG) & 0xFF;

Is this a superfluous read to make sure the write before really took
effect? If yes, a comment would be useful and you dont have to gather
the result of it and you can drop the masking too.

> +	sdhci_writew(host, ((0<<8) | offset), HOST_PHY_ADDR_REG);

The 0 << 8 is not needed.

> +	timeout = 20;

This can be initialized at declaration.

> +	do {
> +		busy = sdhci_readw(host, HOST_PHY_ADDR_REG) & (1<<9);
> +		if (!busy)
> +			break;
> +		mdelay(1);

Do you really mean to wait for 1 millisecond here? Or us udelay(1)
sufficient? Unfortunately section 4 of the document you referenced gives
no information on the timing involved, but presumably the hardware is
much faster than needing upto 20 milliseconds for reading a register.

> +	} while (timeout--);
> +	if (!timeout)
> +		return -ENODEV;
> +	*data = sdhci_readw(host, HOST_PHY_DATA_REG) & 0xFF;
> +	return 0;
> +}
> +
> +/* Initialize the Arasan PHY */
> +static int arasan_phy_init(struct sdhci_host *host)
> +{
> +	struct sdhci_pci_slot *slot = sdhci_priv(host);
> +	struct pci_dev *pdev = slot->chip->pdev;
> +	u8 val;
> +
> +	if (arasan_phy_write(host, 0, PHY_CONTROL))
> +		return -ENODEV;

Section 5 of the document you linked to says the first step is:

"
If operating in 4-bit PHY mode, write 1’b1 to phyCONTROL_select (bit 0)
bit of reg_phyCONTROL_ctrl register (offset 0x24).If operating in 8-bit
mode this register write is NOT required.
"

Are you in 4-bit or 8-bit mode for the PCIe card you are testing with?
In any case, clearing PHY_CONTROL to 0 upfront seems to go against the
Arasan specification.

> +	if (arasan_phy_read(host, PHY_IOPADCONTROL1, &val))
> +		return -ENODEV;

The second step per the document is to enable DLL (page 24). This seems
to be doing something different. Am I misreading the document?

Also, in general, it is not good to ignore and override the return value
from a function. So you would rather do.

	ret = arasan_phy_read(host, PHY_IOPADCONTROL1, &val);
	if (ret)
		return ret;

to preserve the return value.

I have ignored reviewing the rest of the initialization sequence since I
seem to be missing something. Also, at the end of the initialization
sequence there is a table with values to be written to and read back
from the PHY. Is that a summary of the initialization sequence or
something that needs to be done after follow through of the steps before
that. If its the later, curious as to why its tabled separately.

Also, looks like the document really is not consistent in terms of
naming registers. For example, the document refers to IO_PADS_CTRL1 in
the table on page 25, but searching for it leads you nowwhere. It will
also be nice if you follow the same convention as the hardware document
in code..

> +	if (arasan_phy_write(host, val | RETB_ENABLE, PHY_IOPADCONTROL1))

.. so call this IO_PADS_CTRL1 instead of PHY_IOPADCONTROL1?

> +		return -ENODEV;
> +	if (arasan_phy_read(host, PHY_IOPADCONTROL2, &val))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, val | RTRIM_ENABLE, PHY_IOPADCONTROL2))
> +		return -ENODEV;
> +	mdelay(10);
> +
> +	if (arasan_phy_read(host, PHY_IOPADSTATUS, &val)) {
> +		if (!(val & CALDONE_MASK)) {
> +			dev_err(&pdev->dev, "Phy calibration not done\n");
> +			return -ENODEV;
> +		}
> +	}
> +	if (arasan_phy_read(host, PHY_IOPADCONTROL1, &val))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, val | PDB_ENABLE, PHY_IOPADCONTROL1))
> +		return -ENODEV;
> +	mdelay(10);
> +
> +	if (arasan_phy_read(host, PHY_IOPADSTATUS, &val)) {
> +		if (!(val & CALDONE_MASK)) {
> +			dev_err(&pdev->dev, "Phy calibration not done\n");
> +			return -ENODEV;
> +		}
> +	}
> +	if (arasan_phy_read(host, PHY_IORENCONTROL1, &val))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, val | REN_CMD | REN_STRB, PHY_IORENCONTROL1))
> +		return -ENODEV;
> +	if (arasan_phy_read(host, PHY_IOPUCONTROL1, &val))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, val | PU_CMD, PHY_IOPUCONTROL1))
> +		return -ENODEV;
> +	if (arasan_phy_read(host, PHY_CMDCONTROL, &val))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, val | PDB_CMD, PHY_CMDCONTROL))
> +		return -ENODEV;
> +	if (arasan_phy_read(host, PHY_IORENCONTROL2, &val))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, val | REN_DAT, PHY_IORENCONTROL2))
> +		return -ENODEV;
> +	if (arasan_phy_read(host, PHY_IOPUCONTROL2, &val))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, val | PU_DAT, PHY_IOPUCONTROL2))
> +		return -ENODEV;
> +	if (arasan_phy_read(host, PHY_DATACONTROL, &val))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, val | PDB_DAT, PHY_DATACONTROL))
> +		return -ENODEV;
> +	if (arasan_phy_read(host, PHY_STROBECONTROL, &val))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, val | PDB_STRB, PHY_STROBECONTROL))
> +		return -ENODEV;
> +	if (arasan_phy_read(host, PHY_CLKCONTROL, &val))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, val | PDB_CLK, PHY_CLKCONTROL))
> +		return -ENODEV;
> +	if (arasan_phy_read(host, PHY_CLKBUFSELECT, &val))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, val | 0x7, PHY_CLKBUFSELECT))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, 0x4, PHY_MODECONTROL))
> +		return -ENODEV;
> +	return 0;
> +}
> +
> +
> +static int arasan_set_phy(struct sdhci_host *host)
> +{
> +	u8 clk_sel;
> +	static u32 chg_clk;
> +	u8 val;
> +	u8 otap, itap, dll_sts, io_pad;
> +
> +	if (chg_clk != host->mmc->ios.clock) {
> +		chg_clk = host->mmc->ios.clock;
> +		if (host->mmc->ios.clock == 200000000)
> +			clk_sel = 0;
> +		else if (host->mmc->ios.clock == 100000000)
> +			clk_sel = 2;
> +		else if (host->mmc->ios.clock == 50000000)
> +			clk_sel = 1;
> +		else
> +			clk_sel = 0;
> +		/* Change phy settings only if there is a change in clock */
> +		goto set_phy;

This goto is not really needed.

> +	} else
> +		return 0;

In fact, the code will be much easier to read if you do:

	if (chg_clk == host->mmc->ios.clock)
		return 0;

	/* rest of logic follows */

> +set_phy:
> +	if (host->mmc_host_ops.hs400_enhanced_strobe) {
> +		if (arasan_phy_write(host, 0x1, PHY_MODECONTROL))
> +			return -ENODEV;
> +		otap = ((0x2<<1) | OTAP_DLY_EN);

I think such code will be easier to read if you define a macro of the sort:

#define OTAP_DELAY(x)	((x) << 1)

and then do:

		otap = OTAP_DELAY(0x2) | OTAP_DLY_EN;

> +		if (arasan_phy_write(host, otap, PHY_OUTPUTTAPDELAY))
> +			return -ENODEV;
> +		if (arasan_phy_write(host, 0, PHY_INPUTTAPDELAY))
> +			return -ENODEV;
> +		if (arasan_phy_write(host, DLL_TRIM, PHY_DLLTRIM))
> +			return -ENODEV;
> +		if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
> +			return -ENODEV;
> +		dll_sts = (clk_sel<<5) | DLL_ENABLE;

Similarly here and in other places throughout the patch.

> +		if (arasan_phy_write(host, dll_sts, PHY_DLLCONTROL))
> +			return -ENODEV;
> +		if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
> +			return -ENODEV;
> +	} else {
> +		switch (host->mmc->ios.timing) {
> +		case MMC_TIMING_LEGACY:
> +			if (arasan_phy_write(host, 0x4, PHY_MODECONTROL))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, 0, PHY_INPUTTAPDELAY))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, 0, PHY_OUTPUTTAPDELAY))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
> +				return -ENODEV;
> +			if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
> +				return -ENODEV;
> +			break;
> +		case MMC_TIMING_MMC_HS:
> +			if (arasan_phy_write(host, 0, PHY_MODECONTROL))
> +				return -ENODEV;
> +			otap = ((0x2<<3) | OTAP_DLY_EN);
> +			if (arasan_phy_write(host, otap, PHY_OUTPUTTAPDELAY))
> +				return -ENODEV;
> +			itap = ((0x2<<1) | ITAP_DLY_EN);
> +			if (arasan_phy_write(host, itap, PHY_INPUTTAPDELAY))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, 0, PHY_INPUTTAPDELAY))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, DLL_TRIM, PHY_DLLTRIM))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
> +				return -ENODEV;
> +			dll_sts = (clk_sel<<5) | DLL_ENABLE;
> +			if (arasan_phy_write(host, dll_sts, PHY_DLLCONTROL))
> +				return -ENODEV;
> +			if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
> +				return -ENODEV;
> +			break;
> +		case MMC_TIMING_MMC_HS200:
> +			if (arasan_phy_write(host, 0, PHY_MODECONTROL))
> +				return -ENODEV;
> +			if (arasan_phy_read(host, PHY_IOPADCONTROL1, &val))
> +				return -ENODEV;
> +			io_pad = val | (host->mmc->ios.drv_type<<2);
> +			if (arasan_phy_write(host, io_pad, PHY_IOPADCONTROL1))
> +				return -ENODEV;
> +			otap = ((0x2<<1) | OTAP_DLY_EN);
> +			if (arasan_phy_write(host, otap, PHY_OUTPUTTAPDELAY))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, 0, PHY_INPUTTAPDELAY))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, DLL_TRIM, PHY_DLLTRIM))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
> +				return -ENODEV;
> +			dll_sts = (clk_sel<<5) | DLL_ENABLE;
> +			if (arasan_phy_write(host, dll_sts, PHY_DLLCONTROL))
> +				return -ENODEV;
> +			if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
> +				return -ENODEV;
> +			if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
> +				return -ENODEV;
> +			break;
> +		case MMC_TIMING_UHS_DDR50:
> +			if (arasan_phy_write(host, 0x8, PHY_MODECONTROL))
> +				return -ENODEV;
> +			otap = ((0x2<<3) | OTAP_DLY_EN);

Is the shift by 3 a typo? If the idea was to set SEL_DLY_TXCLK, you
should be doing BIT(5) instead.

> +			if (arasan_phy_write(host, otap, PHY_OUTPUTTAPDELAY))
> +				return -ENODEV;
> +			itap = ((0x2<<1) | ITAP_DLY_EN);
> +			if (arasan_phy_write(host, itap, PHY_INPUTTAPDELAY))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, DLL_TRIM, PHY_DLLTRIM))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
> +				return -ENODEV;
> +			dll_sts = (clk_sel<<5) | DLL_ENABLE;
> +			if (arasan_phy_write(host, dll_sts, PHY_DLLCONTROL))
> +				return -ENODEV;
> +			if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
> +				return -ENODEV;
> +			break;
> +		case MMC_TIMING_MMC_HS400:
> +			if (arasan_phy_write(host, 0x2, PHY_MODECONTROL))
> +				return -ENODEV;
> +			if (arasan_phy_read(host, PHY_IOPADCONTROL1, &val))
> +				return -ENODEV;
> +			io_pad = val | (host->mmc->ios.drv_type<<2);
> +			if (arasan_phy_write(host, io_pad, PHY_IOPADCONTROL1))
> +				return -ENODEV;
> +			otap = ((0x2<<1) | OTAP_DLY_EN);
> +			if (arasan_phy_write(host, otap, PHY_OUTPUTTAPDELAY))
> +				return -ENODEV;
> +			itap = ((0xA<<1) | ITAP_DLY_EN);
> +			if (arasan_phy_write(host, itap, PHY_INPUTTAPDELAY))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, DLL_TRIM, PHY_DLLTRIM))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
> +				return -ENODEV;
> +			dll_sts = (clk_sel<<5) | DLL_ENABLE;
> +			if (arasan_phy_write(host, dll_sts, PHY_DLLCONTROL))
> +				return -ENODEV;
> +			if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, 0x0E, PHY_CLKBUFSELECT))
> +				return -ENODEV;

There is no setp to write PHY_CLKBUFSELECT for HS400 in the document
(pages 26-27)?

Thanks,
Sekhar
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Atul Garg Oct. 13, 2017, 2:22 a.m. UTC | #2
Hi Sekhar,
Thanks for comments.

On Wed, Oct 11, 2017 at 7:23 AM, Sekhar Nori <nsekhar@ti.com> wrote:
> Hi Atul,
>
> On Tuesday 10 October 2017 11:12 PM, Atul Garg wrote:
>> The Arasan controller is based on a FPGA platform and has integrated phy
>> with specific phy registers used during the initialization and
>> management of different modes. The phy and the controller are integrated
>> and registers are very specific to Arasan.
>>
>> Arasan being an IP provider, licenses these IPs to various companies for
>> integration of IP in custom SOCs. The custom SOCs define own register
>> map depending on how bits are tied inside the SOC for phy registers,
>> depending on SOC memory plan and hence will require own platform drivers.
>>
>> If more details on phy registers are required, an interface document is
>> hosted at https://arasan.com/NF/eMMC5.1 PHY Programming in Linux.pdf.
>>
>> Signed-off-by: Atul Garg <agarg@arasan.com>
>> ---
>>  drivers/mmc/host/Makefile           |  18 +-
>>  drivers/mmc/host/sdhci-pci-arasan.c | 325 ++++++++++++++++++++++++++++++++++++
>>  drivers/mmc/host/sdhci-pci-arasan.h |  80 +++++++++
>>  drivers/mmc/host/sdhci-pci-core.c   |  16 ++
>>  drivers/mmc/host/sdhci-pci.h        |   4 +
>>  5 files changed, 431 insertions(+), 12 deletions(-)
>>  create mode 100644 drivers/mmc/host/sdhci-pci-arasan.c
>>  create mode 100644 drivers/mmc/host/sdhci-pci-arasan.h
>>
>> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
>> index 2b5a813..b9b2c6c 100644
>> --- a/drivers/mmc/host/Makefile
>> +++ b/drivers/mmc/host/Makefile
>> @@ -2,15 +2,14 @@
>>  # Makefile for MMC/SD host controller drivers
>>  #
>>
>> -obj-$(CONFIG_MMC_ARMMMCI) += armmmci.o
>> -armmmci-y := mmci.o
>> -armmmci-$(CONFIG_MMC_QCOM_DML) += mmci_qcom_dml.o
>> +obj-$(CONFIG_MMC_ARMMMCI)    += mmci.o
>> +obj-$(CONFIG_MMC_QCOM_DML)   += mmci_qcom_dml.o
>
> These look like stray changes unrelated to this patch?

Yes will take care of them

>
>>  obj-$(CONFIG_MMC_PXA)                += pxamci.o
>>  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-pci-arasan.o
>>  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
>> @@ -37,13 +36,9 @@ obj-$(CONFIG_MMC_S3C)      += s3cmci.o
>>  obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o
>>  obj-$(CONFIG_MMC_TMIO)               += tmio_mmc.o
>>  obj-$(CONFIG_MMC_TMIO_CORE)  += tmio_mmc_core.o
>> -obj-$(CONFIG_MMC_SDHI)               += renesas_sdhi_core.o
>> -ifeq ($(subst m,y,$(CONFIG_MMC_SDHI_SYS_DMAC)),y)
>> -obj-$(CONFIG_MMC_SDHI)               += renesas_sdhi_sys_dmac.o
>> -endif
>> -ifeq ($(subst m,y,$(CONFIG_MMC_SDHI_INTERNAL_DMAC)),y)
>> -obj-$(CONFIG_MMC_SDHI)               += renesas_sdhi_internal_dmac.o
>> -endif
>> +tmio_mmc_core-y                      := tmio_mmc_pio.o
>> +tmio_mmc_core-$(subst m,y,$(CONFIG_MMC_SDHI))        += tmio_mmc_dma.o
>> +obj-$(CONFIG_MMC_SDHI)               += sh_mobile_sdhi.o
>
> These too look unrelated to $SUBJECT

will take care of them

>
>>  obj-$(CONFIG_MMC_CB710)              += cb710-mmc.o
>>  obj-$(CONFIG_MMC_VIA_SDMMC)  += via-sdmmc.o
>>  obj-$(CONFIG_SDH_BFIN)               += bfin_sdh.o
>> @@ -89,7 +84,6 @@ obj-$(CONFIG_MMC_SDHCI_MSM)         += sdhci-msm.o
>>  obj-$(CONFIG_MMC_SDHCI_ST)           += sdhci-st.o
>>  obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32)      += sdhci-pic32.o
>>  obj-$(CONFIG_MMC_SDHCI_BRCMSTB)              += sdhci-brcmstb.o
>> -obj-$(CONFIG_MMC_SDHCI_OMAP)         += sdhci-omap.o
>
> This too is unrelated.

will take care of them

>
>>
>>  ifeq ($(CONFIG_CB710_DEBUG),y)
>>       CFLAGS-cb710-mmc        += -DDEBUG
>> diff --git a/drivers/mmc/host/sdhci-pci-arasan.c b/drivers/mmc/host/sdhci-pci-arasan.c
>> new file mode 100644
>> index 0000000..57fbf8f
>> --- /dev/null
>> +++ b/drivers/mmc/host/sdhci-pci-arasan.c
>> @@ -0,0 +1,325 @@
>> +/*
>> + * Copyright (C) 2017 Arasan Chip Systems Inc.,
>> + *
>> + * Authors: Atul Garg <agarg@arasan.com>
>> + *
>> + * 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 "sdhci.h"
>> +#include "sdhci-pci.h"
>> +#include "sdhci-pci-arasan.h"
>> +
>> +static int arasan_phy_write(struct sdhci_host *host, u8 data, u8 offset)
>> +{
>> +     u32 timeout;
>> +     u8 busy;
>> +
>> +     sdhci_writew(host, data, HOST_PHY_DATA_REG);
>> +     sdhci_writew(host, ((1<<8) | offset), HOST_PHY_ADDR_REG);
>> +     timeout = 20;
>> +     do {
>> +             busy = sdhci_readw(host, HOST_PHY_ADDR_REG) & (1<<9);
>> +             if (!busy)
>> +                     break;
>> +             mdelay(1);
>> +     } while (timeout--);
>> +     if (!timeout)
>> +             return -ENODEV;
>> +     return 0;
>> +}
>> +
>> +static int arasan_phy_read(struct sdhci_host *host, u8 offset, u8 *data)
>> +{
>> +     u32 timeout;
>> +     u8 busy;
>> +
>> +     sdhci_writew(host, 0, HOST_PHY_DATA_REG);
>> +     *data = sdhci_readw(host, HOST_PHY_DATA_REG) & 0xFF;
>
> Is this a superfluous read to make sure the write before really took
> effect? If yes, a comment would be useful and you dont have to gather
> the result of it and you can drop the masking too.
>

Yes the read is to make sure the write before has effect. Anyway
removing it as the read value is not checked.

>> +     sdhci_writew(host, ((0<<8) | offset), HOST_PHY_ADDR_REG);
>
> The 0 << 8 is not needed.
>

will take care of it

>> +     timeout = 20;

will take care of it

>
> This can be initialized at declaration.
>
>> +     do {
>> +             busy = sdhci_readw(host, HOST_PHY_ADDR_REG) & (1<<9);
>> +             if (!busy)
>> +                     break;
>> +             mdelay(1);
>
> Do you really mean to wait for 1 millisecond here? Or us udelay(1)
> sufficient? Unfortunately section 4 of the document you referenced gives
> no information on the timing involved, but presumably the hardware is
> much faster than needing upto 20 milliseconds for reading a register.
>

The platform needs some time to settle down we will revisit it with
our team members and see what we can do about this

>> +     } while (timeout--);
>> +     if (!timeout)
>> +             return -ENODEV;
>> +     *data = sdhci_readw(host, HOST_PHY_DATA_REG) & 0xFF;
>> +     return 0;
>> +}
>> +
>> +/* Initialize the Arasan PHY */
>> +static int arasan_phy_init(struct sdhci_host *host)
>> +{
>> +     struct sdhci_pci_slot *slot = sdhci_priv(host);
>> +     struct pci_dev *pdev = slot->chip->pdev;
>> +     u8 val;
>> +
>> +     if (arasan_phy_write(host, 0, PHY_CONTROL))
>> +             return -ENODEV;
>
> Section 5 of the document you linked to says the first step is:
>
> "
> If operating in 4-bit PHY mode, write 1’b1 to phyCONTROL_select (bit 0)
> bit of reg_phyCONTROL_ctrl register (offset 0x24).If operating in 8-bit
> mode this register write is NOT required.
> "
>
> Are you in 4-bit or 8-bit mode for the PCIe card you are testing with?
> In any case, clearing PHY_CONTROL to 0 upfront seems to go against the
> Arasan specification.
>

8bit mode. As it defaults to 0 anyway, programming againis not required

>> +     if (arasan_phy_read(host, PHY_IOPADCONTROL1, &val))
>> +             return -ENODEV;
>
> The second step per the document is to enable DLL (page 24). This seems
> to be doing something different. Am I misreading the document?
>
> Also, in general, it is not good to ignore and override the return value
> from a function. So you would rather do.
>
>         ret = arasan_phy_read(host, PHY_IOPADCONTROL1, &val);
>         if (ret)
>                 return ret;
>
> to preserve the return value.
>
> I have ignored reviewing the rest of the initialization sequence since I
> seem to be missing something. Also, at the end of the initialization
> sequence there is a table with values to be written to and read back
> from the PHY. Is that a summary of the initialization sequence or
> something that needs to be done after follow through of the steps before
> that. If its the later, curious as to why its tabled separately.
>
> Also, looks like the document really is not consistent in terms of
> naming registers. For example, the document refers to IO_PADS_CTRL1 in
> the table on page 25, but searching for it leads you nowwhere. It will
> also be nice if you follow the same convention as the hardware document
> in code..
>
>> +     if (arasan_phy_write(host, val | RETB_ENABLE, PHY_IOPADCONTROL1))
>
> .. so call this IO_PADS_CTRL1 instead of PHY_IOPADCONTROL1?
>

we will update the document

>> +             return -ENODEV;
>> +     if (arasan_phy_read(host, PHY_IOPADCONTROL2, &val))
>> +             return -ENODEV;
>> +     if (arasan_phy_write(host, val | RTRIM_ENABLE, PHY_IOPADCONTROL2))
>> +             return -ENODEV;
>> +     mdelay(10);
>> +
>> +     if (arasan_phy_read(host, PHY_IOPADSTATUS, &val)) {
>> +             if (!(val & CALDONE_MASK)) {
>> +                     dev_err(&pdev->dev, "Phy calibration not done\n");
>> +                     return -ENODEV;
>> +             }
>> +     }
>> +     if (arasan_phy_read(host, PHY_IOPADCONTROL1, &val))
>> +             return -ENODEV;
>> +     if (arasan_phy_write(host, val | PDB_ENABLE, PHY_IOPADCONTROL1))
>> +             return -ENODEV;
>> +     mdelay(10);
>> +
>> +     if (arasan_phy_read(host, PHY_IOPADSTATUS, &val)) {
>> +             if (!(val & CALDONE_MASK)) {
>> +                     dev_err(&pdev->dev, "Phy calibration not done\n");
>> +                     return -ENODEV;
>> +             }
>> +     }
>> +     if (arasan_phy_read(host, PHY_IORENCONTROL1, &val))
>> +             return -ENODEV;
>> +     if (arasan_phy_write(host, val | REN_CMD | REN_STRB, PHY_IORENCONTROL1))
>> +             return -ENODEV;
>> +     if (arasan_phy_read(host, PHY_IOPUCONTROL1, &val))
>> +             return -ENODEV;
>> +     if (arasan_phy_write(host, val | PU_CMD, PHY_IOPUCONTROL1))
>> +             return -ENODEV;
>> +     if (arasan_phy_read(host, PHY_CMDCONTROL, &val))
>> +             return -ENODEV;
>> +     if (arasan_phy_write(host, val | PDB_CMD, PHY_CMDCONTROL))
>> +             return -ENODEV;
>> +     if (arasan_phy_read(host, PHY_IORENCONTROL2, &val))
>> +             return -ENODEV;
>> +     if (arasan_phy_write(host, val | REN_DAT, PHY_IORENCONTROL2))
>> +             return -ENODEV;
>> +     if (arasan_phy_read(host, PHY_IOPUCONTROL2, &val))
>> +             return -ENODEV;
>> +     if (arasan_phy_write(host, val | PU_DAT, PHY_IOPUCONTROL2))
>> +             return -ENODEV;
>> +     if (arasan_phy_read(host, PHY_DATACONTROL, &val))
>> +             return -ENODEV;
>> +     if (arasan_phy_write(host, val | PDB_DAT, PHY_DATACONTROL))
>> +             return -ENODEV;
>> +     if (arasan_phy_read(host, PHY_STROBECONTROL, &val))
>> +             return -ENODEV;
>> +     if (arasan_phy_write(host, val | PDB_STRB, PHY_STROBECONTROL))
>> +             return -ENODEV;
>> +     if (arasan_phy_read(host, PHY_CLKCONTROL, &val))
>> +             return -ENODEV;
>> +     if (arasan_phy_write(host, val | PDB_CLK, PHY_CLKCONTROL))
>> +             return -ENODEV;
>> +     if (arasan_phy_read(host, PHY_CLKBUFSELECT, &val))
>> +             return -ENODEV;
>> +     if (arasan_phy_write(host, val | 0x7, PHY_CLKBUFSELECT))
>> +             return -ENODEV;
>> +     if (arasan_phy_write(host, 0x4, PHY_MODECONTROL))
>> +             return -ENODEV;
>> +     return 0;
>> +}
>> +
>> +
>> +static int arasan_set_phy(struct sdhci_host *host)
>> +{
>> +     u8 clk_sel;
>> +     static u32 chg_clk;
>> +     u8 val;
>> +     u8 otap, itap, dll_sts, io_pad;
>> +
>> +     if (chg_clk != host->mmc->ios.clock) {
>> +             chg_clk = host->mmc->ios.clock;
>> +             if (host->mmc->ios.clock == 200000000)
>> +                     clk_sel = 0;
>> +             else if (host->mmc->ios.clock == 100000000)
>> +                     clk_sel = 2;
>> +             else if (host->mmc->ios.clock == 50000000)
>> +                     clk_sel = 1;
>> +             else
>> +                     clk_sel = 0;
>> +             /* Change phy settings only if there is a change in clock */
>> +             goto set_phy;
>
> This goto is not really needed.
>
>> +     } else
>> +             return 0;
>
> In fact, the code will be much easier to read if you do:
>
>         if (chg_clk == host->mmc->ios.clock)
>                 return 0;
>
>         /* rest of logic follows */
>

will take care of it

>> +set_phy:
>> +     if (host->mmc_host_ops.hs400_enhanced_strobe) {
>> +             if (arasan_phy_write(host, 0x1, PHY_MODECONTROL))
>> +                     return -ENODEV;
>> +             otap = ((0x2<<1) | OTAP_DLY_EN);
>
> I think such code will be easier to read if you define a macro of the sort:
>
> #define OTAP_DELAY(x)   ((x) << 1)
>
> and then do:
>
>                 otap = OTAP_DELAY(0x2) | OTAP_DLY_EN;
>
>> +             if (arasan_phy_write(host, otap, PHY_OUTPUTTAPDELAY))
>> +                     return -ENODEV;
>> +             if (arasan_phy_write(host, 0, PHY_INPUTTAPDELAY))
>> +                     return -ENODEV;
>> +             if (arasan_phy_write(host, DLL_TRIM, PHY_DLLTRIM))
>> +                     return -ENODEV;
>> +             if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
>> +                     return -ENODEV;
>> +             dll_sts = (clk_sel<<5) | DLL_ENABLE;
>
> Similarly here and in other places throughout the patch.
>

will take care of it after looking at other places too

>> +             if (arasan_phy_write(host, dll_sts, PHY_DLLCONTROL))
>> +                     return -ENODEV;
>> +             if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
>> +                     return -ENODEV;
>> +     } else {
>> +             switch (host->mmc->ios.timing) {
>> +             case MMC_TIMING_LEGACY:
>> +                     if (arasan_phy_write(host, 0x4, PHY_MODECONTROL))
>> +                             return -ENODEV;
>> +                     if (arasan_phy_write(host, 0, PHY_INPUTTAPDELAY))
>> +                             return -ENODEV;
>> +                     if (arasan_phy_write(host, 0, PHY_OUTPUTTAPDELAY))
>> +                             return -ENODEV;
>> +                     if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
>> +                             return -ENODEV;
>> +                     if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
>> +                             return -ENODEV;
>> +                     break;
>> +             case MMC_TIMING_MMC_HS:
>> +                     if (arasan_phy_write(host, 0, PHY_MODECONTROL))
>> +                             return -ENODEV;
>> +                     otap = ((0x2<<3) | OTAP_DLY_EN);
>> +                     if (arasan_phy_write(host, otap, PHY_OUTPUTTAPDELAY))
>> +                             return -ENODEV;
>> +                     itap = ((0x2<<1) | ITAP_DLY_EN);
>> +                     if (arasan_phy_write(host, itap, PHY_INPUTTAPDELAY))
>> +                             return -ENODEV;
>> +                     if (arasan_phy_write(host, 0, PHY_INPUTTAPDELAY))
>> +                             return -ENODEV;
>> +                     if (arasan_phy_write(host, DLL_TRIM, PHY_DLLTRIM))
>> +                             return -ENODEV;
>> +                     if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
>> +                             return -ENODEV;
>> +                     dll_sts = (clk_sel<<5) | DLL_ENABLE;
>> +                     if (arasan_phy_write(host, dll_sts, PHY_DLLCONTROL))
>> +                             return -ENODEV;
>> +                     if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
>> +                             return -ENODEV;
>> +                     break;
>> +             case MMC_TIMING_MMC_HS200:
>> +                     if (arasan_phy_write(host, 0, PHY_MODECONTROL))
>> +                             return -ENODEV;
>> +                     if (arasan_phy_read(host, PHY_IOPADCONTROL1, &val))
>> +                             return -ENODEV;
>> +                     io_pad = val | (host->mmc->ios.drv_type<<2);
>> +                     if (arasan_phy_write(host, io_pad, PHY_IOPADCONTROL1))
>> +                             return -ENODEV;
>> +                     otap = ((0x2<<1) | OTAP_DLY_EN);
>> +                     if (arasan_phy_write(host, otap, PHY_OUTPUTTAPDELAY))
>> +                             return -ENODEV;
>> +                     if (arasan_phy_write(host, 0, PHY_INPUTTAPDELAY))
>> +                             return -ENODEV;
>> +                     if (arasan_phy_write(host, DLL_TRIM, PHY_DLLTRIM))
>> +                             return -ENODEV;
>> +                     if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
>> +                             return -ENODEV;
>> +                     dll_sts = (clk_sel<<5) | DLL_ENABLE;
>> +                     if (arasan_phy_write(host, dll_sts, PHY_DLLCONTROL))
>> +                             return -ENODEV;
>> +                     if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
>> +                             return -ENODEV;
>> +                     if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
>> +                             return -ENODEV;
>> +                     break;
>> +             case MMC_TIMING_UHS_DDR50:
>> +                     if (arasan_phy_write(host, 0x8, PHY_MODECONTROL))
>> +                             return -ENODEV;
>> +                     otap = ((0x2<<3) | OTAP_DLY_EN);
>
> Is the shift by 3 a typo? If the idea was to set SEL_DLY_TXCLK, you
> should be doing BIT(5) instead.
>

will update

>> +                     if (arasan_phy_write(host, otap, PHY_OUTPUTTAPDELAY))
>> +                             return -ENODEV;
>> +                     itap = ((0x2<<1) | ITAP_DLY_EN);
>> +                     if (arasan_phy_write(host, itap, PHY_INPUTTAPDELAY))
>> +                             return -ENODEV;
>> +                     if (arasan_phy_write(host, DLL_TRIM, PHY_DLLTRIM))
>> +                             return -ENODEV;
>> +                     if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
>> +                             return -ENODEV;
>> +                     dll_sts = (clk_sel<<5) | DLL_ENABLE;
>> +                     if (arasan_phy_write(host, dll_sts, PHY_DLLCONTROL))
>> +                             return -ENODEV;
>> +                     if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
>> +                             return -ENODEV;
>> +                     break;
>> +             case MMC_TIMING_MMC_HS400:
>> +                     if (arasan_phy_write(host, 0x2, PHY_MODECONTROL))
>> +                             return -ENODEV;
>> +                     if (arasan_phy_read(host, PHY_IOPADCONTROL1, &val))
>> +                             return -ENODEV;
>> +                     io_pad = val | (host->mmc->ios.drv_type<<2);
>> +                     if (arasan_phy_write(host, io_pad, PHY_IOPADCONTROL1))
>> +                             return -ENODEV;
>> +                     otap = ((0x2<<1) | OTAP_DLY_EN);
>> +                     if (arasan_phy_write(host, otap, PHY_OUTPUTTAPDELAY))
>> +                             return -ENODEV;
>> +                     itap = ((0xA<<1) | ITAP_DLY_EN);
>> +                     if (arasan_phy_write(host, itap, PHY_INPUTTAPDELAY))
>> +                             return -ENODEV;
>> +                     if (arasan_phy_write(host, DLL_TRIM, PHY_DLLTRIM))
>> +                             return -ENODEV;
>> +                     if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
>> +                             return -ENODEV;
>> +                     dll_sts = (clk_sel<<5) | DLL_ENABLE;
>> +                     if (arasan_phy_write(host, dll_sts, PHY_DLLCONTROL))
>> +                             return -ENODEV;
>> +                     if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
>> +                             return -ENODEV;
>> +                     if (arasan_phy_write(host, 0x0E, PHY_CLKBUFSELECT))
>> +                             return -ENODEV;
>
> There is no setp to write PHY_CLKBUFSELECT for HS400 in the document
> (pages 26-27)?
>

will update the document

> Thanks,
> Sekhar
Adrian Hunter Oct. 19, 2017, 8:05 a.m. UTC | #3
On 10/10/17 20:42, Atul Garg wrote:
> The Arasan controller is based on a FPGA platform and has integrated phy
> with specific phy registers used during the initialization and
> management of different modes. The phy and the controller are integrated
> and registers are very specific to Arasan.
> 
> Arasan being an IP provider, licenses these IPs to various companies for
> integration of IP in custom SOCs. The custom SOCs define own register
> map depending on how bits are tied inside the SOC for phy registers,
> depending on SOC memory plan and hence will require own platform drivers.
> 
> If more details on phy registers are required, an interface document is
> hosted at https://arasan.com/NF/eMMC5.1 PHY Programming in Linux.pdf.
> 
> Signed-off-by: Atul Garg <agarg@arasan.com>

Sekhar Noir already covered some things.  There are some more comments below.

> ---
>  drivers/mmc/host/Makefile           |  18 +-
>  drivers/mmc/host/sdhci-pci-arasan.c | 325 ++++++++++++++++++++++++++++++++++++
>  drivers/mmc/host/sdhci-pci-arasan.h |  80 +++++++++
>  drivers/mmc/host/sdhci-pci-core.c   |  16 ++
>  drivers/mmc/host/sdhci-pci.h        |   4 +
>  5 files changed, 431 insertions(+), 12 deletions(-)
>  create mode 100644 drivers/mmc/host/sdhci-pci-arasan.c
>  create mode 100644 drivers/mmc/host/sdhci-pci-arasan.h
> 
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index 2b5a813..b9b2c6c 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -2,15 +2,14 @@
>  # Makefile for MMC/SD host controller drivers
>  #
>  
> -obj-$(CONFIG_MMC_ARMMMCI) += armmmci.o
> -armmmci-y := mmci.o
> -armmmci-$(CONFIG_MMC_QCOM_DML) += mmci_qcom_dml.o
> +obj-$(CONFIG_MMC_ARMMMCI)	+= mmci.o
> +obj-$(CONFIG_MMC_QCOM_DML)	+= mmci_qcom_dml.o
>  obj-$(CONFIG_MMC_PXA)		+= pxamci.o
>  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-pci-arasan.o
>  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
> @@ -37,13 +36,9 @@ obj-$(CONFIG_MMC_S3C)   	+= s3cmci.o
>  obj-$(CONFIG_MMC_SDRICOH_CS)	+= sdricoh_cs.o
>  obj-$(CONFIG_MMC_TMIO)		+= tmio_mmc.o
>  obj-$(CONFIG_MMC_TMIO_CORE)	+= tmio_mmc_core.o
> -obj-$(CONFIG_MMC_SDHI)		+= renesas_sdhi_core.o
> -ifeq ($(subst m,y,$(CONFIG_MMC_SDHI_SYS_DMAC)),y)
> -obj-$(CONFIG_MMC_SDHI)		+= renesas_sdhi_sys_dmac.o
> -endif
> -ifeq ($(subst m,y,$(CONFIG_MMC_SDHI_INTERNAL_DMAC)),y)
> -obj-$(CONFIG_MMC_SDHI)		+= renesas_sdhi_internal_dmac.o
> -endif
> +tmio_mmc_core-y			:= tmio_mmc_pio.o
> +tmio_mmc_core-$(subst m,y,$(CONFIG_MMC_SDHI))	+= tmio_mmc_dma.o
> +obj-$(CONFIG_MMC_SDHI)		+= sh_mobile_sdhi.o
>  obj-$(CONFIG_MMC_CB710)		+= cb710-mmc.o
>  obj-$(CONFIG_MMC_VIA_SDMMC)	+= via-sdmmc.o
>  obj-$(CONFIG_SDH_BFIN)		+= bfin_sdh.o
> @@ -89,7 +84,6 @@ obj-$(CONFIG_MMC_SDHCI_MSM)		+= sdhci-msm.o
>  obj-$(CONFIG_MMC_SDHCI_ST)		+= sdhci-st.o
>  obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32)	+= sdhci-pic32.o
>  obj-$(CONFIG_MMC_SDHCI_BRCMSTB)		+= sdhci-brcmstb.o
> -obj-$(CONFIG_MMC_SDHCI_OMAP)		+= sdhci-omap.o
>  
>  ifeq ($(CONFIG_CB710_DEBUG),y)
>  	CFLAGS-cb710-mmc	+= -DDEBUG
> diff --git a/drivers/mmc/host/sdhci-pci-arasan.c b/drivers/mmc/host/sdhci-pci-arasan.c
> new file mode 100644
> index 0000000..57fbf8f
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-pci-arasan.c
> @@ -0,0 +1,325 @@
> +/*
> + * Copyright (C) 2017 Arasan Chip Systems Inc.,
> + *
> + * Authors: Atul Garg <agarg@arasan.com>
> + *
> + * 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 "sdhci.h"
> +#include "sdhci-pci.h"
> +#include "sdhci-pci-arasan.h"
> +
> +static int arasan_phy_write(struct sdhci_host *host, u8 data, u8 offset)
> +{
> +	u32 timeout;
> +	u8 busy;
> +
> +	sdhci_writew(host, data, HOST_PHY_DATA_REG);
> +	sdhci_writew(host, ((1<<8) | offset), HOST_PHY_ADDR_REG);
> +	timeout = 20;
> +	do {
> +		busy = sdhci_readw(host, HOST_PHY_ADDR_REG) & (1<<9);
> +		if (!busy)
> +			break;
> +		mdelay(1);

Why not usleep_range instead?

> +	} while (timeout--);
> +	if (!timeout)
> +		return -ENODEV;
> +	return 0;
> +}
> +
> +static int arasan_phy_read(struct sdhci_host *host, u8 offset, u8 *data)
> +{
> +	u32 timeout;
> +	u8 busy;
> +
> +	sdhci_writew(host, 0, HOST_PHY_DATA_REG);
> +	*data = sdhci_readw(host, HOST_PHY_DATA_REG) & 0xFF;
> +	sdhci_writew(host, ((0<<8) | offset), HOST_PHY_ADDR_REG);
> +	timeout = 20;
> +	do {
> +		busy = sdhci_readw(host, HOST_PHY_ADDR_REG) & (1<<9);
> +		if (!busy)
> +			break;
> +		mdelay(1);

Why not usleep_range instead?

> +	} while (timeout--);
> +	if (!timeout)
> +		return -ENODEV;
> +	*data = sdhci_readw(host, HOST_PHY_DATA_REG) & 0xFF;
> +	return 0;
> +}
> +
> +/* Initialize the Arasan PHY */
> +static int arasan_phy_init(struct sdhci_host *host)
> +{
> +	struct sdhci_pci_slot *slot = sdhci_priv(host);
> +	struct pci_dev *pdev = slot->chip->pdev;
> +	u8 val;
> +
> +	if (arasan_phy_write(host, 0, PHY_CONTROL))
> +		return -ENODEV;
> +	if (arasan_phy_read(host, PHY_IOPADCONTROL1, &val))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, val | RETB_ENABLE, PHY_IOPADCONTROL1))
> +		return -ENODEV;
> +	if (arasan_phy_read(host, PHY_IOPADCONTROL2, &val))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, val | RTRIM_ENABLE, PHY_IOPADCONTROL2))
> +		return -ENODEV;

All these statements can be combined e.g.

	if (arasan_phy_write(host, 0, PHY_CONTROL) ||
	    arasan_phy_read(host, PHY_IOPADCONTROL1, &val) ||
	    arasan_phy_write(host, val | RETB_ENABLE, PHY_IOPADCONTROL1) ||
	    arasan_phy_read(host, PHY_IOPADCONTROL2, &val) ||
	    arasan_phy_write(host, val | RTRIM_ENABLE, PHY_IOPADCONTROL2))
		return -ENODEV;

Same with more examples below.

> +	mdelay(10);

Why not msleep instead?

> +
> +	if (arasan_phy_read(host, PHY_IOPADSTATUS, &val)) {
> +		if (!(val & CALDONE_MASK)) {
> +			dev_err(&pdev->dev, "Phy calibration not done\n");
> +			return -ENODEV;
> +		}
> +	}
> +	if (arasan_phy_read(host, PHY_IOPADCONTROL1, &val))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, val | PDB_ENABLE, PHY_IOPADCONTROL1))
> +		return -ENODEV;
> +	mdelay(10);

Why not msleep instead?

> +
> +	if (arasan_phy_read(host, PHY_IOPADSTATUS, &val)) {
> +		if (!(val & CALDONE_MASK)) {
> +			dev_err(&pdev->dev, "Phy calibration not done\n");
> +			return -ENODEV;
> +		}
> +	}
> +	if (arasan_phy_read(host, PHY_IORENCONTROL1, &val))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, val | REN_CMD | REN_STRB, PHY_IORENCONTROL1))
> +		return -ENODEV;
> +	if (arasan_phy_read(host, PHY_IOPUCONTROL1, &val))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, val | PU_CMD, PHY_IOPUCONTROL1))
> +		return -ENODEV;
> +	if (arasan_phy_read(host, PHY_CMDCONTROL, &val))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, val | PDB_CMD, PHY_CMDCONTROL))
> +		return -ENODEV;
> +	if (arasan_phy_read(host, PHY_IORENCONTROL2, &val))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, val | REN_DAT, PHY_IORENCONTROL2))
> +		return -ENODEV;
> +	if (arasan_phy_read(host, PHY_IOPUCONTROL2, &val))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, val | PU_DAT, PHY_IOPUCONTROL2))
> +		return -ENODEV;
> +	if (arasan_phy_read(host, PHY_DATACONTROL, &val))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, val | PDB_DAT, PHY_DATACONTROL))
> +		return -ENODEV;
> +	if (arasan_phy_read(host, PHY_STROBECONTROL, &val))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, val | PDB_STRB, PHY_STROBECONTROL))
> +		return -ENODEV;
> +	if (arasan_phy_read(host, PHY_CLKCONTROL, &val))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, val | PDB_CLK, PHY_CLKCONTROL))
> +		return -ENODEV;
> +	if (arasan_phy_read(host, PHY_CLKBUFSELECT, &val))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, val | 0x7, PHY_CLKBUFSELECT))
> +		return -ENODEV;
> +	if (arasan_phy_write(host, 0x4, PHY_MODECONTROL))
> +		return -ENODEV;
> +	return 0;
> +}
> +
> +
> +static int arasan_set_phy(struct sdhci_host *host)
> +{
> +	u8 clk_sel;
> +	static u32 chg_clk;
> +	u8 val;
> +	u8 otap, itap, dll_sts, io_pad;
> +
> +	if (chg_clk != host->mmc->ios.clock) {
> +		chg_clk = host->mmc->ios.clock;
> +		if (host->mmc->ios.clock == 200000000)
> +			clk_sel = 0;
> +		else if (host->mmc->ios.clock == 100000000)
> +			clk_sel = 2;
> +		else if (host->mmc->ios.clock == 50000000)
> +			clk_sel = 1;
> +		else
> +			clk_sel = 0;
> +		/* Change phy settings only if there is a change in clock */
> +		goto set_phy;
> +	} else
> +		return 0;
> +set_phy:
> +	if (host->mmc_host_ops.hs400_enhanced_strobe) {
> +		if (arasan_phy_write(host, 0x1, PHY_MODECONTROL))
> +			return -ENODEV;
> +		otap = ((0x2<<1) | OTAP_DLY_EN);
> +		if (arasan_phy_write(host, otap, PHY_OUTPUTTAPDELAY))
> +			return -ENODEV;
> +		if (arasan_phy_write(host, 0, PHY_INPUTTAPDELAY))
> +			return -ENODEV;
> +		if (arasan_phy_write(host, DLL_TRIM, PHY_DLLTRIM))
> +			return -ENODEV;
> +		if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
> +			return -ENODEV;
> +		dll_sts = (clk_sel<<5) | DLL_ENABLE;
> +		if (arasan_phy_write(host, dll_sts, PHY_DLLCONTROL))
> +			return -ENODEV;
> +		if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
> +			return -ENODEV;
> +	} else {
> +		switch (host->mmc->ios.timing) {
> +		case MMC_TIMING_LEGACY:
> +			if (arasan_phy_write(host, 0x4, PHY_MODECONTROL))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, 0, PHY_INPUTTAPDELAY))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, 0, PHY_OUTPUTTAPDELAY))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
> +				return -ENODEV;
> +			if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
> +				return -ENODEV;
> +			break;
> +		case MMC_TIMING_MMC_HS:
> +			if (arasan_phy_write(host, 0, PHY_MODECONTROL))
> +				return -ENODEV;
> +			otap = ((0x2<<3) | OTAP_DLY_EN);
> +			if (arasan_phy_write(host, otap, PHY_OUTPUTTAPDELAY))
> +				return -ENODEV;
> +			itap = ((0x2<<1) | ITAP_DLY_EN);
> +			if (arasan_phy_write(host, itap, PHY_INPUTTAPDELAY))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, 0, PHY_INPUTTAPDELAY))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, DLL_TRIM, PHY_DLLTRIM))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
> +				return -ENODEV;
> +			dll_sts = (clk_sel<<5) | DLL_ENABLE;
> +			if (arasan_phy_write(host, dll_sts, PHY_DLLCONTROL))
> +				return -ENODEV;
> +			if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
> +				return -ENODEV;
> +			break;
> +		case MMC_TIMING_MMC_HS200:
> +			if (arasan_phy_write(host, 0, PHY_MODECONTROL))
> +				return -ENODEV;
> +			if (arasan_phy_read(host, PHY_IOPADCONTROL1, &val))
> +				return -ENODEV;
> +			io_pad = val | (host->mmc->ios.drv_type<<2);
> +			if (arasan_phy_write(host, io_pad, PHY_IOPADCONTROL1))
> +				return -ENODEV;
> +			otap = ((0x2<<1) | OTAP_DLY_EN);
> +			if (arasan_phy_write(host, otap, PHY_OUTPUTTAPDELAY))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, 0, PHY_INPUTTAPDELAY))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, DLL_TRIM, PHY_DLLTRIM))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
> +				return -ENODEV;
> +			dll_sts = (clk_sel<<5) | DLL_ENABLE;
> +			if (arasan_phy_write(host, dll_sts, PHY_DLLCONTROL))
> +				return -ENODEV;
> +			if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
> +				return -ENODEV;
> +			if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
> +				return -ENODEV;
> +			break;
> +		case MMC_TIMING_UHS_DDR50:
> +			if (arasan_phy_write(host, 0x8, PHY_MODECONTROL))
> +				return -ENODEV;
> +			otap = ((0x2<<3) | OTAP_DLY_EN);
> +			if (arasan_phy_write(host, otap, PHY_OUTPUTTAPDELAY))
> +				return -ENODEV;
> +			itap = ((0x2<<1) | ITAP_DLY_EN);
> +			if (arasan_phy_write(host, itap, PHY_INPUTTAPDELAY))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, DLL_TRIM, PHY_DLLTRIM))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
> +				return -ENODEV;
> +			dll_sts = (clk_sel<<5) | DLL_ENABLE;
> +			if (arasan_phy_write(host, dll_sts, PHY_DLLCONTROL))
> +				return -ENODEV;
> +			if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
> +				return -ENODEV;
> +			break;
> +		case MMC_TIMING_MMC_HS400:
> +			if (arasan_phy_write(host, 0x2, PHY_MODECONTROL))
> +				return -ENODEV;
> +			if (arasan_phy_read(host, PHY_IOPADCONTROL1, &val))
> +				return -ENODEV;
> +			io_pad = val | (host->mmc->ios.drv_type<<2);
> +			if (arasan_phy_write(host, io_pad, PHY_IOPADCONTROL1))
> +				return -ENODEV;
> +			otap = ((0x2<<1) | OTAP_DLY_EN);
> +			if (arasan_phy_write(host, otap, PHY_OUTPUTTAPDELAY))
> +				return -ENODEV;
> +			itap = ((0xA<<1) | ITAP_DLY_EN);
> +			if (arasan_phy_write(host, itap, PHY_INPUTTAPDELAY))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, DLL_TRIM, PHY_DLLTRIM))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
> +				return -ENODEV;
> +			dll_sts = (clk_sel<<5) | DLL_ENABLE;
> +			if (arasan_phy_write(host, dll_sts, PHY_DLLCONTROL))
> +				return -ENODEV;
> +			if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
> +				return -ENODEV;
> +			if (arasan_phy_write(host, 0x0E, PHY_CLKBUFSELECT))
> +				return -ENODEV;
> +			break;
> +		default:
> +			break;
> +		}
> +	}
> +	return 0;
> +}
> +
> +int arasan_pci_probe(struct sdhci_pci_chip *chip)
> +{
> +	struct pci_dev *dev;
> +
> +	dev = pci_get_device(PCI_VENDOR_ID_ARASAN,
> +			PCI_DEVICE_ID_ARASAN_PHY_EMMC, NULL);

What is this for?

> +	return 0;
> +}
> +int arasan_pci_probe_slot(struct sdhci_pci_slot *slot)
> +{
> +	int err;
> +
> +	err = arasan_phy_init(slot->host);
> +	if (err)
> +		return -ENODEV;
> +	return 0;
> +}
> +
> +void arasan_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
> +{
> +	u16 clk;
> +
> +	host->mmc->actual_clock = 0;
> +	sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
> +	if (clock == 0)
> +		return;
> +	clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock);
> +	sdhci_enable_clk(host, clk);
> +
> +	/*Change phy settings for the new clock */
> +	arasan_set_phy(host);
> +}
> diff --git a/drivers/mmc/host/sdhci-pci-arasan.h b/drivers/mmc/host/sdhci-pci-arasan.h

We shouldn't have header files we don't need.  Please put definitions in
sdhci-pci-arasan.c instead.

> new file mode 100644
> index 0000000..819ed93
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-pci-arasan.h
> @@ -0,0 +1,80 @@
> +/*
> + * Copyright (C) 2017 Arasan Chip Systems Inc.,
> + *
> + * Authors: Atul Garg <agarg@arasan.com>
> + *
> + * 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_PCI_ARASAN_H
> +#define __SDHCI_PCI_ARASAN_H
> +
> +#include "sdhci-pci.h"
> +
> +/* Extra registers for Arasan SD Host Controller for eMMC5.1 PHY */
> +#define HOST_PHY_ADDR_REG	0x300
> +#define HOST_PHY_DATA_REG	0x304
> +
> +#define PHY_DLLCONTROL		0x00
> +#define PHY_IOPADCONTROL1	0x01
> +#define PHY_IOPADCONTROL2	0x02
> +#define PHY_IOPADSTATUS	0x03
> +#define PHY_IOODCONTROL1	0x04
> +#define PHY_IOODCONTROL2	0x05
> +#define PHY_IORENCONTROL1	0x06
> +#define PHY_IORENCONTROL2	0x07
> +#define PHY_IOPUCONTROL1	0x08
> +#define PHY_IOPUCONTROL2	0x09
> +#define PHY_IOODRELCONTROL1	0x0A
> +#define PHY_IOODRELCONTROL2	0x0B
> +#define PHY_INPUTTAPDELAY	0x0C
> +#define PHY_OUTPUTTAPDELAY	0x0D
> +#define PHY_STROBESELECT	0x0E
> +#define PHY_CLKBUFSELECT	0x0F
> +#define PHY_MODECONTROL	0x11
> +#define PHY_DLLTRIM		0x12
> +#define PHY_LDOCONTROL		0x1F
> +#define PHY_CMDCONTROL		0x20
> +#define PHY_DATACONTROL	0x21
> +#define PHY_STROBECONTROL	0x22
> +#define PHY_CLKCONTROL		0x23
> +#define PHY_CONTROL		0x24
> +
> +#define DLL_ENABLE	BIT(3)
> +#define RTRIM_ENABLE	BIT(1)
> +#define PDB_ENABLE	BIT(1)
> +#define RETB_ENABLE	BIT(6)
> +#define ODEN_CMD	BIT(1)
> +#define ODEN_DAT	0xFF
> +#define REN_STRB	BIT(0)
> +#define REN_CMD	BIT(1)
> +#define REN_DAT	0xFF
> +#define PU_CMD		BIT(1)
> +#define PU_DAT		0xFF
> +#define ITAP_DLY_EN	BIT(0)
> +#define OTAP_DLY_EN	BIT(0)
> +#define OD_REL_CMD	BIT(1)
> +#define OD_REL_DAT	0xFF
> +#define DLL_TRIM	0x8
> +#define PDB_CMD	BIT(0)
> +#define PDB_DAT	0xFF
> +#define PDB_STRB	BIT(0)
> +#define PDB_CLK	BIT(0)
> +#define LDO_RDYB	0xFE
> +#define CALDONE_MASK	0x10
> +
> +extern int arasan_pci_probe_slot(struct sdhci_pci_slot *slot);
> +
> +extern int arasan_pci_probe(struct sdhci_pci_chip *chip);
> +
> +extern void arasan_sdhci_set_clock(struct sdhci_host *host, unsigned int clock);

These 3 are only ever going to be called in sdhci-pci-core.c so let's just
put the declarations there - see below.  Also "extern" is redundant.

> +
> +#endif /* __SDHCI_PCI_ARASAN_H */
> diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
> index 5f3f7b5..3bd77dd 100644
> --- a/drivers/mmc/host/sdhci-pci-core.c
> +++ b/drivers/mmc/host/sdhci-pci-core.c
> @@ -1112,6 +1112,21 @@ static const struct sdhci_pci_fixes sdhci_rtsx = {
>  	.probe_slot	= rtsx_probe_slot,
>  };
>  

Let's just put the declarations here:

int arasan_pci_probe_slot(struct sdhci_pci_slot *slot);
int arasan_pci_probe(struct sdhci_pci_chip *chip);
void arasan_sdhci_set_clock(struct sdhci_host *host, unsigned int clock);

> +static const struct sdhci_ops arasan_sdhci_pci_ops = {
> +	.set_clock			= arasan_sdhci_set_clock,
> +	.enable_dma			= sdhci_pci_enable_dma,
> +	.set_bus_width			= sdhci_pci_set_bus_width,

sdhci_pci_set_bus_width has been replaced with sdhci_set_bus_width

> +	.reset				= sdhci_reset,
> +	.set_uhs_signaling		= sdhci_set_uhs_signaling,
> +};
> +
> +static const struct sdhci_pci_fixes sdhci_arasan = {
> +	.probe      = arasan_pci_probe,
> +	.probe_slot = arasan_pci_probe_slot,
> +	.ops	    = &arasan_sdhci_pci_ops,
> +};
> +
> +
>  /*AMD chipset generation*/
>  enum amd_chipset_gen {
>  	AMD_CHIPSET_BEFORE_ML,
> @@ -1314,6 +1329,7 @@ static const struct pci_device_id pci_ids[] = {
>  	SDHCI_PCI_DEVICE(O2, SDS1,     o2),
>  	SDHCI_PCI_DEVICE(O2, SEABIRD0, o2),
>  	SDHCI_PCI_DEVICE(O2, SEABIRD1, o2),
> +	SDHCI_PCI_DEVICE(ARASAN, PHY_EMMC, arasan),
>  	SDHCI_PCI_DEVICE_CLASS(AMD, SYSTEM_SDHCI, PCI_CLASS_MASK, amd),
>  	/* Generic SD host controller */
>  	{PCI_DEVICE_CLASS(SYSTEM_SDHCI, PCI_CLASS_MASK)},
> diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
> index 3c1dd79..e370836 100644
> --- a/drivers/mmc/host/sdhci-pci.h
> +++ b/drivers/mmc/host/sdhci-pci.h
> @@ -48,6 +48,10 @@
>  
>  #define PCI_SUBDEVICE_ID_NI_7884	0x7884
>  
> +/* Arasan Device IDs */

Please omit the comment - the ID names are already self-explanatory.

> +#define PCI_VENDOR_ID_ARASAN	0x16e6
> +#define PCI_DEVICE_ID_ARASAN_PHY_EMMC	0x0670
> +
>  /*
>   * PCI device class and mask
>   */
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Adrian Hunter Oct. 19, 2017, 11:53 a.m. UTC | #4
On 19/10/17 11:05, Adrian Hunter wrote:
> On 10/10/17 20:42, Atul Garg wrote:
> 
> Let's just put the declarations here:
> 
> int arasan_pci_probe_slot(struct sdhci_pci_slot *slot);
> int arasan_pci_probe(struct sdhci_pci_chip *chip);
> void arasan_sdhci_set_clock(struct sdhci_host *host, unsigned int clock);
> 

Actually checkpatch doesn't like that so let's put them in sdhci-pci.h
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 2b5a813..b9b2c6c 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -2,15 +2,14 @@ 
 # Makefile for MMC/SD host controller drivers
 #
 
-obj-$(CONFIG_MMC_ARMMMCI) += armmmci.o
-armmmci-y := mmci.o
-armmmci-$(CONFIG_MMC_QCOM_DML) += mmci_qcom_dml.o
+obj-$(CONFIG_MMC_ARMMMCI)	+= mmci.o
+obj-$(CONFIG_MMC_QCOM_DML)	+= mmci_qcom_dml.o
 obj-$(CONFIG_MMC_PXA)		+= pxamci.o
 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-pci-arasan.o
 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
@@ -37,13 +36,9 @@  obj-$(CONFIG_MMC_S3C)   	+= s3cmci.o
 obj-$(CONFIG_MMC_SDRICOH_CS)	+= sdricoh_cs.o
 obj-$(CONFIG_MMC_TMIO)		+= tmio_mmc.o
 obj-$(CONFIG_MMC_TMIO_CORE)	+= tmio_mmc_core.o
-obj-$(CONFIG_MMC_SDHI)		+= renesas_sdhi_core.o
-ifeq ($(subst m,y,$(CONFIG_MMC_SDHI_SYS_DMAC)),y)
-obj-$(CONFIG_MMC_SDHI)		+= renesas_sdhi_sys_dmac.o
-endif
-ifeq ($(subst m,y,$(CONFIG_MMC_SDHI_INTERNAL_DMAC)),y)
-obj-$(CONFIG_MMC_SDHI)		+= renesas_sdhi_internal_dmac.o
-endif
+tmio_mmc_core-y			:= tmio_mmc_pio.o
+tmio_mmc_core-$(subst m,y,$(CONFIG_MMC_SDHI))	+= tmio_mmc_dma.o
+obj-$(CONFIG_MMC_SDHI)		+= sh_mobile_sdhi.o
 obj-$(CONFIG_MMC_CB710)		+= cb710-mmc.o
 obj-$(CONFIG_MMC_VIA_SDMMC)	+= via-sdmmc.o
 obj-$(CONFIG_SDH_BFIN)		+= bfin_sdh.o
@@ -89,7 +84,6 @@  obj-$(CONFIG_MMC_SDHCI_MSM)		+= sdhci-msm.o
 obj-$(CONFIG_MMC_SDHCI_ST)		+= sdhci-st.o
 obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32)	+= sdhci-pic32.o
 obj-$(CONFIG_MMC_SDHCI_BRCMSTB)		+= sdhci-brcmstb.o
-obj-$(CONFIG_MMC_SDHCI_OMAP)		+= sdhci-omap.o
 
 ifeq ($(CONFIG_CB710_DEBUG),y)
 	CFLAGS-cb710-mmc	+= -DDEBUG
diff --git a/drivers/mmc/host/sdhci-pci-arasan.c b/drivers/mmc/host/sdhci-pci-arasan.c
new file mode 100644
index 0000000..57fbf8f
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pci-arasan.c
@@ -0,0 +1,325 @@ 
+/*
+ * Copyright (C) 2017 Arasan Chip Systems Inc.,
+ *
+ * Authors: Atul Garg <agarg@arasan.com>
+ *
+ * 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 "sdhci.h"
+#include "sdhci-pci.h"
+#include "sdhci-pci-arasan.h"
+
+static int arasan_phy_write(struct sdhci_host *host, u8 data, u8 offset)
+{
+	u32 timeout;
+	u8 busy;
+
+	sdhci_writew(host, data, HOST_PHY_DATA_REG);
+	sdhci_writew(host, ((1<<8) | offset), HOST_PHY_ADDR_REG);
+	timeout = 20;
+	do {
+		busy = sdhci_readw(host, HOST_PHY_ADDR_REG) & (1<<9);
+		if (!busy)
+			break;
+		mdelay(1);
+	} while (timeout--);
+	if (!timeout)
+		return -ENODEV;
+	return 0;
+}
+
+static int arasan_phy_read(struct sdhci_host *host, u8 offset, u8 *data)
+{
+	u32 timeout;
+	u8 busy;
+
+	sdhci_writew(host, 0, HOST_PHY_DATA_REG);
+	*data = sdhci_readw(host, HOST_PHY_DATA_REG) & 0xFF;
+	sdhci_writew(host, ((0<<8) | offset), HOST_PHY_ADDR_REG);
+	timeout = 20;
+	do {
+		busy = sdhci_readw(host, HOST_PHY_ADDR_REG) & (1<<9);
+		if (!busy)
+			break;
+		mdelay(1);
+	} while (timeout--);
+	if (!timeout)
+		return -ENODEV;
+	*data = sdhci_readw(host, HOST_PHY_DATA_REG) & 0xFF;
+	return 0;
+}
+
+/* Initialize the Arasan PHY */
+static int arasan_phy_init(struct sdhci_host *host)
+{
+	struct sdhci_pci_slot *slot = sdhci_priv(host);
+	struct pci_dev *pdev = slot->chip->pdev;
+	u8 val;
+
+	if (arasan_phy_write(host, 0, PHY_CONTROL))
+		return -ENODEV;
+	if (arasan_phy_read(host, PHY_IOPADCONTROL1, &val))
+		return -ENODEV;
+	if (arasan_phy_write(host, val | RETB_ENABLE, PHY_IOPADCONTROL1))
+		return -ENODEV;
+	if (arasan_phy_read(host, PHY_IOPADCONTROL2, &val))
+		return -ENODEV;
+	if (arasan_phy_write(host, val | RTRIM_ENABLE, PHY_IOPADCONTROL2))
+		return -ENODEV;
+	mdelay(10);
+
+	if (arasan_phy_read(host, PHY_IOPADSTATUS, &val)) {
+		if (!(val & CALDONE_MASK)) {
+			dev_err(&pdev->dev, "Phy calibration not done\n");
+			return -ENODEV;
+		}
+	}
+	if (arasan_phy_read(host, PHY_IOPADCONTROL1, &val))
+		return -ENODEV;
+	if (arasan_phy_write(host, val | PDB_ENABLE, PHY_IOPADCONTROL1))
+		return -ENODEV;
+	mdelay(10);
+
+	if (arasan_phy_read(host, PHY_IOPADSTATUS, &val)) {
+		if (!(val & CALDONE_MASK)) {
+			dev_err(&pdev->dev, "Phy calibration not done\n");
+			return -ENODEV;
+		}
+	}
+	if (arasan_phy_read(host, PHY_IORENCONTROL1, &val))
+		return -ENODEV;
+	if (arasan_phy_write(host, val | REN_CMD | REN_STRB, PHY_IORENCONTROL1))
+		return -ENODEV;
+	if (arasan_phy_read(host, PHY_IOPUCONTROL1, &val))
+		return -ENODEV;
+	if (arasan_phy_write(host, val | PU_CMD, PHY_IOPUCONTROL1))
+		return -ENODEV;
+	if (arasan_phy_read(host, PHY_CMDCONTROL, &val))
+		return -ENODEV;
+	if (arasan_phy_write(host, val | PDB_CMD, PHY_CMDCONTROL))
+		return -ENODEV;
+	if (arasan_phy_read(host, PHY_IORENCONTROL2, &val))
+		return -ENODEV;
+	if (arasan_phy_write(host, val | REN_DAT, PHY_IORENCONTROL2))
+		return -ENODEV;
+	if (arasan_phy_read(host, PHY_IOPUCONTROL2, &val))
+		return -ENODEV;
+	if (arasan_phy_write(host, val | PU_DAT, PHY_IOPUCONTROL2))
+		return -ENODEV;
+	if (arasan_phy_read(host, PHY_DATACONTROL, &val))
+		return -ENODEV;
+	if (arasan_phy_write(host, val | PDB_DAT, PHY_DATACONTROL))
+		return -ENODEV;
+	if (arasan_phy_read(host, PHY_STROBECONTROL, &val))
+		return -ENODEV;
+	if (arasan_phy_write(host, val | PDB_STRB, PHY_STROBECONTROL))
+		return -ENODEV;
+	if (arasan_phy_read(host, PHY_CLKCONTROL, &val))
+		return -ENODEV;
+	if (arasan_phy_write(host, val | PDB_CLK, PHY_CLKCONTROL))
+		return -ENODEV;
+	if (arasan_phy_read(host, PHY_CLKBUFSELECT, &val))
+		return -ENODEV;
+	if (arasan_phy_write(host, val | 0x7, PHY_CLKBUFSELECT))
+		return -ENODEV;
+	if (arasan_phy_write(host, 0x4, PHY_MODECONTROL))
+		return -ENODEV;
+	return 0;
+}
+
+
+static int arasan_set_phy(struct sdhci_host *host)
+{
+	u8 clk_sel;
+	static u32 chg_clk;
+	u8 val;
+	u8 otap, itap, dll_sts, io_pad;
+
+	if (chg_clk != host->mmc->ios.clock) {
+		chg_clk = host->mmc->ios.clock;
+		if (host->mmc->ios.clock == 200000000)
+			clk_sel = 0;
+		else if (host->mmc->ios.clock == 100000000)
+			clk_sel = 2;
+		else if (host->mmc->ios.clock == 50000000)
+			clk_sel = 1;
+		else
+			clk_sel = 0;
+		/* Change phy settings only if there is a change in clock */
+		goto set_phy;
+	} else
+		return 0;
+set_phy:
+	if (host->mmc_host_ops.hs400_enhanced_strobe) {
+		if (arasan_phy_write(host, 0x1, PHY_MODECONTROL))
+			return -ENODEV;
+		otap = ((0x2<<1) | OTAP_DLY_EN);
+		if (arasan_phy_write(host, otap, PHY_OUTPUTTAPDELAY))
+			return -ENODEV;
+		if (arasan_phy_write(host, 0, PHY_INPUTTAPDELAY))
+			return -ENODEV;
+		if (arasan_phy_write(host, DLL_TRIM, PHY_DLLTRIM))
+			return -ENODEV;
+		if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
+			return -ENODEV;
+		dll_sts = (clk_sel<<5) | DLL_ENABLE;
+		if (arasan_phy_write(host, dll_sts, PHY_DLLCONTROL))
+			return -ENODEV;
+		if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
+			return -ENODEV;
+	} else {
+		switch (host->mmc->ios.timing) {
+		case MMC_TIMING_LEGACY:
+			if (arasan_phy_write(host, 0x4, PHY_MODECONTROL))
+				return -ENODEV;
+			if (arasan_phy_write(host, 0, PHY_INPUTTAPDELAY))
+				return -ENODEV;
+			if (arasan_phy_write(host, 0, PHY_OUTPUTTAPDELAY))
+				return -ENODEV;
+			if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
+				return -ENODEV;
+			if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
+				return -ENODEV;
+			break;
+		case MMC_TIMING_MMC_HS:
+			if (arasan_phy_write(host, 0, PHY_MODECONTROL))
+				return -ENODEV;
+			otap = ((0x2<<3) | OTAP_DLY_EN);
+			if (arasan_phy_write(host, otap, PHY_OUTPUTTAPDELAY))
+				return -ENODEV;
+			itap = ((0x2<<1) | ITAP_DLY_EN);
+			if (arasan_phy_write(host, itap, PHY_INPUTTAPDELAY))
+				return -ENODEV;
+			if (arasan_phy_write(host, 0, PHY_INPUTTAPDELAY))
+				return -ENODEV;
+			if (arasan_phy_write(host, DLL_TRIM, PHY_DLLTRIM))
+				return -ENODEV;
+			if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
+				return -ENODEV;
+			dll_sts = (clk_sel<<5) | DLL_ENABLE;
+			if (arasan_phy_write(host, dll_sts, PHY_DLLCONTROL))
+				return -ENODEV;
+			if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
+				return -ENODEV;
+			break;
+		case MMC_TIMING_MMC_HS200:
+			if (arasan_phy_write(host, 0, PHY_MODECONTROL))
+				return -ENODEV;
+			if (arasan_phy_read(host, PHY_IOPADCONTROL1, &val))
+				return -ENODEV;
+			io_pad = val | (host->mmc->ios.drv_type<<2);
+			if (arasan_phy_write(host, io_pad, PHY_IOPADCONTROL1))
+				return -ENODEV;
+			otap = ((0x2<<1) | OTAP_DLY_EN);
+			if (arasan_phy_write(host, otap, PHY_OUTPUTTAPDELAY))
+				return -ENODEV;
+			if (arasan_phy_write(host, 0, PHY_INPUTTAPDELAY))
+				return -ENODEV;
+			if (arasan_phy_write(host, DLL_TRIM, PHY_DLLTRIM))
+				return -ENODEV;
+			if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
+				return -ENODEV;
+			dll_sts = (clk_sel<<5) | DLL_ENABLE;
+			if (arasan_phy_write(host, dll_sts, PHY_DLLCONTROL))
+				return -ENODEV;
+			if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
+				return -ENODEV;
+			if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
+				return -ENODEV;
+			break;
+		case MMC_TIMING_UHS_DDR50:
+			if (arasan_phy_write(host, 0x8, PHY_MODECONTROL))
+				return -ENODEV;
+			otap = ((0x2<<3) | OTAP_DLY_EN);
+			if (arasan_phy_write(host, otap, PHY_OUTPUTTAPDELAY))
+				return -ENODEV;
+			itap = ((0x2<<1) | ITAP_DLY_EN);
+			if (arasan_phy_write(host, itap, PHY_INPUTTAPDELAY))
+				return -ENODEV;
+			if (arasan_phy_write(host, DLL_TRIM, PHY_DLLTRIM))
+				return -ENODEV;
+			if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
+				return -ENODEV;
+			dll_sts = (clk_sel<<5) | DLL_ENABLE;
+			if (arasan_phy_write(host, dll_sts, PHY_DLLCONTROL))
+				return -ENODEV;
+			if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
+				return -ENODEV;
+			break;
+		case MMC_TIMING_MMC_HS400:
+			if (arasan_phy_write(host, 0x2, PHY_MODECONTROL))
+				return -ENODEV;
+			if (arasan_phy_read(host, PHY_IOPADCONTROL1, &val))
+				return -ENODEV;
+			io_pad = val | (host->mmc->ios.drv_type<<2);
+			if (arasan_phy_write(host, io_pad, PHY_IOPADCONTROL1))
+				return -ENODEV;
+			otap = ((0x2<<1) | OTAP_DLY_EN);
+			if (arasan_phy_write(host, otap, PHY_OUTPUTTAPDELAY))
+				return -ENODEV;
+			itap = ((0xA<<1) | ITAP_DLY_EN);
+			if (arasan_phy_write(host, itap, PHY_INPUTTAPDELAY))
+				return -ENODEV;
+			if (arasan_phy_write(host, DLL_TRIM, PHY_DLLTRIM))
+				return -ENODEV;
+			if (arasan_phy_write(host, 0, PHY_DLLCONTROL))
+				return -ENODEV;
+			dll_sts = (clk_sel<<5) | DLL_ENABLE;
+			if (arasan_phy_write(host, dll_sts, PHY_DLLCONTROL))
+				return -ENODEV;
+			if (arasan_phy_read(host, PHY_DLLCONTROL, &val))
+				return -ENODEV;
+			if (arasan_phy_write(host, 0x0E, PHY_CLKBUFSELECT))
+				return -ENODEV;
+			break;
+		default:
+			break;
+		}
+	}
+	return 0;
+}
+
+int arasan_pci_probe(struct sdhci_pci_chip *chip)
+{
+	struct pci_dev *dev;
+
+	dev = pci_get_device(PCI_VENDOR_ID_ARASAN,
+			PCI_DEVICE_ID_ARASAN_PHY_EMMC, NULL);
+	return 0;
+}
+int arasan_pci_probe_slot(struct sdhci_pci_slot *slot)
+{
+	int err;
+
+	err = arasan_phy_init(slot->host);
+	if (err)
+		return -ENODEV;
+	return 0;
+}
+
+void arasan_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+	u16 clk;
+
+	host->mmc->actual_clock = 0;
+	sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
+	if (clock == 0)
+		return;
+	clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock);
+	sdhci_enable_clk(host, clk);
+
+	/*Change phy settings for the new clock */
+	arasan_set_phy(host);
+}
diff --git a/drivers/mmc/host/sdhci-pci-arasan.h b/drivers/mmc/host/sdhci-pci-arasan.h
new file mode 100644
index 0000000..819ed93
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pci-arasan.h
@@ -0,0 +1,80 @@ 
+/*
+ * Copyright (C) 2017 Arasan Chip Systems Inc.,
+ *
+ * Authors: Atul Garg <agarg@arasan.com>
+ *
+ * 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_PCI_ARASAN_H
+#define __SDHCI_PCI_ARASAN_H
+
+#include "sdhci-pci.h"
+
+/* Extra registers for Arasan SD Host Controller for eMMC5.1 PHY */
+#define HOST_PHY_ADDR_REG	0x300
+#define HOST_PHY_DATA_REG	0x304
+
+#define PHY_DLLCONTROL		0x00
+#define PHY_IOPADCONTROL1	0x01
+#define PHY_IOPADCONTROL2	0x02
+#define PHY_IOPADSTATUS	0x03
+#define PHY_IOODCONTROL1	0x04
+#define PHY_IOODCONTROL2	0x05
+#define PHY_IORENCONTROL1	0x06
+#define PHY_IORENCONTROL2	0x07
+#define PHY_IOPUCONTROL1	0x08
+#define PHY_IOPUCONTROL2	0x09
+#define PHY_IOODRELCONTROL1	0x0A
+#define PHY_IOODRELCONTROL2	0x0B
+#define PHY_INPUTTAPDELAY	0x0C
+#define PHY_OUTPUTTAPDELAY	0x0D
+#define PHY_STROBESELECT	0x0E
+#define PHY_CLKBUFSELECT	0x0F
+#define PHY_MODECONTROL	0x11
+#define PHY_DLLTRIM		0x12
+#define PHY_LDOCONTROL		0x1F
+#define PHY_CMDCONTROL		0x20
+#define PHY_DATACONTROL	0x21
+#define PHY_STROBECONTROL	0x22
+#define PHY_CLKCONTROL		0x23
+#define PHY_CONTROL		0x24
+
+#define DLL_ENABLE	BIT(3)
+#define RTRIM_ENABLE	BIT(1)
+#define PDB_ENABLE	BIT(1)
+#define RETB_ENABLE	BIT(6)
+#define ODEN_CMD	BIT(1)
+#define ODEN_DAT	0xFF
+#define REN_STRB	BIT(0)
+#define REN_CMD	BIT(1)
+#define REN_DAT	0xFF
+#define PU_CMD		BIT(1)
+#define PU_DAT		0xFF
+#define ITAP_DLY_EN	BIT(0)
+#define OTAP_DLY_EN	BIT(0)
+#define OD_REL_CMD	BIT(1)
+#define OD_REL_DAT	0xFF
+#define DLL_TRIM	0x8
+#define PDB_CMD	BIT(0)
+#define PDB_DAT	0xFF
+#define PDB_STRB	BIT(0)
+#define PDB_CLK	BIT(0)
+#define LDO_RDYB	0xFE
+#define CALDONE_MASK	0x10
+
+extern int arasan_pci_probe_slot(struct sdhci_pci_slot *slot);
+
+extern int arasan_pci_probe(struct sdhci_pci_chip *chip);
+
+extern void arasan_sdhci_set_clock(struct sdhci_host *host, unsigned int clock);
+
+#endif /* __SDHCI_PCI_ARASAN_H */
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index 5f3f7b5..3bd77dd 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -1112,6 +1112,21 @@  static const struct sdhci_pci_fixes sdhci_rtsx = {
 	.probe_slot	= rtsx_probe_slot,
 };
 
+static const struct sdhci_ops arasan_sdhci_pci_ops = {
+	.set_clock			= arasan_sdhci_set_clock,
+	.enable_dma			= sdhci_pci_enable_dma,
+	.set_bus_width			= sdhci_pci_set_bus_width,
+	.reset				= sdhci_reset,
+	.set_uhs_signaling		= sdhci_set_uhs_signaling,
+};
+
+static const struct sdhci_pci_fixes sdhci_arasan = {
+	.probe      = arasan_pci_probe,
+	.probe_slot = arasan_pci_probe_slot,
+	.ops	    = &arasan_sdhci_pci_ops,
+};
+
+
 /*AMD chipset generation*/
 enum amd_chipset_gen {
 	AMD_CHIPSET_BEFORE_ML,
@@ -1314,6 +1329,7 @@  static const struct pci_device_id pci_ids[] = {
 	SDHCI_PCI_DEVICE(O2, SDS1,     o2),
 	SDHCI_PCI_DEVICE(O2, SEABIRD0, o2),
 	SDHCI_PCI_DEVICE(O2, SEABIRD1, o2),
+	SDHCI_PCI_DEVICE(ARASAN, PHY_EMMC, arasan),
 	SDHCI_PCI_DEVICE_CLASS(AMD, SYSTEM_SDHCI, PCI_CLASS_MASK, amd),
 	/* Generic SD host controller */
 	{PCI_DEVICE_CLASS(SYSTEM_SDHCI, PCI_CLASS_MASK)},
diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
index 3c1dd79..e370836 100644
--- a/drivers/mmc/host/sdhci-pci.h
+++ b/drivers/mmc/host/sdhci-pci.h
@@ -48,6 +48,10 @@ 
 
 #define PCI_SUBDEVICE_ID_NI_7884	0x7884
 
+/* Arasan Device IDs */
+#define PCI_VENDOR_ID_ARASAN	0x16e6
+#define PCI_DEVICE_ID_ARASAN_PHY_EMMC	0x0670
+
 /*
  * PCI device class and mask
  */