All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 0/9] mmc: dw_mmc: add support for device tree based instantiation
@ 2012-08-29 10:48 Thomas Abraham
  2012-08-29 10:48 ` [PATCH v5 1/9] mmc: dw_mmc: convert copy of struct device in struct dw_mci to a reference Thomas Abraham
                   ` (8 more replies)
  0 siblings, 9 replies; 29+ messages in thread
From: Thomas Abraham @ 2012-08-29 10:48 UTC (permalink / raw)
  To: linux-mmc, devicetree-discuss
  Cc: will.newton, cjb, grant.likely, rob.herring, linux-samsung-soc,
	kgene.kim, girish.shivananjappa, jh80.chung, tgih.jun, patches

This is the fifth version of the patch series that adds device tree support
for Synopsis Designware Mobile Storage Host Controller. All the review comments
from the v4 version have been addressed and rebased on mmc-next branch. Since v4,
patches 6/9 and 9/9 have been modified. This has been tested on only smdk5250.

Thomas Abraham (9):
  mmc: dw_mmc: convert copy of struct device in struct dw_mci to a reference
  mmc: dw_mmc: Use devm_* functions in dw_mmc platform driver
  mmc: dw_mmc: allow probe to succeed even if one slot is initialized
  mmc: dw_mmc: lookup for optional biu and ciu clocks
  mmc: dw_mmc: add quirk to indicate missing write protect line
  mmc: dw_mmc: add device tree support
  mmc: dw_mmc: prepare functions in dw_mmc-pltfm for reuse
  mmc: dw_mmc: add support for implementation specific callbacks
  mmc: dw_mmc: add support for exynos specific implementation of dw-mshc

 .../devicetree/bindings/mmc/exynos-dw-mshc.txt     |   85 ++++++
 .../devicetree/bindings/mmc/synposis-dw-mshc.txt   |   79 ++++++
 drivers/mmc/host/Kconfig                           |    9 +
 drivers/mmc/host/Makefile                          |    1 +
 drivers/mmc/host/dw_mmc-exynos.c                   |  243 ++++++++++++++++
 drivers/mmc/host/dw_mmc-pci.c                      |    2 +-
 drivers/mmc/host/dw_mmc-pltfm.c                    |   60 +++--
 drivers/mmc/host/dw_mmc-pltfm.h                    |   20 ++
 drivers/mmc/host/dw_mmc.c                          |  289 ++++++++++++++++----
 drivers/mmc/host/dw_mmc.h                          |   22 ++
 include/linux/mmc/dw_mmc.h                         |   13 +-
 11 files changed, 748 insertions(+), 75 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
 create mode 100644 Documentation/devicetree/bindings/mmc/synposis-dw-mshc.txt
 create mode 100644 drivers/mmc/host/dw_mmc-exynos.c
 create mode 100644 drivers/mmc/host/dw_mmc-pltfm.h


^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH v5 1/9] mmc: dw_mmc: convert copy of struct device in struct dw_mci to a reference
  2012-08-29 10:48 [PATCH v5 0/9] mmc: dw_mmc: add support for device tree based instantiation Thomas Abraham
@ 2012-08-29 10:48 ` Thomas Abraham
  2012-08-29 10:48 ` [PATCH v5 2/9] mmc: dw_mmc: Use devm_* functions in dw_mmc platform driver Thomas Abraham
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 29+ messages in thread
From: Thomas Abraham @ 2012-08-29 10:48 UTC (permalink / raw)
  To: linux-mmc, devicetree-discuss
  Cc: will.newton, cjb, grant.likely, rob.herring, linux-samsung-soc,
	kgene.kim, girish.shivananjappa, jh80.chung, tgih.jun, patches

The 'struct dw_mci' maintains a copy of the pdev->dev instance instead of
maintaining a reference to that 'struct device' instance. Any resource
allocated using the device resource management kernel API with the instance
of 'struct device' in 'struct dw_mci' is then incorrect. Fix this by
converting the copy of 'struct device' in 'struct dw_mci' to a reference.

Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
Acked-by: Will Newton <will.newton@imgtec.com>
---
 drivers/mmc/host/dw_mmc-pci.c   |    2 +-
 drivers/mmc/host/dw_mmc-pltfm.c |    2 +-
 drivers/mmc/host/dw_mmc.c       |   56 +++++++++++++++++++-------------------
 include/linux/mmc/dw_mmc.h      |    2 +-
 4 files changed, 31 insertions(+), 31 deletions(-)

diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c
index dc0d25a..592545a 100644
--- a/drivers/mmc/host/dw_mmc-pci.c
+++ b/drivers/mmc/host/dw_mmc-pci.c
@@ -59,7 +59,7 @@ static int __devinit dw_mci_pci_probe(struct pci_dev *pdev,
 
 	host->irq = pdev->irq;
 	host->irq_flags = IRQF_SHARED;
-	host->dev = pdev->dev;
+	host->dev = &pdev->dev;
 	host->pdata = &pci_board_data;
 
 	host->regs = pci_iomap(pdev, PCI_BAR_NO, COMPLETE_BAR);
diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index a54d3a3..528d37b 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -43,7 +43,7 @@ static int __devinit dw_mci_pltfm_probe(struct platform_device *pdev)
 		goto err_free;
 	}
 
-	host->dev = pdev->dev;
+	host->dev = &pdev->dev;
 	host->irq_flags = 0;
 	host->pdata = pdev->dev.platform_data;
 	ret = -ENOMEM;
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 36f98c0..a18e73c 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -266,7 +266,7 @@ static void dw_mci_start_command(struct dw_mci *host,
 				 struct mmc_command *cmd, u32 cmd_flags)
 {
 	host->cmd = cmd;
-	dev_vdbg(&host->dev,
+	dev_vdbg(host->dev,
 		 "start command: ARGR=0x%08x CMDR=0x%08x\n",
 		 cmd->arg, cmd_flags);
 
@@ -308,7 +308,7 @@ static void dw_mci_dma_cleanup(struct dw_mci *host)
 
 	if (data)
 		if (!data->host_cookie)
-			dma_unmap_sg(&host->dev,
+			dma_unmap_sg(host->dev,
 				     data->sg,
 				     data->sg_len,
 				     dw_mci_get_dma_dir(data));
@@ -334,7 +334,7 @@ static void dw_mci_idmac_complete_dma(struct dw_mci *host)
 {
 	struct mmc_data *data = host->data;
 
-	dev_vdbg(&host->dev, "DMA complete\n");
+	dev_vdbg(host->dev, "DMA complete\n");
 
 	host->dma_ops->cleanup(host);
 
@@ -414,13 +414,13 @@ static int dw_mci_idmac_init(struct dw_mci *host)
 	dma_support = (mci_readl(host, HCON) >> 16) & 0x3;
 
 	if (!dma_support || dma_support > 2) {
-		dev_err(&host->dev,
+		dev_err(host->dev,
 			"Host Controller does not support IDMA Tx.\n");
 		host->dma_ops = NULL;
 		return -ENODEV;
 	}
 
-	dev_info(&host->dev, "Using internal DMA controller.\n");
+	dev_info(host->dev, "Using internal DMA controller.\n");
 
 	/* Forward link the descriptor list */
 	for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
@@ -476,7 +476,7 @@ static int dw_mci_pre_dma_transfer(struct dw_mci *host,
 			return -EINVAL;
 	}
 
-	sg_len = dma_map_sg(&host->dev,
+	sg_len = dma_map_sg(host->dev,
 			    data->sg,
 			    data->sg_len,
 			    dw_mci_get_dma_dir(data));
@@ -519,7 +519,7 @@ static void dw_mci_post_req(struct mmc_host *mmc,
 		return;
 
 	if (data->host_cookie)
-		dma_unmap_sg(&slot->host->dev,
+		dma_unmap_sg(slot->host->dev,
 			     data->sg,
 			     data->sg_len,
 			     dw_mci_get_dma_dir(data));
@@ -545,7 +545,7 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
 
 	host->using_dma = 1;
 
-	dev_vdbg(&host->dev,
+	dev_vdbg(host->dev,
 		 "sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n",
 		 (unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
 		 sg_len);
@@ -939,12 +939,12 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
 		slot = list_entry(host->queue.next,
 				  struct dw_mci_slot, queue_node);
 		list_del(&slot->queue_node);
-		dev_vdbg(&host->dev, "list not empty: %s is next\n",
+		dev_vdbg(host->dev, "list not empty: %s is next\n",
 			 mmc_hostname(slot->mmc));
 		host->state = STATE_SENDING_CMD;
 		dw_mci_start_request(host, slot);
 	} else {
-		dev_vdbg(&host->dev, "list empty\n");
+		dev_vdbg(host->dev, "list empty\n");
 		host->state = STATE_IDLE;
 	}
 
@@ -1083,7 +1083,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
 					data->bytes_xfered = 0;
 					data->error = -ETIMEDOUT;
 				} else {
-					dev_err(&host->dev,
+					dev_err(host->dev,
 						"data FIFO error "
 						"(status=%08x)\n",
 						status);
@@ -1772,7 +1772,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 	struct mmc_host *mmc;
 	struct dw_mci_slot *slot;
 
-	mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), &host->dev);
+	mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
 	if (!mmc)
 		return -ENOMEM;
 
@@ -1884,10 +1884,10 @@ static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id)
 static void dw_mci_init_dma(struct dw_mci *host)
 {
 	/* Alloc memory for sg translation */
-	host->sg_cpu = dma_alloc_coherent(&host->dev, PAGE_SIZE,
+	host->sg_cpu = dma_alloc_coherent(host->dev, PAGE_SIZE,
 					  &host->sg_dma, GFP_KERNEL);
 	if (!host->sg_cpu) {
-		dev_err(&host->dev, "%s: could not alloc DMA memory\n",
+		dev_err(host->dev, "%s: could not alloc DMA memory\n",
 			__func__);
 		goto no_dma;
 	}
@@ -1903,12 +1903,12 @@ static void dw_mci_init_dma(struct dw_mci *host)
 	if (host->dma_ops->init && host->dma_ops->start &&
 	    host->dma_ops->stop && host->dma_ops->cleanup) {
 		if (host->dma_ops->init(host)) {
-			dev_err(&host->dev, "%s: Unable to initialize "
+			dev_err(host->dev, "%s: Unable to initialize "
 				"DMA Controller.\n", __func__);
 			goto no_dma;
 		}
 	} else {
-		dev_err(&host->dev, "DMA initialization not found.\n");
+		dev_err(host->dev, "DMA initialization not found.\n");
 		goto no_dma;
 	}
 
@@ -1916,7 +1916,7 @@ static void dw_mci_init_dma(struct dw_mci *host)
 	return;
 
 no_dma:
-	dev_info(&host->dev, "Using PIO mode.\n");
+	dev_info(host->dev, "Using PIO mode.\n");
 	host->use_dma = 0;
 	return;
 }
@@ -1948,19 +1948,19 @@ int dw_mci_probe(struct dw_mci *host)
 	u32 fifo_size;
 
 	if (!host->pdata || !host->pdata->init) {
-		dev_err(&host->dev,
+		dev_err(host->dev,
 			"Platform data must supply init function\n");
 		return -ENODEV;
 	}
 
 	if (!host->pdata->select_slot && host->pdata->num_slots > 1) {
-		dev_err(&host->dev,
+		dev_err(host->dev,
 			"Platform data must supply select_slot function\n");
 		return -ENODEV;
 	}
 
 	if (!host->pdata->bus_hz) {
-		dev_err(&host->dev,
+		dev_err(host->dev,
 			"Platform data must supply bus speed\n");
 		return -ENODEV;
 	}
@@ -1998,7 +1998,7 @@ int dw_mci_probe(struct dw_mci *host)
 	}
 
 	/* Reset all blocks */
-	if (!mci_wait_reset(&host->dev, host))
+	if (!mci_wait_reset(host->dev, host))
 		return -ENODEV;
 
 	host->dma_ops = host->pdata->dma_ops;
@@ -2065,7 +2065,7 @@ int dw_mci_probe(struct dw_mci *host)
 	 * Need to check the version-id and set data-offset for DATA register.
 	 */
 	host->verid = SDMMC_GET_VERID(mci_readl(host, VERID));
-	dev_info(&host->dev, "Version ID is %04x\n", host->verid);
+	dev_info(host->dev, "Version ID is %04x\n", host->verid);
 
 	if (host->verid < DW_MMC_240A)
 		host->data_offset = DATA_OFFSET;
@@ -2082,12 +2082,12 @@ int dw_mci_probe(struct dw_mci *host)
 		   DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
 	mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */
 
-	dev_info(&host->dev, "DW MMC controller at irq %d, "
+	dev_info(host->dev, "DW MMC controller at irq %d, "
 		 "%d bit host data width, "
 		 "%u deep fifo\n",
 		 host->irq, width, fifo_size);
 	if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
-		dev_info(&host->dev, "Internal DMAC interrupt fix enabled.\n");
+		dev_info(host->dev, "Internal DMAC interrupt fix enabled.\n");
 
 	return 0;
 
@@ -2106,7 +2106,7 @@ err_workqueue:
 err_dmaunmap:
 	if (host->use_dma && host->dma_ops->exit)
 		host->dma_ops->exit(host);
-	dma_free_coherent(&host->dev, PAGE_SIZE,
+	dma_free_coherent(host->dev, PAGE_SIZE,
 			  host->sg_cpu, host->sg_dma);
 
 	if (host->vmmc) {
@@ -2125,7 +2125,7 @@ void dw_mci_remove(struct dw_mci *host)
 	mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
 
 	for (i = 0; i < host->num_slots; i++) {
-		dev_dbg(&host->dev, "remove slot %d\n", i);
+		dev_dbg(host->dev, "remove slot %d\n", i);
 		if (host->slot[i])
 			dw_mci_cleanup_slot(host->slot[i], i);
 	}
@@ -2136,7 +2136,7 @@ void dw_mci_remove(struct dw_mci *host)
 
 	free_irq(host->irq, host);
 	destroy_workqueue(host->card_workqueue);
-	dma_free_coherent(&host->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+	dma_free_coherent(host->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
 
 	if (host->use_dma && host->dma_ops->exit)
 		host->dma_ops->exit(host);
@@ -2188,7 +2188,7 @@ int dw_mci_resume(struct dw_mci *host)
 	if (host->vmmc)
 		regulator_enable(host->vmmc);
 
-	if (!mci_wait_reset(&host->dev, host)) {
+	if (!mci_wait_reset(host->dev, host)) {
 		ret = -ENODEV;
 		return ret;
 	}
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
index 7a7ebd3..a37a573 100644
--- a/include/linux/mmc/dw_mmc.h
+++ b/include/linux/mmc/dw_mmc.h
@@ -156,7 +156,7 @@ struct dw_mci {
 	u32			fifoth_val;
 	u16			verid;
 	u16			data_offset;
-	struct device		dev;
+	struct device		*dev;
 	struct dw_mci_board	*pdata;
 	struct dw_mci_slot	*slot[MAX_MCI_SLOTS];
 
-- 
1.6.6.rc2

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v5 2/9] mmc: dw_mmc: Use devm_* functions in dw_mmc platform driver
  2012-08-29 10:48 [PATCH v5 0/9] mmc: dw_mmc: add support for device tree based instantiation Thomas Abraham
  2012-08-29 10:48 ` [PATCH v5 1/9] mmc: dw_mmc: convert copy of struct device in struct dw_mci to a reference Thomas Abraham
@ 2012-08-29 10:48 ` Thomas Abraham
  2012-08-29 10:48 ` [PATCH v5 3/9] mmc: dw_mmc: allow probe to succeed even if one slot is initialized Thomas Abraham
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 29+ messages in thread
From: Thomas Abraham @ 2012-08-29 10:48 UTC (permalink / raw)
  To: linux-mmc, devicetree-discuss
  Cc: will.newton, cjb, grant.likely, rob.herring, linux-samsung-soc,
	kgene.kim, girish.shivananjappa, jh80.chung, tgih.jun, patches

Use devm_* managed functions for simpler error handling.

Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
Acked-by: Will Newton <will.newton@imgtec.com>
---
 drivers/mmc/host/dw_mmc-pltfm.c |   29 ++++++++---------------------
 1 files changed, 8 insertions(+), 21 deletions(-)

diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index 528d37b..ce7be1a 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -27,38 +27,27 @@ static int __devinit dw_mci_pltfm_probe(struct platform_device *pdev)
 	struct resource	*regs;
 	int ret;
 
-	host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL);
+	host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL);
 	if (!host)
 		return -ENOMEM;
 
 	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!regs) {
-		ret = -ENXIO;
-		goto err_free;
-	}
+	if (!regs)
+		return -ENXIO;
 
 	host->irq = platform_get_irq(pdev, 0);
-	if (host->irq < 0) {
-		ret = host->irq;
-		goto err_free;
-	}
+	if (host->irq < 0)
+		return host->irq;
 
 	host->dev = &pdev->dev;
 	host->irq_flags = 0;
 	host->pdata = pdev->dev.platform_data;
-	ret = -ENOMEM;
-	host->regs = ioremap(regs->start, resource_size(regs));
+	host->regs = devm_request_and_ioremap(&pdev->dev, regs);
 	if (!host->regs)
-		goto err_free;
+		return -ENOMEM;
+
 	platform_set_drvdata(pdev, host);
 	ret = dw_mci_probe(host);
-	if (ret)
-		goto err_out;
-	return ret;
-err_out:
-	iounmap(host->regs);
-err_free:
-	kfree(host);
 	return ret;
 }
 
@@ -68,8 +57,6 @@ static int __devexit dw_mci_pltfm_remove(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, NULL);
 	dw_mci_remove(host);
-	iounmap(host->regs);
-	kfree(host);
 	return 0;
 }
 
-- 
1.6.6.rc2

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v5 3/9] mmc: dw_mmc: allow probe to succeed even if one slot is initialized
  2012-08-29 10:48 [PATCH v5 0/9] mmc: dw_mmc: add support for device tree based instantiation Thomas Abraham
  2012-08-29 10:48 ` [PATCH v5 1/9] mmc: dw_mmc: convert copy of struct device in struct dw_mci to a reference Thomas Abraham
  2012-08-29 10:48 ` [PATCH v5 2/9] mmc: dw_mmc: Use devm_* functions in dw_mmc platform driver Thomas Abraham
@ 2012-08-29 10:48 ` Thomas Abraham
  2012-08-29 10:48 ` [PATCH v5 4/9] mmc: dw_mmc: lookup for optional biu and ciu clocks Thomas Abraham
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 29+ messages in thread
From: Thomas Abraham @ 2012-08-29 10:48 UTC (permalink / raw)
  To: linux-mmc, devicetree-discuss
  Cc: will.newton, cjb, grant.likely, rob.herring, linux-samsung-soc,
	kgene.kim, girish.shivananjappa, jh80.chung, tgih.jun, patches

Instead of aborting the probe in case a slot initialization fails, allow
initialization of as many slots as possible. If there are atleast one
instance of slot that is successfully initialized, allow the driver probe
to succeed.

Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
Acked-by: Will Newton <will.newton@imgtec.com>
---
 drivers/mmc/host/dw_mmc.c |   23 +++++++++++++----------
 1 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index a18e73c..227c42e 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -1946,6 +1946,7 @@ int dw_mci_probe(struct dw_mci *host)
 {
 	int width, i, ret = 0;
 	u32 fifo_size;
+	int init_slots = 0;
 
 	if (!host->pdata || !host->pdata->init) {
 		dev_err(host->dev,
@@ -2054,10 +2055,18 @@ int dw_mci_probe(struct dw_mci *host)
 	/* We need at least one slot to succeed */
 	for (i = 0; i < host->num_slots; i++) {
 		ret = dw_mci_init_slot(host, i);
-		if (ret) {
-			ret = -ENODEV;
-			goto err_init_slot;
-		}
+		if (ret)
+			dev_dbg(host->dev, "slot %d init failed\n", i);
+		else
+			init_slots++;
+	}
+
+	if (init_slots) {
+		dev_info(host->dev, "%d slots initialized\n", init_slots);
+	} else {
+		dev_dbg(host->dev, "attempted to initialize %d slots, "
+					"but failed on all\n", host->num_slots);
+		goto err_init_slot;
 	}
 
 	/*
@@ -2092,12 +2101,6 @@ int dw_mci_probe(struct dw_mci *host)
 	return 0;
 
 err_init_slot:
-	/* De-init any initialized slots */
-	while (i > 0) {
-		if (host->slot[i])
-			dw_mci_cleanup_slot(host->slot[i], i);
-		i--;
-	}
 	free_irq(host->irq, host);
 
 err_workqueue:
-- 
1.6.6.rc2


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v5 4/9] mmc: dw_mmc: lookup for optional biu and ciu clocks
  2012-08-29 10:48 [PATCH v5 0/9] mmc: dw_mmc: add support for device tree based instantiation Thomas Abraham
                   ` (2 preceding siblings ...)
  2012-08-29 10:48 ` [PATCH v5 3/9] mmc: dw_mmc: allow probe to succeed even if one slot is initialized Thomas Abraham
@ 2012-08-29 10:48 ` Thomas Abraham
  2012-08-30  8:48   ` Jaehoon Chung
  2012-08-29 10:48 ` [PATCH v5 5/9] mmc: dw_mmc: add quirk to indicate missing write protect line Thomas Abraham
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 29+ messages in thread
From: Thomas Abraham @ 2012-08-29 10:48 UTC (permalink / raw)
  To: linux-mmc, devicetree-discuss
  Cc: will.newton, cjb, grant.likely, rob.herring, linux-samsung-soc,
	kgene.kim, girish.shivananjappa, jh80.chung, tgih.jun, patches

Some platforms allow for clock gating and control of bus interface unit clock
and card interface unit clock. Add support for clock lookup of optional biu
and ciu clocks for clock gating and clock speed determination.

Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
Acked-by: Will Newton <will.newton@imgtec.com>
---
 drivers/mmc/host/dw_mmc.c  |   42 +++++++++++++++++++++++++++++++++++++++---
 include/linux/mmc/dw_mmc.h |    4 ++++
 2 files changed, 43 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 227c42e..90c7c7b 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -1960,18 +1960,38 @@ int dw_mci_probe(struct dw_mci *host)
 		return -ENODEV;
 	}
 
-	if (!host->pdata->bus_hz) {
+	host->biu_clk = clk_get(host->dev, "biu");
+	if (IS_ERR(host->biu_clk))
+		dev_dbg(host->dev, "biu clock not available\n");
+	else
+		clk_prepare_enable(host->biu_clk);
+
+	host->ciu_clk = clk_get(host->dev, "ciu");
+	if (IS_ERR(host->ciu_clk))
+		dev_dbg(host->dev, "ciu clock not available\n");
+	else
+		clk_prepare_enable(host->ciu_clk);
+
+	if (IS_ERR(host->ciu_clk))
+		host->bus_hz = host->pdata->bus_hz;
+	else
+		host->bus_hz = clk_get_rate(host->ciu_clk);
+
+	if (!host->bus_hz) {
 		dev_err(host->dev,
 			"Platform data must supply bus speed\n");
-		return -ENODEV;
+		ret = -ENODEV;
+		goto err_clk;
 	}
 
-	host->bus_hz = host->pdata->bus_hz;
 	host->quirks = host->pdata->quirks;
 
 	spin_lock_init(&host->lock);
 	INIT_LIST_HEAD(&host->queue);
 
+	host->dma_ops = host->pdata->dma_ops;
+	dw_mci_init_dma(host);
+
 	/*
 	 * Get the host data width - this assumes that HCON has been set with
 	 * the correct values.
@@ -2116,6 +2136,16 @@ err_dmaunmap:
 		regulator_disable(host->vmmc);
 		regulator_put(host->vmmc);
 	}
+
+err_clk:
+	if (!IS_ERR(host->ciu_clk)) {
+		clk_disable_unprepare(host->ciu_clk);
+		clk_put(host->ciu_clk);
+	}
+	if (!IS_ERR(host->biu_clk)) {
+		clk_disable_unprepare(host->biu_clk);
+		clk_put(host->biu_clk);
+	}
 	return ret;
 }
 EXPORT_SYMBOL(dw_mci_probe);
@@ -2149,6 +2179,12 @@ void dw_mci_remove(struct dw_mci *host)
 		regulator_put(host->vmmc);
 	}
 
+	if (!IS_ERR(host->ciu_clk))
+		clk_disable_unprepare(host->ciu_clk);
+	if (!IS_ERR(host->biu_clk))
+		clk_disable_unprepare(host->biu_clk);
+	clk_put(host->ciu_clk);
+	clk_put(host->biu_clk);
 }
 EXPORT_SYMBOL(dw_mci_remove);
 
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
index a37a573..787ad56 100644
--- a/include/linux/mmc/dw_mmc.h
+++ b/include/linux/mmc/dw_mmc.h
@@ -78,6 +78,8 @@ struct mmc_data;
  * @data_offset: Set the offset of DATA register according to VERID.
  * @dev: Device associated with the MMC controller.
  * @pdata: Platform data associated with the MMC controller.
+ * @biu_clk: Pointer to bus interface unit clock instance.
+ * @ciu_clk: Pointer to card interface unit clock instance.
  * @slot: Slots sharing this MMC controller.
  * @fifo_depth: depth of FIFO.
  * @data_shift: log2 of FIFO item size.
@@ -158,6 +160,8 @@ struct dw_mci {
 	u16			data_offset;
 	struct device		*dev;
 	struct dw_mci_board	*pdata;
+	struct clk		*biu_clk;
+	struct clk		*ciu_clk;
 	struct dw_mci_slot	*slot[MAX_MCI_SLOTS];
 
 	/* FIFO push and pull */
-- 
1.6.6.rc2

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v5 5/9] mmc: dw_mmc: add quirk to indicate missing write protect line
  2012-08-29 10:48 [PATCH v5 0/9] mmc: dw_mmc: add support for device tree based instantiation Thomas Abraham
                   ` (3 preceding siblings ...)
  2012-08-29 10:48 ` [PATCH v5 4/9] mmc: dw_mmc: lookup for optional biu and ciu clocks Thomas Abraham
@ 2012-08-29 10:48 ` Thomas Abraham
  2012-08-29 10:48 ` [PATCH v5 6/9] mmc: dw_mmc: add device tree support Thomas Abraham
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 29+ messages in thread
From: Thomas Abraham @ 2012-08-29 10:48 UTC (permalink / raw)
  To: linux-mmc, devicetree-discuss
  Cc: will.newton, cjb, grant.likely, rob.herring, linux-samsung-soc,
	kgene.kim, girish.shivananjappa, jh80.chung, tgih.jun, patches

If the write protect pad of the controller is not connected to the write
protect pin of the slot, the driver should be notified of this condition
so that incorrect check for write protection by reading the WRTORT
register can avoided. The get_ro platform callback can be used for in
such cases, but with device tree support enabled, such platform callbacks
cannot be supported.

Add a new quirk for notifying the driver about the missing write protect
line so the driver can assume that the card write protection is disabled.

Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
Acked-by: Will Newton <will.newton@imgtec.com>
---
 drivers/mmc/host/dw_mmc.c  |    4 +++-
 include/linux/mmc/dw_mmc.h |    3 ++-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 90c7c7b..1799a2d 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -830,7 +830,9 @@ static int dw_mci_get_ro(struct mmc_host *mmc)
 	struct dw_mci_board *brd = slot->host->pdata;
 
 	/* Use platform get_ro function, else try on board write protect */
-	if (brd->get_ro)
+	if (brd->quirks & DW_MCI_QUIRK_NO_WRITE_PROTECT)
+		read_only = 0;
+	else if (brd->get_ro)
 		read_only = brd->get_ro(slot->id);
 	else
 		read_only =
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
index 787ad56..b72e4aa 100644
--- a/include/linux/mmc/dw_mmc.h
+++ b/include/linux/mmc/dw_mmc.h
@@ -205,7 +205,8 @@ struct dw_mci_dma_ops {
 #define DW_MCI_QUIRK_HIGHSPEED			BIT(2)
 /* Unreliable card detection */
 #define DW_MCI_QUIRK_BROKEN_CARD_DETECTION	BIT(3)
-
+/* Write Protect detection not available */
+#define DW_MCI_QUIRK_NO_WRITE_PROTECT		BIT(4)
 
 struct dma_pdata;
 
-- 
1.6.6.rc2

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v5 6/9] mmc: dw_mmc: add device tree support
  2012-08-29 10:48 [PATCH v5 0/9] mmc: dw_mmc: add support for device tree based instantiation Thomas Abraham
                   ` (4 preceding siblings ...)
  2012-08-29 10:48 ` [PATCH v5 5/9] mmc: dw_mmc: add quirk to indicate missing write protect line Thomas Abraham
@ 2012-08-29 10:48 ` Thomas Abraham
  2012-08-29 10:48 ` [PATCH v5 7/9] mmc: dw_mmc: prepare functions in dw_mmc-pltfm for reuse Thomas Abraham
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 29+ messages in thread
From: Thomas Abraham @ 2012-08-29 10:48 UTC (permalink / raw)
  To: linux-mmc, devicetree-discuss
  Cc: will.newton, cjb, grant.likely, rob.herring, linux-samsung-soc,
	kgene.kim, girish.shivananjappa, jh80.chung, tgih.jun, patches

Add device tree based discovery support.

Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
Acked-by: Will Newton <will.newton@imgtec.com>
---
 .../devicetree/bindings/mmc/synposis-dw-mshc.txt   |   79 ++++++++++++
 drivers/mmc/host/dw_mmc-pltfm.c                    |    9 ++
 drivers/mmc/host/dw_mmc.c                          |  130 ++++++++++++++++++-
 3 files changed, 211 insertions(+), 7 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mmc/synposis-dw-mshc.txt

diff --git a/Documentation/devicetree/bindings/mmc/synposis-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synposis-dw-mshc.txt
new file mode 100644
index 0000000..06cd32d
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/synposis-dw-mshc.txt
@@ -0,0 +1,79 @@
+* Synopsis Designware Mobile Storage Host Controller
+
+The Synopsis designware mobile storage host controller is used to interface
+a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
+differences between the core mmc properties described by mmc.txt and the
+properties used by the Synopsis Designware Mobile Storage Host Controller.
+
+Required Properties:
+
+* compatible: should be
+	- snps,dw-mshc: for controllers compliant with synopsis dw-mshc.
+* #address-cells: should be 1.
+* #size-cells: should be 0.
+
+# Slots: The slot specific information are contained within child-nodes with
+  each child-node representing a supported slot. There should be atleast one
+  child node representing a card slot. The name of the child node representing
+  the slot is recommended to be slot@n where n is the unique number of the slot
+  connnected to the controller. The following are optional properties which
+  can be included in the slot child node.
+
+	* reg: specifies the physical slot number. The valid values of this
+	  property is 0 to (num-slots -1), where num-slots is the value
+	  specified by the num-slots property.
+
+	* bus-width: as documented in mmc core bindings.
+
+	* wp-gpios: specifies the write protect gpio line. The format of the
+	  gpio specifier depends on the gpio controller. If the write-protect
+	  line is not available, this property is optional.
+
+Optional properties:
+
+* num-slots: specifies the number of slots supported by the controller.
+  The number of physical slots actually used could be equal or less than the
+  value specified by num-slots. If this property is not specified, the value
+  of num-slot property is assumed to be 1.
+
+* fifo-depth: The maximum size of the tx/rx fifo's. If this property is not
+  specified, the default value of the fifo size is determined from the
+  controller registers.
+
+* card-detect-delay: Delay in milli-seconds before detecting card after card
+  insert event. The default value is 0.
+
+* supports-highspeed: Enables support for high speed cards (upto 50MHz)
+
+* broken-cd: as documented in mmc core bindings.
+
+Aliases:
+
+- All the MSHC controller nodes should be represented in the aliases node using
+  the following format 'mshc{n}' where n is a unique number for the alias.
+
+Example:
+
+The MSHC controller node can be split into two portions, SoC specific and
+board specific portions as listed below.
+
+	dwmmc0@12200000 {
+		compatible = "snps,dw-mshc";
+		reg = <0x12200000 0x1000>;
+		interrupts = <0 75 0>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+	};
+
+	dwmmc0@12200000 {
+		num-slots = <1>;
+		supports-highspeed;
+		broken-cd;
+		fifo-depth = <0x80>;
+		card-detect-delay = <200>;
+
+		slot@0 {
+			reg = <0>;
+			bus-width = <8>;
+		};
+	};
diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index ce7be1a..7205905 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -19,6 +19,8 @@
 #include <linux/mmc/host.h>
 #include <linux/mmc/mmc.h>
 #include <linux/mmc/dw_mmc.h>
+#include <linux/of.h>
+
 #include "dw_mmc.h"
 
 static int __devinit dw_mci_pltfm_probe(struct platform_device *pdev)
@@ -94,10 +96,17 @@ static int dw_mci_pltfm_resume(struct device *dev)
 
 static SIMPLE_DEV_PM_OPS(dw_mci_pltfm_pmops, dw_mci_pltfm_suspend, dw_mci_pltfm_resume);
 
+static const struct of_device_id dw_mci_pltfm_match[] = {
+	{ .compatible = "snps,dw-mshc", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
+
 static struct platform_driver dw_mci_pltfm_driver = {
 	.remove		= __exit_p(dw_mci_pltfm_remove),
 	.driver		= {
 		.name		= "dw_mmc",
+		.of_match_table	= of_match_ptr(dw_mci_pltfm_match),
 		.pm		= &dw_mci_pltfm_pmops,
 	},
 };
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 1799a2d..1308619 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -33,6 +33,7 @@
 #include <linux/bitops.h>
 #include <linux/regulator/consumer.h>
 #include <linux/workqueue.h>
+#include <linux/of.h>
 
 #include "dw_mmc.h"
 
@@ -1769,10 +1770,57 @@ static void dw_mci_work_routine_card(struct work_struct *work)
 	}
 }
 
+#ifdef CONFIG_OF
+/* given a slot id, find out the device node representing that slot */
+static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot)
+{
+	struct device_node *np;
+	const __be32 *addr;
+	int len;
+
+	if (!dev || !dev->of_node)
+		return NULL;
+
+	for_each_child_of_node(dev->of_node, np) {
+		addr = of_get_property(np, "reg", &len);
+		if (!addr || (len < sizeof(int)))
+			continue;
+		if (be32_to_cpup(addr) == slot)
+			return np;
+	}
+	return NULL;
+}
+
+/* find out bus-width for a given slot */
+static u32 dw_mci_of_get_bus_wd(struct device *dev, u8 slot)
+{
+	struct device_node *np = dw_mci_of_find_slot_node(dev, slot);
+	u32 bus_wd = 1;
+
+	if (!np)
+		return 1;
+
+	if (of_property_read_u32(np, "bus-width", &bus_wd))
+		dev_err(dev, "bus-width property not found, assuming width"
+			       " as 1\n");
+	return bus_wd;
+}
+#else /* CONFIG_OF */
+static u32 dw_mci_of_get_bus_wd(struct device *dev, u8 slot)
+{
+	return 1;
+}
+static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot)
+{
+	return NULL;
+}
+#endif /* CONFIG_OF */
+
 static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 {
 	struct mmc_host *mmc;
 	struct dw_mci_slot *slot;
+	u8 bus_width;
 
 	mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
 	if (!mmc)
@@ -1782,6 +1830,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 	slot->id = id;
 	slot->mmc = mmc;
 	slot->host = host;
+	host->slot[id] = slot;
 
 	mmc->ops = &dw_mci_ops;
 	mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510);
@@ -1806,8 +1855,18 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 		mmc->caps2 = host->pdata->caps2;
 
 	if (host->pdata->get_bus_wd)
-		if (host->pdata->get_bus_wd(slot->id) >= 4)
-			mmc->caps |= MMC_CAP_4_BIT_DATA;
+		bus_width = host->pdata->get_bus_wd(slot->id);
+	else if (host->dev->of_node)
+		bus_width = dw_mci_of_get_bus_wd(host->dev, slot->id);
+	else
+		bus_width = 1;
+
+	switch (bus_width) {
+	case 8:
+		mmc->caps |= MMC_CAP_8_BIT_DATA;
+	case 4:
+		mmc->caps |= MMC_CAP_4_BIT_DATA;
+	}
 
 	if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
 		mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
@@ -1852,7 +1911,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 	else
 		clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
 
-	host->slot[id] = slot;
 	mmc_add_host(mmc);
 
 #if defined(CONFIG_DEBUG_FS)
@@ -1944,16 +2002,74 @@ static bool mci_wait_reset(struct device *dev, struct dw_mci *host)
 	return false;
 }
 
+#ifdef CONFIG_OF
+static struct dw_mci_of_quirks {
+	char *quirk;
+	int id;
+} of_quirks[] = {
+	{
+		.quirk	= "supports-highspeed",
+		.id	= DW_MCI_QUIRK_HIGHSPEED,
+	}, {
+		.quirk	= "broken-cd",
+		.id	= DW_MCI_QUIRK_BROKEN_CARD_DETECTION,
+	},
+};
+
+static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
+{
+	struct dw_mci_board *pdata;
+	struct device *dev = host->dev;
+	struct device_node *np = dev->of_node;
+	int idx;
+
+	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata) {
+		dev_err(dev, "could not allocate memory for pdata\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	/* find out number of slots supported */
+	if (of_property_read_u32(dev->of_node, "num-slots",
+				&pdata->num_slots)) {
+		dev_info(dev, "num-slots property not found, "
+				"assuming 1 slot is available\n");
+		pdata->num_slots = 1;
+	}
+
+	/* get quirks */
+	for (idx = 0; idx < ARRAY_SIZE(of_quirks); idx++)
+		if (of_get_property(np, of_quirks[idx].quirk, NULL))
+			pdata->quirks |= of_quirks[idx].id;
+
+	if (of_property_read_u32(np, "fifo-depth", &pdata->fifo_depth))
+		dev_info(dev, "fifo-depth property not found, using "
+				"value of FIFOTH register as default\n");
+
+	of_property_read_u32(np, "card-detect-delay", &pdata->detect_delay_ms);
+
+	return pdata;
+}
+
+#else /* CONFIG_OF */
+static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
+{
+	return ERR_PTR(-EINVAL);
+}
+#endif /* CONFIG_OF */
+
 int dw_mci_probe(struct dw_mci *host)
 {
 	int width, i, ret = 0;
 	u32 fifo_size;
 	int init_slots = 0;
 
-	if (!host->pdata || !host->pdata->init) {
-		dev_err(host->dev,
-			"Platform data must supply init function\n");
-		return -ENODEV;
+	if (!host->pdata) {
+		host->pdata = dw_mci_parse_dt(host);
+		if (IS_ERR(host->pdata)) {
+			dev_err(host->dev, "platform data not available\n");
+			return -EINVAL;
+		}
 	}
 
 	if (!host->pdata->select_slot && host->pdata->num_slots > 1) {
-- 
1.6.6.rc2

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v5 7/9] mmc: dw_mmc: prepare functions in dw_mmc-pltfm for reuse
  2012-08-29 10:48 [PATCH v5 0/9] mmc: dw_mmc: add support for device tree based instantiation Thomas Abraham
                   ` (5 preceding siblings ...)
  2012-08-29 10:48 ` [PATCH v5 6/9] mmc: dw_mmc: add device tree support Thomas Abraham
@ 2012-08-29 10:48 ` Thomas Abraham
  2012-08-29 10:48 ` [PATCH v5 8/9] mmc: dw_mmc: add support for implementation specific callbacks Thomas Abraham
  2012-08-29 10:48 ` [PATCH v5 9/9] mmc: dw_mmc: add support for exynos specific implementation of dw-mshc Thomas Abraham
  8 siblings, 0 replies; 29+ messages in thread
From: Thomas Abraham @ 2012-08-29 10:48 UTC (permalink / raw)
  To: linux-mmc, devicetree-discuss
  Cc: will.newton, cjb, grant.likely, rob.herring, linux-samsung-soc,
	kgene.kim, girish.shivananjappa, jh80.chung, tgih.jun, patches

Platform implementations of dw-mshc controller may choose to exterd the features
of the standard dw-mshc controller such as adding additional clocking options or
modifying the bus interface. Support for such implementation specific extensions
can be incorporated into dw_mmc-pltfm, but including multiple such platform
specific implementations would convolute the existing dw_mmc-pltfm code.

Instead, it would be better to create implementation specific platform drivers
to support implementation specific features. Such platforms drivers can reuse
the existing infrastructure in dw_mmc-pltfm for resource identification and
controller registration and provide support for implementation specific
features. So, allow the infrastructure in dw_mmc-pltfm to be reused by other
implementation specific platform drivers.

Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
Acked-by: Will Newton <will.newton@imgtec.com>
---
 drivers/mmc/host/dw_mmc-pltfm.c |   12 ++++++++++--
 drivers/mmc/host/dw_mmc-pltfm.h |   19 +++++++++++++++++++
 2 files changed, 29 insertions(+), 2 deletions(-)
 create mode 100644 drivers/mmc/host/dw_mmc-pltfm.h

diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index 7205905..e17da91 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -23,7 +23,7 @@
 
 #include "dw_mmc.h"
 
-static int __devinit dw_mci_pltfm_probe(struct platform_device *pdev)
+int dw_mci_pltfm_register(struct platform_device *pdev)
 {
 	struct dw_mci *host;
 	struct resource	*regs;
@@ -52,6 +52,12 @@ static int __devinit dw_mci_pltfm_probe(struct platform_device *pdev)
 	ret = dw_mci_probe(host);
 	return ret;
 }
+EXPORT_SYMBOL_GPL(dw_mci_pltfm_register);
+
+static int __devinit dw_mci_pltfm_probe(struct platform_device *pdev)
+{
+	return dw_mci_pltfm_register(pdev);
+}
 
 static int __devexit dw_mci_pltfm_remove(struct platform_device *pdev)
 {
@@ -61,6 +67,7 @@ static int __devexit dw_mci_pltfm_remove(struct platform_device *pdev)
 	dw_mci_remove(host);
 	return 0;
 }
+EXPORT_SYMBOL_GPL(dw_mci_pltfm_remove);
 
 #ifdef CONFIG_PM_SLEEP
 /*
@@ -94,7 +101,8 @@ static int dw_mci_pltfm_resume(struct device *dev)
 #define dw_mci_pltfm_resume	NULL
 #endif /* CONFIG_PM_SLEEP */
 
-static SIMPLE_DEV_PM_OPS(dw_mci_pltfm_pmops, dw_mci_pltfm_suspend, dw_mci_pltfm_resume);
+SIMPLE_DEV_PM_OPS(dw_mci_pltfm_pmops, dw_mci_pltfm_suspend, dw_mci_pltfm_resume);
+EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops);
 
 static const struct of_device_id dw_mci_pltfm_match[] = {
 	{ .compatible = "snps,dw-mshc", },
diff --git a/drivers/mmc/host/dw_mmc-pltfm.h b/drivers/mmc/host/dw_mmc-pltfm.h
new file mode 100644
index 0000000..6c065d9
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc-pltfm.h
@@ -0,0 +1,19 @@
+/*
+ * Synopsys DesignWare Multimedia Card Interface Platform driver
+ *
+ * Copyright (C) 2012, Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _DW_MMC_PLTFM_H_
+#define _DW_MMC_PLTFM_H_
+
+extern int dw_mci_pltfm_register(struct platform_device *pdev);
+extern int __devexit dw_mci_pltfm_remove(struct platform_device *pdev);
+extern const struct dev_pm_ops dw_mci_pltfm_pmops;
+
+#endif /* _DW_MMC_PLTFM_H_ */
-- 
1.6.6.rc2

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v5 8/9] mmc: dw_mmc: add support for implementation specific callbacks
  2012-08-29 10:48 [PATCH v5 0/9] mmc: dw_mmc: add support for device tree based instantiation Thomas Abraham
                   ` (6 preceding siblings ...)
  2012-08-29 10:48 ` [PATCH v5 7/9] mmc: dw_mmc: prepare functions in dw_mmc-pltfm for reuse Thomas Abraham
@ 2012-08-29 10:48 ` Thomas Abraham
  2012-09-04 19:47   ` Thomas Abraham
  2012-09-07  9:40   ` Thomas Abraham
  2012-08-29 10:48 ` [PATCH v5 9/9] mmc: dw_mmc: add support for exynos specific implementation of dw-mshc Thomas Abraham
  8 siblings, 2 replies; 29+ messages in thread
From: Thomas Abraham @ 2012-08-29 10:48 UTC (permalink / raw)
  To: linux-mmc, devicetree-discuss
  Cc: will.newton, cjb, grant.likely, rob.herring, linux-samsung-soc,
	kgene.kim, girish.shivananjappa, jh80.chung, tgih.jun, patches

The core dw-mshc controller driver can let platform specific implementations
of the dw-mshc controller to control the hardware as required by such
implementations. This is acheived by invoking implementation specific (optional)
callbacks. Define the list of callbacks supported the add invocation points
for the same.

Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
Acked-by: Will Newton <will.newton@imgtec.com>
---
 drivers/mmc/host/dw_mmc-pltfm.c |   12 ++++++++++--
 drivers/mmc/host/dw_mmc-pltfm.h |    3 ++-
 drivers/mmc/host/dw_mmc.c       |   38 +++++++++++++++++++++++++++++++++++++-
 drivers/mmc/host/dw_mmc.h       |   22 ++++++++++++++++++++++
 include/linux/mmc/dw_mmc.h      |    4 ++++
 5 files changed, 75 insertions(+), 4 deletions(-)

diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index e17da91..c960ca7 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -23,7 +23,8 @@
 
 #include "dw_mmc.h"
 
-int dw_mci_pltfm_register(struct platform_device *pdev)
+int dw_mci_pltfm_register(struct platform_device *pdev,
+				struct dw_mci_drv_data *drv_data)
 {
 	struct dw_mci *host;
 	struct resource	*regs;
@@ -41,6 +42,7 @@ int dw_mci_pltfm_register(struct platform_device *pdev)
 	if (host->irq < 0)
 		return host->irq;
 
+	host->drv_data = drv_data;
 	host->dev = &pdev->dev;
 	host->irq_flags = 0;
 	host->pdata = pdev->dev.platform_data;
@@ -48,6 +50,12 @@ int dw_mci_pltfm_register(struct platform_device *pdev)
 	if (!host->regs)
 		return -ENOMEM;
 
+	if (host->drv_data->init) {
+		ret = host->drv_data->init(host);
+		if (ret)
+			return ret;
+	}
+
 	platform_set_drvdata(pdev, host);
 	ret = dw_mci_probe(host);
 	return ret;
@@ -56,7 +64,7 @@ EXPORT_SYMBOL_GPL(dw_mci_pltfm_register);
 
 static int __devinit dw_mci_pltfm_probe(struct platform_device *pdev)
 {
-	return dw_mci_pltfm_register(pdev);
+	return dw_mci_pltfm_register(pdev, NULL);
 }
 
 static int __devexit dw_mci_pltfm_remove(struct platform_device *pdev)
diff --git a/drivers/mmc/host/dw_mmc-pltfm.h b/drivers/mmc/host/dw_mmc-pltfm.h
index 6c065d9..301f245 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.h
+++ b/drivers/mmc/host/dw_mmc-pltfm.h
@@ -12,7 +12,8 @@
 #ifndef _DW_MMC_PLTFM_H_
 #define _DW_MMC_PLTFM_H_
 
-extern int dw_mci_pltfm_register(struct platform_device *pdev);
+extern int dw_mci_pltfm_register(struct platform_device *pdev,
+				struct dw_mci_drv_data *drv_data);
 extern int __devexit dw_mci_pltfm_remove(struct platform_device *pdev);
 extern const struct dev_pm_ops dw_mci_pltfm_pmops;
 
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 1308619..8dd698b 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -231,6 +231,7 @@ static void dw_mci_set_timeout(struct dw_mci *host)
 static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
 {
 	struct mmc_data	*data;
+	struct dw_mci_slot *slot = mmc_priv(mmc);
 	u32 cmdr;
 	cmd->error = -EINPROGRESS;
 
@@ -260,6 +261,9 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
 			cmdr |= SDMMC_CMD_DAT_WR;
 	}
 
+	if (slot->host->drv_data->prepare_command)
+		slot->host->drv_data->prepare_command(slot->host, &cmdr);
+
 	return cmdr;
 }
 
@@ -815,6 +819,9 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 		slot->clock = ios->clock;
 	}
 
+	if (slot->host->drv_data->set_ios)
+		slot->host->drv_data->set_ios(slot->host, ios);
+
 	switch (ios->power_mode) {
 	case MMC_POWER_UP:
 		set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
@@ -1820,6 +1827,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 {
 	struct mmc_host *mmc;
 	struct dw_mci_slot *slot;
+	int ctrl_id, ret;
 	u8 bus_width;
 
 	mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
@@ -1851,6 +1859,16 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 	if (host->pdata->caps)
 		mmc->caps = host->pdata->caps;
 
+	if (host->dev->of_node) {
+		ctrl_id = of_alias_get_id(host->dev->of_node, "mshc");
+		if (ctrl_id < 0)
+			ctrl_id = 0;
+	} else {
+		ctrl_id = to_platform_device(host->dev)->id;
+	}
+	if (host->drv_data && host->drv_data->caps)
+		mmc->caps |= host->drv_data->caps[ctrl_id];
+
 	if (host->pdata->caps2)
 		mmc->caps2 = host->pdata->caps2;
 
@@ -1861,6 +1879,14 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 	else
 		bus_width = 1;
 
+	if (host->drv_data->setup_bus) {
+		struct device_node *slot_np;
+		slot_np = dw_mci_of_find_slot_node(host->dev, slot->id);
+		ret = host->drv_data->setup_bus(host, slot_np, bus_width);
+		if (ret)
+			goto err_setup_bus;
+	}
+
 	switch (bus_width) {
 	case 8:
 		mmc->caps |= MMC_CAP_8_BIT_DATA;
@@ -1927,6 +1953,10 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 	queue_work(host->card_workqueue, &host->card_work);
 
 	return 0;
+
+err_setup_bus:
+	mmc_free_host(mmc);
+	return -EINVAL;
 }
 
 static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id)
@@ -2021,7 +2051,7 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
 	struct dw_mci_board *pdata;
 	struct device *dev = host->dev;
 	struct device_node *np = dev->of_node;
-	int idx;
+	int idx, ret;
 
 	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
 	if (!pdata) {
@@ -2048,6 +2078,12 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
 
 	of_property_read_u32(np, "card-detect-delay", &pdata->detect_delay_ms);
 
+	if (host->drv_data->parse_dt) {
+		ret = host->drv_data->parse_dt(host);
+		if (ret)
+			return ERR_PTR(ret);
+	}
+
 	return pdata;
 }
 
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 15c27e1..a9f045a 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -182,4 +182,26 @@ extern int dw_mci_suspend(struct dw_mci *host);
 extern int dw_mci_resume(struct dw_mci *host);
 #endif
 
+/**
+ * dw_mci driver data - dw-mshc implementation specific driver data.
+ * @caps: mmc subsystem specified capabilities of the controller(s).
+ * @init: early implementation specific initialization.
+ * @prepare_command: handle CMD register extensions.
+ * @set_ios: handle bus specific extensions.
+ * @parse_dt: parse implementation specific device tree properties.
+ * @setup_bus: initialize io-interface
+ *
+ * Provide controller implementation specific extensions. The usage of this
+ * data structure is fully optional and usage of each member in this structure
+ * is optional as well.
+ */
+struct dw_mci_drv_data {
+	unsigned long	*caps;
+	int		(*init)(struct dw_mci *host);
+	void		(*prepare_command)(struct dw_mci *host, u32 *cmdr);
+	void		(*set_ios)(struct dw_mci *host, struct mmc_ios *ios);
+	int		(*parse_dt)(struct dw_mci *host);
+	int		(*setup_bus)(struct dw_mci *host,
+				struct device_node *slot_np, u8 bus_width);
+};
 #endif /* _DW_MMC_H_ */
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
index b72e4aa..6cb043e 100644
--- a/include/linux/mmc/dw_mmc.h
+++ b/include/linux/mmc/dw_mmc.h
@@ -78,6 +78,8 @@ struct mmc_data;
  * @data_offset: Set the offset of DATA register according to VERID.
  * @dev: Device associated with the MMC controller.
  * @pdata: Platform data associated with the MMC controller.
+ * @drv_data: Driver specific data for identified variant of the controller
+ * @priv: Implementation defined private data.
  * @biu_clk: Pointer to bus interface unit clock instance.
  * @ciu_clk: Pointer to card interface unit clock instance.
  * @slot: Slots sharing this MMC controller.
@@ -160,6 +162,8 @@ struct dw_mci {
 	u16			data_offset;
 	struct device		*dev;
 	struct dw_mci_board	*pdata;
+	struct dw_mci_drv_data	*drv_data;
+	void			*priv;
 	struct clk		*biu_clk;
 	struct clk		*ciu_clk;
 	struct dw_mci_slot	*slot[MAX_MCI_SLOTS];
-- 
1.6.6.rc2

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v5 9/9] mmc: dw_mmc: add support for exynos specific implementation of dw-mshc
  2012-08-29 10:48 [PATCH v5 0/9] mmc: dw_mmc: add support for device tree based instantiation Thomas Abraham
                   ` (7 preceding siblings ...)
  2012-08-29 10:48 ` [PATCH v5 8/9] mmc: dw_mmc: add support for implementation specific callbacks Thomas Abraham
@ 2012-08-29 10:48 ` Thomas Abraham
  2012-09-04 19:48   ` Thomas Abraham
  8 siblings, 1 reply; 29+ messages in thread
From: Thomas Abraham @ 2012-08-29 10:48 UTC (permalink / raw)
  To: linux-mmc, devicetree-discuss
  Cc: will.newton, cjb, grant.likely, rob.herring, linux-samsung-soc,
	kgene.kim, girish.shivananjappa, jh80.chung, tgih.jun, patches

Samsung Exynos SoC's extend the dw-mshc controller for additional clock and bus
control. Add support for these extensions and include provide device tree based
discovery suppory as well.

Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
Acked-by: Will Newton <will.newton@imgtec.com>
---
 .../devicetree/bindings/mmc/exynos-dw-mshc.txt     |   85 +++++++
 drivers/mmc/host/Kconfig                           |    9 +
 drivers/mmc/host/Makefile                          |    1 +
 drivers/mmc/host/dw_mmc-exynos.c                   |  243 ++++++++++++++++++++
 4 files changed, 338 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
 create mode 100644 drivers/mmc/host/dw_mmc-exynos.c

diff --git a/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
new file mode 100644
index 0000000..cc82e11
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
@@ -0,0 +1,85 @@
+* Samsung Exynos specific extensions to the Synopsis Designware Mobile
+  Storage Host Controller
+
+The Synopsis designware mobile storage host controller is used to interface
+a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
+differences between the core Synopsis dw mshc controller properties described
+by synposis-dw-mshc.txt and the properties used by the Samsung Exynos specific
+extensions to the Synopsis Designware Mobile Storage Host Controller.
+
+Required Properties:
+
+* compatible: should be
+	- "samsung,exynos4210-dw-mshc": for controllers with Samsung Exynos4210
+	  specific extentions.
+	- "samsung,exynos4412-dw-mshc": for controllers with Samsung Exynos4412
+	  specific extentions.
+	- "samsung,exynos5250-dw-mshc": for controllers with Samsung Exynos5250
+	  specific extentions.
+
+* samsung,dw-mshc-sdr-timing: Specifies the value of CUI clock divider, CIU
+  clock phase shift value in transmit mode and CIU clock phase shift value in
+  receive mode for single data rate mode operation. Refer notes of the valid
+  values below.
+
+* samsung,dw-mshc-ddr-timing: Specifies the value of CUI clock divider, CIU
+  clock phase shift value in transmit mode and CIU clock phase shift value in
+  receive mode for double data rate mode operation. Refer notes of the valid
+  values below. The order of the cells should be
+
+    - First Cell: 	CIU clock divider value (applicable only for Exynos5
+			SoC's, should be zero for Exynos4 SoC's)
+    - Second Cell:	CIU clock phase shift value for tx mode.
+    - Third Cell:	CIU clock phase shift value for rx mode.
+
+  Valid values for SDR and DDR CIU clock timing for Exynos5250:
+
+    - valid values for CIU clock divider, tx phase shift and rx phase shift
+      is 0 to 7.
+
+    - When CIU clock divider value is set to 3, all possible 8 phase shift
+      values can be used.
+
+    - If CIU clock divider value is 0 (that is divide by 1), both tx and rx
+      phase shift clocks should be 0.
+
+Required properties for a slot:
+
+* gpios: specifies a list of gpios used for command, clock and data bus. The
+  first gpio is the command line and the second gpio is the clock line. The
+  rest of the gpios (depending on the bus-width property) are the data lines in
+  no particular order. The format of the gpio specifier depends on the gpio
+  controller.
+
+Example:
+
+  The MSHC controller node can be split into two portions, SoC specific and
+  board specific portions as listed below.
+
+	dwmmc0@12200000 {
+		compatible = "samsung,exynos5250-dw-mshc";
+		reg = <0x12200000 0x1000>;
+		interrupts = <0 75 0>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+	};
+
+	dwmmc0@12200000 {
+		num-slots = <1>;
+		supports-highspeed;
+		broken-cd;
+		fifo-depth = <0x80>;
+		card-detect-delay = <200>;
+		samsung,dw-mshc-sdr-timing = <2 3 3>;
+		samsung,dw-mshc-ddr-timing = <1 2 3>;
+
+		slot@0 {
+			reg = <0>;
+			bus-width = <8>;
+			gpios = <&gpc0 0 2 0 3>, <&gpc0 1 2 0 3>,
+				<&gpc1 0 2 3 3>, <&gpc1 1 2 3 3>,
+				<&gpc1 2 2 3 3>, <&gpc1 3 2 3 3>,
+				<&gpc0 3 2 3 3>, <&gpc0 4 2 3 3>,
+				<&gpc0 5 2 3 3>, <&gpc0 6 2 3 3>;
+		};
+	};
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index aa131b3..9bf10e7 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -540,6 +540,15 @@ config MMC_DW_PLTFM
 
 	  If unsure, say Y.
 
+config MMC_DW_EXYNOS
+	tristate "Exynos specific extentions for Synopsys DW Memory Card Interface"
+	depends on MMC_DW
+	select MMC_DW_PLTFM
+	help
+	  This selects support for Samsung Exynos SoC specific extensions to the
+	  Synopsys DesignWare Memory Card Interface driver. Select this option
+	  for platforms based on Exynos4 and Exynos5 SoC's.
+
 config MMC_DW_PCI
 	tristate "Synopsys Designware MCI support on PCI bus"
 	depends on MMC_DW && PCI
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 8922b06..17ad0a7 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_MMC_VIA_SDMMC)	+= via-sdmmc.o
 obj-$(CONFIG_SDH_BFIN)		+= bfin_sdh.o
 obj-$(CONFIG_MMC_DW)		+= dw_mmc.o
 obj-$(CONFIG_MMC_DW_PLTFM)	+= dw_mmc-pltfm.o
+obj-$(CONFIG_MMC_DW_EXYNOS)	+= dw_mmc-exynos.o
 obj-$(CONFIG_MMC_DW_PCI)	+= dw_mmc-pci.o
 obj-$(CONFIG_MMC_SH_MMCIF)	+= sh_mmcif.o
 obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c
new file mode 100644
index 0000000..cb8a196
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc-exynos.c
@@ -0,0 +1,243 @@
+/*
+ * Exynos Specific Extensions for Synopsys DW Multimedia Card Interface driver
+ *
+ * Copyright (C) 2012, Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/dw_mmc.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+
+#include "dw_mmc.h"
+#include "dw_mmc-pltfm.h"
+
+#define NUM_PINS(x)			(x + 2)
+
+#define SDMMC_CLKSEL			0x09C
+#define SDMMC_CLKSEL_CCLK_SAMPLE(x)	(((x) & 7) << 0)
+#define SDMMC_CLKSEL_CCLK_DRIVE(x)	(((x) & 7) << 16)
+#define SDMMC_CLKSEL_CCLK_DIVIDER(x)	(((x) & 7) << 24)
+#define SDMMC_CLKSEL_GET_DIVRATIO(x)	((((x) >> 24) & 0x7) + 1)
+#define SDMMC_CLKSEL_GET_DRV_WD2(x)	(((x) >> 16) & 0x3)
+#define SDMMC_CLKSEL_GET_DRV_WD3(x)	(((x) >> 16) & 0x7)
+#define SDMMC_CLKSEL_TIMING(x, y, z)	(SDMMC_CLKSEL_CCLK_SAMPLE(x) |	\
+					SDMMC_CLKSEL_CCLK_DRIVE(y) |	\
+					SDMMC_CLKSEL_CCLK_DIVIDER(z))
+
+#define SDMMC_CMD_USE_HOLD_REG		BIT(29)
+
+#define EXYNOS4210_FIXED_CIU_CLK_DIV	2
+#define EXYNOS4412_FIXED_CIU_CLK_DIV	4
+
+/* Variations in Exynos specific dw-mshc controller */
+enum dw_mci_exynos_type {
+	DW_MCI_TYPE_EXYNOS4210,
+	DW_MCI_TYPE_EXYNOS4412,
+	DW_MCI_TYPE_EXYNOS5250,
+};
+
+/* Exynos implementation specific driver private data */
+struct dw_mci_exynos_priv_data {
+	enum dw_mci_exynos_type		ctrl_type;
+	u32				sdr_timing;
+	u32				ddr_timing;
+};
+
+static struct dw_mci_exynos_compatible {
+	char				*compatible;
+	enum dw_mci_exynos_type		ctrl_type;
+} exynos_compat[] = {
+	{
+		.compatible	= "samsung,exynos4210-dw-mshc",
+		.ctrl_type	= DW_MCI_TYPE_EXYNOS4210,
+	}, {
+		.compatible	= "samsung,exynos4412-dw-mshc",
+		.ctrl_type	= DW_MCI_TYPE_EXYNOS4412,
+	}, {
+		.compatible	= "samsung,exynos5250-dw-mshc",
+		.ctrl_type	= DW_MCI_TYPE_EXYNOS5250,
+	},
+};
+
+static int dw_mci_exynos_priv_init(struct dw_mci *host)
+{
+	struct dw_mci_exynos_priv_data *priv;
+	int idx;
+
+	priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		dev_err(host->dev, "mem alloc failed for private data\n");
+		return -ENOMEM;
+	}
+
+	for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
+		if (of_device_is_compatible(host->dev->of_node,
+					exynos_compat[idx].compatible))
+			priv->ctrl_type = exynos_compat[idx].ctrl_type;
+	}
+
+	host->priv = priv;
+	return 0;
+}
+
+static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
+{
+	/*
+	 * Exynos4412 and Exynos5250 extends the use of CMD register with the
+	 * use of bit 29 (which is reserved on standard MSHC controllers) for
+	 * optionally bypassing the HOLD register for command and data. The
+	 * HOLD register should be bypassed in case there is no phase shift
+	 * applied on CMD/DATA that is sent to the card.
+	 */
+	if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL)))
+		*cmdr |= SDMMC_CMD_USE_HOLD_REG;
+}
+
+static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
+{
+	struct dw_mci_exynos_priv_data *priv = host->priv;
+
+	if (ios->timing == MMC_TIMING_UHS_DDR50)
+		mci_writel(host, CLKSEL, priv->ddr_timing);
+	else
+		mci_writel(host, CLKSEL, priv->sdr_timing);
+
+	host->bus_hz = clk_get_rate(host->ciu_clk);
+	if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5250)
+		host->bus_hz /= SDMMC_CLKSEL_GET_DIVRATIO(
+					mci_readl(host, CLKSEL));
+	else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
+		host->bus_hz /= EXYNOS4412_FIXED_CIU_CLK_DIV;
+	else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
+		host->bus_hz /= EXYNOS4210_FIXED_CIU_CLK_DIV;
+}
+
+static int dw_mci_exynos_parse_dt(struct dw_mci *host)
+{
+	struct dw_mci_exynos_priv_data *priv = host->priv;
+	u32 timing[3];
+	int ret;
+
+	ret = of_property_read_u32_array(host->dev->of_node,
+			"samsung,dw-mshc-sdr-timing", timing, 3);
+	if (ret)
+		return ret;
+
+	priv->sdr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], timing[2]);
+
+	ret = of_property_read_u32_array(host->dev->of_node,
+			"samsung,dw-mshc-ddr-timing", timing, 3);
+	if (ret)
+		return ret;
+
+	priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], timing[2]);
+	return 0;
+}
+
+static int dw_mci_exynos_setup_bus(struct dw_mci *host,
+				struct device_node *slot_np, u8 bus_width)
+{
+	int idx, gpio, ret;
+
+	if (!slot_np)
+		return -EINVAL;
+
+	/* cmd + clock + bus-width pins */
+	for (idx = 0; idx < NUM_PINS(bus_width); idx++) {
+		gpio = of_get_gpio(slot_np, idx);
+		if (!gpio_is_valid(gpio)) {
+			dev_err(host->dev, "invalid gpio: %d\n", gpio);
+			return -EINVAL;
+		}
+
+		ret = devm_gpio_request(host->dev, gpio, "dw-mci-bus");
+		if (ret) {
+			dev_err(host->dev, "gpio [%d] request failed\n", gpio);
+			return -EBUSY;
+		}
+	}
+
+	gpio = of_get_named_gpio(slot_np, "wp-gpios", 0);
+	if (gpio_is_valid(gpio)) {
+		if (devm_gpio_request(host->dev, gpio, "dw-mci-wp"))
+			dev_info(host->dev, "gpio [%d] request failed\n",
+						gpio);
+	} else {
+		dev_info(host->dev, "wp gpio not available");
+		host->pdata->quirks |= DW_MCI_QUIRK_NO_WRITE_PROTECT;
+	}
+
+	if (host->pdata->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)
+		return 0;
+
+	gpio = of_get_named_gpio(slot_np, "samsung,cd-pinmux-gpio", 0);
+	if (gpio_is_valid(gpio)) {
+		if (devm_gpio_request(host->dev, gpio, "dw-mci-cd"))
+			dev_err(host->dev, "gpio [%d] request failed\n", gpio);
+	} else {
+		dev_info(host->dev, "cd gpio not available");
+	}
+
+	return 0;
+}
+
+/* Exynos5250 controller specific capabilities */
+static unsigned long exynos5250_dwmmc_caps[4] = {
+	MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR |
+		MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23,
+	MMC_CAP_CMD23,
+	MMC_CAP_CMD23,
+	MMC_CAP_CMD23,
+};
+
+static struct dw_mci_drv_data exynos5250_drv_data = {
+	.caps			= exynos5250_dwmmc_caps,
+	.init			= dw_mci_exynos_priv_init,
+	.prepare_command	= dw_mci_exynos_prepare_command,
+	.set_ios		= dw_mci_exynos_set_ios,
+	.parse_dt		= dw_mci_exynos_parse_dt,
+	.setup_bus		= dw_mci_exynos_setup_bus,
+};
+
+static const struct of_device_id dw_mci_exynos_match[] = {
+	{ .compatible = "samsung,exynos5250-dw-mshc",
+			.data = (void *)&exynos5250_drv_data, },
+	{},
+};
+MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
+
+int dw_mci_exynos_probe(struct platform_device *pdev)
+{
+	struct dw_mci_drv_data *drv_data;
+	const struct of_device_id *match;
+
+	match = of_match_node(dw_mci_exynos_match, pdev->dev.of_node);
+	drv_data = match->data;
+	return dw_mci_pltfm_register(pdev, drv_data);
+}
+
+static struct platform_driver dw_mci_exynos_pltfm_driver = {
+	.probe		= dw_mci_exynos_probe,
+	.remove		= __exit_p(dw_mci_pltfm_remove),
+	.driver		= {
+		.name		= "dwmmc_exynos",
+		.of_match_table	= of_match_ptr(dw_mci_exynos_match),
+		.pm		= &dw_mci_pltfm_pmops,
+	},
+};
+
+module_platform_driver(dw_mci_exynos_pltfm_driver);
+
+MODULE_DESCRIPTION("Samsung Specific DW-MSHC Driver Extension");
+MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dwmmc-exynos");
-- 
1.6.6.rc2


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH v5 4/9] mmc: dw_mmc: lookup for optional biu and ciu clocks
  2012-08-29 10:48 ` [PATCH v5 4/9] mmc: dw_mmc: lookup for optional biu and ciu clocks Thomas Abraham
@ 2012-08-30  8:48   ` Jaehoon Chung
  2012-08-31  5:29     ` Thomas Abraham
  0 siblings, 1 reply; 29+ messages in thread
From: Jaehoon Chung @ 2012-08-30  8:48 UTC (permalink / raw)
  To: Thomas Abraham
  Cc: linux-mmc, devicetree-discuss, will.newton, cjb, grant.likely,
	rob.herring, linux-samsung-soc, kgene.kim, girish.shivananjappa,
	jh80.chung, tgih.jun, patches

Hi Thomas,

On 08/29/2012 07:48 PM, Thomas Abraham wrote:
> Some platforms allow for clock gating and control of bus interface unit clock
> and card interface unit clock. Add support for clock lookup of optional biu
> and ciu clocks for clock gating and clock speed determination.
> 
> Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
> Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
> Acked-by: Will Newton <will.newton@imgtec.com>
> ---
>  drivers/mmc/host/dw_mmc.c  |   42 +++++++++++++++++++++++++++++++++++++++---
>  include/linux/mmc/dw_mmc.h |    4 ++++
>  2 files changed, 43 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
> index 227c42e..90c7c7b 100644
> --- a/drivers/mmc/host/dw_mmc.c
> +++ b/drivers/mmc/host/dw_mmc.c
> @@ -1960,18 +1960,38 @@ int dw_mci_probe(struct dw_mci *host)
>  		return -ENODEV;
>  	}
>  
> -	if (!host->pdata->bus_hz) {
> +	host->biu_clk = clk_get(host->dev, "biu");
> +	if (IS_ERR(host->biu_clk))
> +		dev_dbg(host->dev, "biu clock not available\n");
> +	else
> +		clk_prepare_enable(host->biu_clk);
biu is clock for bus interface?
if didn't get "biu_clk" or didn't clk_prepare_enable(), then can we initialize the card?
> +
> +	host->ciu_clk = clk_get(host->dev, "ciu");
> +	if (IS_ERR(host->ciu_clk))
> +		dev_dbg(host->dev, "ciu clock not available\n");
> +	else
> +		clk_prepare_enable(host->ciu_clk);
> +
> +	if (IS_ERR(host->ciu_clk))
> +		host->bus_hz = host->pdata->bus_hz;
> +	else
> +		host->bus_hz = clk_get_rate(host->ciu_clk);
if clk_get_rate() is incorrect value(ex,400MHz),
then mmc->f_min value is too high.
because mmc->f_min is assigned to DIV_ROUND_UP(host->bus_hz, 510) into dw_mc_init_slot.
Do you have any opinion for solving this?
> +
> +	if (!host->bus_hz) {
>  		dev_err(host->dev,
>  			"Platform data must supply bus speed\n");
> -		return -ENODEV;
> +		ret = -ENODEV;
> +		goto err_clk;
>  	}
>  
> -	host->bus_hz = host->pdata->bus_hz;
>  	host->quirks = host->pdata->quirks;
>  
>  	spin_lock_init(&host->lock);
>  	INIT_LIST_HEAD(&host->queue);
>  
> +	host->dma_ops = host->pdata->dma_ops;
> +	dw_mci_init_dma(host);
> +
>  	/*
>  	 * Get the host data width - this assumes that HCON has been set with
>  	 * the correct values.
> @@ -2116,6 +2136,16 @@ err_dmaunmap:
>  		regulator_disable(host->vmmc);
>  		regulator_put(host->vmmc);
>  	}
> +
> +err_clk:
> +	if (!IS_ERR(host->ciu_clk)) {
> +		clk_disable_unprepare(host->ciu_clk);
> +		clk_put(host->ciu_clk);
> +	}
> +	if (!IS_ERR(host->biu_clk)) {
> +		clk_disable_unprepare(host->biu_clk);
> +		clk_put(host->biu_clk);
> +	}
>  	return ret;
>  }
>  EXPORT_SYMBOL(dw_mci_probe);
> @@ -2149,6 +2179,12 @@ void dw_mci_remove(struct dw_mci *host)
>  		regulator_put(host->vmmc);
>  	}
>  
> +	if (!IS_ERR(host->ciu_clk))
> +		clk_disable_unprepare(host->ciu_clk);
> +	if (!IS_ERR(host->biu_clk))
> +		clk_disable_unprepare(host->biu_clk);
> +	clk_put(host->ciu_clk);
> +	clk_put(host->biu_clk);
>  }
>  EXPORT_SYMBOL(dw_mci_remove);
>  
> diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
> index a37a573..787ad56 100644
> --- a/include/linux/mmc/dw_mmc.h
> +++ b/include/linux/mmc/dw_mmc.h
> @@ -78,6 +78,8 @@ struct mmc_data;
>   * @data_offset: Set the offset of DATA register according to VERID.
>   * @dev: Device associated with the MMC controller.
>   * @pdata: Platform data associated with the MMC controller.
> + * @biu_clk: Pointer to bus interface unit clock instance.
> + * @ciu_clk: Pointer to card interface unit clock instance.
>   * @slot: Slots sharing this MMC controller.
>   * @fifo_depth: depth of FIFO.
>   * @data_shift: log2 of FIFO item size.
> @@ -158,6 +160,8 @@ struct dw_mci {
>  	u16			data_offset;
>  	struct device		*dev;
>  	struct dw_mci_board	*pdata;
> +	struct clk		*biu_clk;
> +	struct clk		*ciu_clk;
>  	struct dw_mci_slot	*slot[MAX_MCI_SLOTS];
>  
>  	/* FIFO push and pull */
> 


^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v5 4/9] mmc: dw_mmc: lookup for optional biu and ciu clocks
  2012-08-30  8:48   ` Jaehoon Chung
@ 2012-08-31  5:29     ` Thomas Abraham
  2012-08-31  6:02       ` Jaehoon Chung
  0 siblings, 1 reply; 29+ messages in thread
From: Thomas Abraham @ 2012-08-31  5:29 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: linux-mmc, devicetree-discuss, will.newton, cjb, grant.likely,
	rob.herring, linux-samsung-soc, kgene.kim, girish.shivananjappa,
	tgih.jun, patches

On 30 August 2012 14:18, Jaehoon Chung <jh80.chung@samsung.com> wrote:
> Hi Thomas,
>
> On 08/29/2012 07:48 PM, Thomas Abraham wrote:
>> Some platforms allow for clock gating and control of bus interface unit clock
>> and card interface unit clock. Add support for clock lookup of optional biu
>> and ciu clocks for clock gating and clock speed determination.
>>
>> Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
>> Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
>> Acked-by: Will Newton <will.newton@imgtec.com>
>> ---
>>  drivers/mmc/host/dw_mmc.c  |   42 +++++++++++++++++++++++++++++++++++++++---
>>  include/linux/mmc/dw_mmc.h |    4 ++++
>>  2 files changed, 43 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
>> index 227c42e..90c7c7b 100644
>> --- a/drivers/mmc/host/dw_mmc.c
>> +++ b/drivers/mmc/host/dw_mmc.c
>> @@ -1960,18 +1960,38 @@ int dw_mci_probe(struct dw_mci *host)
>>               return -ENODEV;
>>       }
>>
>> -     if (!host->pdata->bus_hz) {
>> +     host->biu_clk = clk_get(host->dev, "biu");
>> +     if (IS_ERR(host->biu_clk))
>> +             dev_dbg(host->dev, "biu clock not available\n");
>> +     else
>> +             clk_prepare_enable(host->biu_clk);
> biu is clock for bus interface?
> if didn't get "biu_clk" or didn't clk_prepare_enable(), then can we initialize the card?

Hi Jaehoon,

Yes, the biu clock is for bus interface. The biu and ciu clock lookup
and enable here is optional in the above change. If a platform does
not define these clocks, then the platform code is responsible for
enabling these clocks.

>> +
>> +     host->ciu_clk = clk_get(host->dev, "ciu");
>> +     if (IS_ERR(host->ciu_clk))
>> +             dev_dbg(host->dev, "ciu clock not available\n");
>> +     else
>> +             clk_prepare_enable(host->ciu_clk);
>> +
>> +     if (IS_ERR(host->ciu_clk))
>> +             host->bus_hz = host->pdata->bus_hz;
>> +     else
>> +             host->bus_hz = clk_get_rate(host->ciu_clk);
> if clk_get_rate() is incorrect value(ex,400MHz),
> then mmc->f_min value is too high.
> because mmc->f_min is assigned to DIV_ROUND_UP(host->bus_hz, 510) into dw_mc_init_slot.
> Do you have any opinion for solving this?

One option on Exynos5250 is to use the clock divider in the CLKSEL
register to divide the ciu clock to a lower value. For Exynos4, since
there is no clock divider in CLKSEL register, the platform code should
ensure that the ciu clock has a valid range.

Thanks,
Thomas.

[...]

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v5 4/9] mmc: dw_mmc: lookup for optional biu and ciu clocks
  2012-08-31  5:29     ` Thomas Abraham
@ 2012-08-31  6:02       ` Jaehoon Chung
  2012-08-31  6:32         ` Thomas Abraham
  2012-08-31  6:32         ` Thomas Abraham
  0 siblings, 2 replies; 29+ messages in thread
From: Jaehoon Chung @ 2012-08-31  6:02 UTC (permalink / raw)
  To: Thomas Abraham
  Cc: Jaehoon Chung, linux-mmc, devicetree-discuss, will.newton, cjb,
	grant.likely, rob.herring, linux-samsung-soc, kgene.kim,
	girish.shivananjappa, tgih.jun, patches

On 08/31/2012 02:29 PM, Thomas Abraham wrote:
> On 30 August 2012 14:18, Jaehoon Chung <jh80.chung@samsung.com> wrote:
>> Hi Thomas,
>>
>> On 08/29/2012 07:48 PM, Thomas Abraham wrote:
>>> Some platforms allow for clock gating and control of bus interface unit clock
>>> and card interface unit clock. Add support for clock lookup of optional biu
>>> and ciu clocks for clock gating and clock speed determination.
>>>
>>> Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
>>> Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
>>> Acked-by: Will Newton <will.newton@imgtec.com>
>>> ---
>>>  drivers/mmc/host/dw_mmc.c  |   42 +++++++++++++++++++++++++++++++++++++++---
>>>  include/linux/mmc/dw_mmc.h |    4 ++++
>>>  2 files changed, 43 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
>>> index 227c42e..90c7c7b 100644
>>> --- a/drivers/mmc/host/dw_mmc.c
>>> +++ b/drivers/mmc/host/dw_mmc.c
>>> @@ -1960,18 +1960,38 @@ int dw_mci_probe(struct dw_mci *host)
>>>               return -ENODEV;
>>>       }
>>>
>>> -     if (!host->pdata->bus_hz) {
>>> +     host->biu_clk = clk_get(host->dev, "biu");
>>> +     if (IS_ERR(host->biu_clk))
>>> +             dev_dbg(host->dev, "biu clock not available\n");
>>> +     else
>>> +             clk_prepare_enable(host->biu_clk);
>> biu is clock for bus interface?
>> if didn't get "biu_clk" or didn't clk_prepare_enable(), then can we initialize the card?
> 
> Hi Jaehoon,
> 
> Yes, the biu clock is for bus interface. The biu and ciu clock lookup
> and enable here is optional in the above change. If a platform does
> not define these clocks, then the platform code is responsible for
> enabling these clocks.
If biu_clk is presented...
Is there no probability that clk_prepare_enable is failed?
> 
>>> +
>>> +     host->ciu_clk = clk_get(host->dev, "ciu");
>>> +     if (IS_ERR(host->ciu_clk))
>>> +             dev_dbg(host->dev, "ciu clock not available\n");
>>> +     else
>>> +             clk_prepare_enable(host->ciu_clk);
>>> +
>>> +     if (IS_ERR(host->ciu_clk))
>>> +             host->bus_hz = host->pdata->bus_hz;
>>> +     else
>>> +             host->bus_hz = clk_get_rate(host->ciu_clk);
>> if clk_get_rate() is incorrect value(ex,400MHz),
>> then mmc->f_min value is too high.
>> because mmc->f_min is assigned to DIV_ROUND_UP(host->bus_hz, 510) into dw_mc_init_slot.
>> Do you have any opinion for solving this?
> 
> One option on Exynos5250 is to use the clock divider in the CLKSEL
> register to divide the ciu clock to a lower value. For Exynos4, since
> there is no clock divider in CLKSEL register, the platform code should
> ensure that the ciu clock has a valid range.
I know that can use div-ratio filed at the clksel register.
On Exynos5, i known that is used the div-ratio at CLKSEL register.
If ciu-clock is 400MHz, host->bus_hz is assigned to 400MHz.
1) host->bus_hz -> 400MHz (at dw-mmc-pltfm.c)
2) mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510) then mmc->f_min is set to 784KHz.
3) then host->bus_hz is re-assigned to value that is divided to div-ratio at CLKSEL register.
   at this time, host->bus_hz = 100MHz...

I think this sequence is something wrong.
(Is 784KHz too high for init card?)

It's just my thinking..if my understanding is wrong, let me know plz.

Best Regards,
Jaehoon Chung

> 
> Thanks,
> Thomas.
> 
> [...]
> --
> 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
> 

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v5 4/9] mmc: dw_mmc: lookup for optional biu and ciu clocks
  2012-08-31  6:02       ` Jaehoon Chung
@ 2012-08-31  6:32         ` Thomas Abraham
  2012-08-31  6:32         ` Thomas Abraham
  1 sibling, 0 replies; 29+ messages in thread
From: Thomas Abraham @ 2012-08-31  6:32 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: linux-mmc, devicetree-discuss, will.newton, cjb, grant.likely,
	rob.herring, linux-samsung-soc, kgene.kim, girish.shivananjappa,
	tgih.jun, patches

On 31 August 2012 11:32, Jaehoon Chung <jh80.chung@samsung.com> wrote:
> On 08/31/2012 02:29 PM, Thomas Abraham wrote:
>> On 30 August 2012 14:18, Jaehoon Chung <jh80.chung@samsung.com> wrote:
>>> Hi Thomas,
>>>
>>> On 08/29/2012 07:48 PM, Thomas Abraham wrote:
>>>> Some platforms allow for clock gating and control of bus interface unit clock
>>>> and card interface unit clock. Add support for clock lookup of optional biu
>>>> and ciu clocks for clock gating and clock speed determination.
>>>>
>>>> Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
>>>> Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
>>>> Acked-by: Will Newton <will.newton@imgtec.com>
>>>> ---
>>>>  drivers/mmc/host/dw_mmc.c  |   42 +++++++++++++++++++++++++++++++++++++++---
>>>>  include/linux/mmc/dw_mmc.h |    4 ++++
>>>>  2 files changed, 43 insertions(+), 3 deletions(-)
>>>>
>>>> diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
>>>> index 227c42e..90c7c7b 100644
>>>> --- a/drivers/mmc/host/dw_mmc.c
>>>> +++ b/drivers/mmc/host/dw_mmc.c
>>>> @@ -1960,18 +1960,38 @@ int dw_mci_probe(struct dw_mci *host)
>>>>               return -ENODEV;
>>>>       }
>>>>
>>>> -     if (!host->pdata->bus_hz) {
>>>> +     host->biu_clk = clk_get(host->dev, "biu");
>>>> +     if (IS_ERR(host->biu_clk))
>>>> +             dev_dbg(host->dev, "biu clock not available\n");
>>>> +     else
>>>> +             clk_prepare_enable(host->biu_clk);
>>> biu is clock for bus interface?
>>> if didn't get "biu_clk" or didn't clk_prepare_enable(), then can we initialize the card?
>>
>> Hi Jaehoon,
>>
>> Yes, the biu clock is for bus interface. The biu and ciu clock lookup
>> and enable here is optional in the above change. If a platform does
>> not define these clocks, then the platform code is responsible for
>> enabling these clocks.
> If biu_clk is presented...
> Is there no probability that clk_prepare_enable is failed?

Yes, clk_prepare_enable can fail. I will fix this by setting biu_clk
and ciu_clk to NULL, in case the clk_prepare_enable fails.

>>
>>>> +
>>>> +     host->ciu_clk = clk_get(host->dev, "ciu");
>>>> +     if (IS_ERR(host->ciu_clk))
>>>> +             dev_dbg(host->dev, "ciu clock not available\n");
>>>> +     else
>>>> +             clk_prepare_enable(host->ciu_clk);
>>>> +
>>>> +     if (IS_ERR(host->ciu_clk))
>>>> +             host->bus_hz = host->pdata->bus_hz;
>>>> +     else
>>>> +             host->bus_hz = clk_get_rate(host->ciu_clk);
>>> if clk_get_rate() is incorrect value(ex,400MHz),
>>> then mmc->f_min value is too high.
>>> because mmc->f_min is assigned to DIV_ROUND_UP(host->bus_hz, 510) into dw_mc_init_slot.
>>> Do you have any opinion for solving this?
>>
>> One option on Exynos5250 is to use the clock divider in the CLKSEL
>> register to divide the ciu clock to a lower value. For Exynos4, since
>> there is no clock divider in CLKSEL register, the platform code should
>> ensure that the ciu clock has a valid range.
> I know that can use div-ratio filed at the clksel register.
> On Exynos5, i known that is used the div-ratio at CLKSEL register.
> If ciu-clock is 400MHz, host->bus_hz is assigned to 400MHz.
> 1) host->bus_hz -> 400MHz (at dw-mmc-pltfm.c)
> 2) mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510) then mmc->f_min is set to 784KHz.
> 3) then host->bus_hz is re-assigned to value that is divided to div-ratio at CLKSEL register.
>    at this time, host->bus_hz = 100MHz...
>
> I think this sequence is something wrong.
> (Is 784KHz too high for init card?)
>
> It's just my thinking..if my understanding is wrong, let me know plz.

You have listed the steps 1 to 3 correctly. So, as per step 3, 100Mhz
/ 510 ~= 196KHz. Which is well within 400KHz. So do you still see a
problem here?

Thanks,
Thomas.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v5 4/9] mmc: dw_mmc: lookup for optional biu and ciu clocks
  2012-08-31  6:02       ` Jaehoon Chung
  2012-08-31  6:32         ` Thomas Abraham
@ 2012-08-31  6:32         ` Thomas Abraham
  2012-08-31  7:59           ` Jaehoon Chung
  1 sibling, 1 reply; 29+ messages in thread
From: Thomas Abraham @ 2012-08-31  6:32 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: linux-mmc, devicetree-discuss, will.newton, cjb, grant.likely,
	rob.herring, linux-samsung-soc, kgene.kim, girish.shivananjappa,
	tgih.jun, patches

On 31 August 2012 11:32, Jaehoon Chung <jh80.chung@samsung.com> wrote:
> On 08/31/2012 02:29 PM, Thomas Abraham wrote:
>> On 30 August 2012 14:18, Jaehoon Chung <jh80.chung@samsung.com> wrote:
>>> Hi Thomas,
>>>
>>> On 08/29/2012 07:48 PM, Thomas Abraham wrote:
>>>> Some platforms allow for clock gating and control of bus interface unit clock
>>>> and card interface unit clock. Add support for clock lookup of optional biu
>>>> and ciu clocks for clock gating and clock speed determination.
>>>>
>>>> Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
>>>> Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
>>>> Acked-by: Will Newton <will.newton@imgtec.com>
>>>> ---
>>>>  drivers/mmc/host/dw_mmc.c  |   42 +++++++++++++++++++++++++++++++++++++++---
>>>>  include/linux/mmc/dw_mmc.h |    4 ++++
>>>>  2 files changed, 43 insertions(+), 3 deletions(-)
>>>>
>>>> diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
>>>> index 227c42e..90c7c7b 100644
>>>> --- a/drivers/mmc/host/dw_mmc.c
>>>> +++ b/drivers/mmc/host/dw_mmc.c
>>>> @@ -1960,18 +1960,38 @@ int dw_mci_probe(struct dw_mci *host)
>>>>               return -ENODEV;
>>>>       }
>>>>
>>>> -     if (!host->pdata->bus_hz) {
>>>> +     host->biu_clk = clk_get(host->dev, "biu");
>>>> +     if (IS_ERR(host->biu_clk))
>>>> +             dev_dbg(host->dev, "biu clock not available\n");
>>>> +     else
>>>> +             clk_prepare_enable(host->biu_clk);
>>> biu is clock for bus interface?
>>> if didn't get "biu_clk" or didn't clk_prepare_enable(), then can we initialize the card?
>>
>> Hi Jaehoon,
>>
>> Yes, the biu clock is for bus interface. The biu and ciu clock lookup
>> and enable here is optional in the above change. If a platform does
>> not define these clocks, then the platform code is responsible for
>> enabling these clocks.
> If biu_clk is presented...
> Is there no probability that clk_prepare_enable is failed?

Yes, clk_prepare_enable can fail. I will fix this by setting biu_clk
and ciu_clk to NULL, in case the clk_prepare_enable fails.

>>
>>>> +
>>>> +     host->ciu_clk = clk_get(host->dev, "ciu");
>>>> +     if (IS_ERR(host->ciu_clk))
>>>> +             dev_dbg(host->dev, "ciu clock not available\n");
>>>> +     else
>>>> +             clk_prepare_enable(host->ciu_clk);
>>>> +
>>>> +     if (IS_ERR(host->ciu_clk))
>>>> +             host->bus_hz = host->pdata->bus_hz;
>>>> +     else
>>>> +             host->bus_hz = clk_get_rate(host->ciu_clk);
>>> if clk_get_rate() is incorrect value(ex,400MHz),
>>> then mmc->f_min value is too high.
>>> because mmc->f_min is assigned to DIV_ROUND_UP(host->bus_hz, 510) into dw_mc_init_slot.
>>> Do you have any opinion for solving this?
>>
>> One option on Exynos5250 is to use the clock divider in the CLKSEL
>> register to divide the ciu clock to a lower value. For Exynos4, since
>> there is no clock divider in CLKSEL register, the platform code should
>> ensure that the ciu clock has a valid range.
> I know that can use div-ratio filed at the clksel register.
> On Exynos5, i known that is used the div-ratio at CLKSEL register.
> If ciu-clock is 400MHz, host->bus_hz is assigned to 400MHz.
> 1) host->bus_hz -> 400MHz (at dw-mmc-pltfm.c)
> 2) mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510) then mmc->f_min is set to 784KHz.
> 3) then host->bus_hz is re-assigned to value that is divided to div-ratio at CLKSEL register.
>    at this time, host->bus_hz = 100MHz...
>
> I think this sequence is something wrong.
> (Is 784KHz too high for init card?)
>
> It's just my thinking..if my understanding is wrong, let me know plz.

You have listed the steps 1 to 3 correctly. So, as per step 3, 100Mhz
/ 510 ~= 196KHz. Which is well within 400KHz. So do you still see a
problem here?

Thanks,
Thomas.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v5 4/9] mmc: dw_mmc: lookup for optional biu and ciu clocks
  2012-08-31  6:32         ` Thomas Abraham
@ 2012-08-31  7:59           ` Jaehoon Chung
  2012-08-31  8:48             ` Thomas Abraham
  0 siblings, 1 reply; 29+ messages in thread
From: Jaehoon Chung @ 2012-08-31  7:59 UTC (permalink / raw)
  To: Thomas Abraham
  Cc: Jaehoon Chung, linux-mmc, devicetree-discuss, will.newton, cjb,
	grant.likely, rob.herring, linux-samsung-soc, kgene.kim,
	girish.shivananjappa, tgih.jun, patches

>>>>> +
>>>>> +     host->ciu_clk = clk_get(host->dev, "ciu");
>>>>> +     if (IS_ERR(host->ciu_clk))
>>>>> +             dev_dbg(host->dev, "ciu clock not available\n");
>>>>> +     else
>>>>> +             clk_prepare_enable(host->ciu_clk);
>>>>> +
>>>>> +     if (IS_ERR(host->ciu_clk))
>>>>> +             host->bus_hz = host->pdata->bus_hz;
>>>>> +     else
>>>>> +             host->bus_hz = clk_get_rate(host->ciu_clk);
>>>> if clk_get_rate() is incorrect value(ex,400MHz),
>>>> then mmc->f_min value is too high.
>>>> because mmc->f_min is assigned to DIV_ROUND_UP(host->bus_hz, 510) into dw_mc_init_slot.
>>>> Do you have any opinion for solving this?
>>>
>>> One option on Exynos5250 is to use the clock divider in the CLKSEL
>>> register to divide the ciu clock to a lower value. For Exynos4, since
>>> there is no clock divider in CLKSEL register, the platform code should
>>> ensure that the ciu clock has a valid range.
>> I know that can use div-ratio filed at the clksel register.
>> On Exynos5, i known that is used the div-ratio at CLKSEL register.
>> If ciu-clock is 400MHz, host->bus_hz is assigned to 400MHz.
>> 1) host->bus_hz -> 400MHz (at dw-mmc-pltfm.c)
>> 2) mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510) then mmc->f_min is set to 784KHz.
>> 3) then host->bus_hz is re-assigned to value that is divided to div-ratio at CLKSEL register.
>>    at this time, host->bus_hz = 100MHz...
>>
>> I think this sequence is something wrong.
>> (Is 784KHz too high for init card?)
>>
>> It's just my thinking..if my understanding is wrong, let me know plz.
> 
> You have listed the steps 1 to 3 correctly. So, as per step 3, 100Mhz
> / 510 ~= 196KHz. Which is well within 400KHz. So do you still see a
> problem here?
How do you think about this? 
[ 4.620000] mmc_host mmc0: Bus speed (slot 0) = 100000000Hz (slot req 784314Hz, actual 781250HZ div = 64)

Best Regards,
Jaehoon Chung
> 
> Thanks,
> Thomas.
> --
> 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
> 

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v5 4/9] mmc: dw_mmc: lookup for optional biu and ciu clocks
  2012-08-31  7:59           ` Jaehoon Chung
@ 2012-08-31  8:48             ` Thomas Abraham
  2012-09-04 19:46               ` Thomas Abraham
  0 siblings, 1 reply; 29+ messages in thread
From: Thomas Abraham @ 2012-08-31  8:48 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: linux-mmc, devicetree-discuss, will.newton, cjb, grant.likely,
	rob.herring, linux-samsung-soc, kgene.kim, girish.shivananjappa,
	tgih.jun, patches

On 31 August 2012 13:29, Jaehoon Chung <jh80.chung@samsung.com> wrote:
>>>>>> +
>>>>>> +     host->ciu_clk = clk_get(host->dev, "ciu");
>>>>>> +     if (IS_ERR(host->ciu_clk))
>>>>>> +             dev_dbg(host->dev, "ciu clock not available\n");
>>>>>> +     else
>>>>>> +             clk_prepare_enable(host->ciu_clk);
>>>>>> +
>>>>>> +     if (IS_ERR(host->ciu_clk))
>>>>>> +             host->bus_hz = host->pdata->bus_hz;
>>>>>> +     else
>>>>>> +             host->bus_hz = clk_get_rate(host->ciu_clk);
>>>>> if clk_get_rate() is incorrect value(ex,400MHz),
>>>>> then mmc->f_min value is too high.
>>>>> because mmc->f_min is assigned to DIV_ROUND_UP(host->bus_hz, 510) into dw_mc_init_slot.
>>>>> Do you have any opinion for solving this?
>>>>
>>>> One option on Exynos5250 is to use the clock divider in the CLKSEL
>>>> register to divide the ciu clock to a lower value. For Exynos4, since
>>>> there is no clock divider in CLKSEL register, the platform code should
>>>> ensure that the ciu clock has a valid range.
>>> I know that can use div-ratio filed at the clksel register.
>>> On Exynos5, i known that is used the div-ratio at CLKSEL register.
>>> If ciu-clock is 400MHz, host->bus_hz is assigned to 400MHz.
>>> 1) host->bus_hz -> 400MHz (at dw-mmc-pltfm.c)
>>> 2) mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510) then mmc->f_min is set to 784KHz.
>>> 3) then host->bus_hz is re-assigned to value that is divided to div-ratio at CLKSEL register.
>>>    at this time, host->bus_hz = 100MHz...
>>>
>>> I think this sequence is something wrong.
>>> (Is 784KHz too high for init card?)
>>>
>>> It's just my thinking..if my understanding is wrong, let me know plz.
>>
>> You have listed the steps 1 to 3 correctly. So, as per step 3, 100Mhz
>> / 510 ~= 196KHz. Which is well within 400KHz. So do you still see a
>> problem here?
> How do you think about this?
> [ 4.620000] mmc_host mmc0: Bus speed (slot 0) = 100000000Hz (slot req 784314Hz, actual 781250HZ div = 64)

You are right, the sequence is wrong. The host->bus_hz is set to the
correct value of 100Mhz in set_ios callback of exynos. But much prior
to that, the dw_mci_init_slot is called and that initializes
mmc->f_min to host->bus_hz / 510 = 400Mhz / 510 = 768KHz.

To fix this, I am planning to add another implementation specific
callback function that will adjust the host->bus_clk as per
implementation specific extensions. This callback will be called right
from dw_mci_probe right after the host->bus_hz is set using the
clk_get_rate() call.

Sorry, I was not paying proper attention to the log messages. Thanks
for your correction.

Regards,
Thomas.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH v5 4/9] mmc: dw_mmc: lookup for optional biu and ciu clocks
  2012-08-31  8:48             ` Thomas Abraham
@ 2012-09-04 19:46               ` Thomas Abraham
  2012-09-05  8:30                 ` Jaehoon Chung
  0 siblings, 1 reply; 29+ messages in thread
From: Thomas Abraham @ 2012-09-04 19:46 UTC (permalink / raw)
  To: linux-mmc, devicetree-discuss
  Cc: will.newton, cjb, grant.likely, rob.herring, linux-samsung-soc,
	kgene.kim, girish.shivananjappa, jh80.chung, tgih.jun, patches

Some platforms allow for clock gating and control of bus interface unit clock
and card interface unit clock. Add support for clock lookup of optional biu
and ciu clocks for clock gating and clock speed determination.

Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
Acked-by: Will Newton <will.newton@imgtec.com>
---
 drivers/mmc/host/dw_mmc.c  |   50 +++++++++++++++++++++++++++++++++++++++++--
 include/linux/mmc/dw_mmc.h |    4 +++
 2 files changed, 51 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 227c42e..e8c8491 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -1960,13 +1960,40 @@ int dw_mci_probe(struct dw_mci *host)
 		return -ENODEV;
 	}
 
-	if (!host->pdata->bus_hz) {
+	host->biu_clk = clk_get(host->dev, "biu");
+	if (IS_ERR(host->biu_clk)) {
+		dev_dbg(host->dev, "biu clock not available\n");
+	} else {
+		ret = clk_prepare_enable(host->biu_clk);
+		if (ret) {
+			dev_err(host->dev, "failed to enable biu clock\n");
+			return ret;
+		}
+	}
+
+	host->ciu_clk = clk_get(host->dev, "ciu");
+	if (IS_ERR(host->ciu_clk)) {
+		dev_dbg(host->dev, "ciu clock not available\n");
+	} else {
+		ret = clk_prepare_enable(host->ciu_clk);
+		if (ret) {
+			dev_err(host->dev, "failed to enable ciu clock\n");
+			goto err_clk_biu;
+		}
+	}
+
+	if (IS_ERR(host->ciu_clk))
+		host->bus_hz = host->pdata->bus_hz;
+	else
+		host->bus_hz = clk_get_rate(host->ciu_clk);
+
+	if (!host->bus_hz) {
 		dev_err(host->dev,
 			"Platform data must supply bus speed\n");
-		return -ENODEV;
+		ret = -ENODEV;
+		goto err_clk_ciu;
 	}
 
-	host->bus_hz = host->pdata->bus_hz;
 	host->quirks = host->pdata->quirks;
 
 	spin_lock_init(&host->lock);
@@ -2116,6 +2143,17 @@ err_dmaunmap:
 		regulator_disable(host->vmmc);
 		regulator_put(host->vmmc);
 	}
+
+err_clk_ciu:
+	if (!IS_ERR(host->ciu_clk)) {
+		clk_disable_unprepare(host->ciu_clk);
+		clk_put(host->ciu_clk);
+	}
+err_clk_biu:
+	if (!IS_ERR(host->biu_clk)) {
+		clk_disable_unprepare(host->biu_clk);
+		clk_put(host->biu_clk);
+	}
 	return ret;
 }
 EXPORT_SYMBOL(dw_mci_probe);
@@ -2149,6 +2187,12 @@ void dw_mci_remove(struct dw_mci *host)
 		regulator_put(host->vmmc);
 	}
 
+	if (!IS_ERR(host->ciu_clk))
+		clk_disable_unprepare(host->ciu_clk);
+	if (!IS_ERR(host->biu_clk))
+		clk_disable_unprepare(host->biu_clk);
+	clk_put(host->ciu_clk);
+	clk_put(host->biu_clk);
 }
 EXPORT_SYMBOL(dw_mci_remove);
 
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
index a37a573..787ad56 100644
--- a/include/linux/mmc/dw_mmc.h
+++ b/include/linux/mmc/dw_mmc.h
@@ -78,6 +78,8 @@ struct mmc_data;
  * @data_offset: Set the offset of DATA register according to VERID.
  * @dev: Device associated with the MMC controller.
  * @pdata: Platform data associated with the MMC controller.
+ * @biu_clk: Pointer to bus interface unit clock instance.
+ * @ciu_clk: Pointer to card interface unit clock instance.
  * @slot: Slots sharing this MMC controller.
  * @fifo_depth: depth of FIFO.
  * @data_shift: log2 of FIFO item size.
@@ -158,6 +160,8 @@ struct dw_mci {
 	u16			data_offset;
 	struct device		*dev;
 	struct dw_mci_board	*pdata;
+	struct clk		*biu_clk;
+	struct clk		*ciu_clk;
 	struct dw_mci_slot	*slot[MAX_MCI_SLOTS];
 
 	/* FIFO push and pull */
-- 
1.6.6.rc2


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v5 8/9] mmc: dw_mmc: add support for implementation specific callbacks
  2012-08-29 10:48 ` [PATCH v5 8/9] mmc: dw_mmc: add support for implementation specific callbacks Thomas Abraham
@ 2012-09-04 19:47   ` Thomas Abraham
  2012-09-07  9:40   ` Thomas Abraham
  1 sibling, 0 replies; 29+ messages in thread
From: Thomas Abraham @ 2012-09-04 19:47 UTC (permalink / raw)
  To: linux-mmc, devicetree-discuss
  Cc: will.newton, cjb, grant.likely, rob.herring, linux-samsung-soc,
	kgene.kim, girish.shivananjappa, jh80.chung, tgih.jun, patches

The core dw-mshc controller driver can let platform specific implementations
of the dw-mshc controller to control the hardware as required by such
implementations. This is acheived by invoking implementation specific (optional)
callbacks. Define the list of callbacks supported the add invocation points
for the same.

Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
Acked-by: Will Newton <will.newton@imgtec.com>
---
 drivers/mmc/host/dw_mmc-pltfm.c |   12 ++++++++-
 drivers/mmc/host/dw_mmc-pltfm.h |    3 +-
 drivers/mmc/host/dw_mmc.c       |   47 ++++++++++++++++++++++++++++++++++++++-
 drivers/mmc/host/dw_mmc.h       |   24 ++++++++++++++++++++
 include/linux/mmc/dw_mmc.h      |    4 +++
 5 files changed, 86 insertions(+), 4 deletions(-)

diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index e17da91..c960ca7 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -23,7 +23,8 @@
 
 #include "dw_mmc.h"
 
-int dw_mci_pltfm_register(struct platform_device *pdev)
+int dw_mci_pltfm_register(struct platform_device *pdev,
+				struct dw_mci_drv_data *drv_data)
 {
 	struct dw_mci *host;
 	struct resource	*regs;
@@ -41,6 +42,7 @@ int dw_mci_pltfm_register(struct platform_device *pdev)
 	if (host->irq < 0)
 		return host->irq;
 
+	host->drv_data = drv_data;
 	host->dev = &pdev->dev;
 	host->irq_flags = 0;
 	host->pdata = pdev->dev.platform_data;
@@ -48,6 +50,12 @@ int dw_mci_pltfm_register(struct platform_device *pdev)
 	if (!host->regs)
 		return -ENOMEM;
 
+	if (host->drv_data->init) {
+		ret = host->drv_data->init(host);
+		if (ret)
+			return ret;
+	}
+
 	platform_set_drvdata(pdev, host);
 	ret = dw_mci_probe(host);
 	return ret;
@@ -56,7 +64,7 @@ EXPORT_SYMBOL_GPL(dw_mci_pltfm_register);
 
 static int __devinit dw_mci_pltfm_probe(struct platform_device *pdev)
 {
-	return dw_mci_pltfm_register(pdev);
+	return dw_mci_pltfm_register(pdev, NULL);
 }
 
 static int __devexit dw_mci_pltfm_remove(struct platform_device *pdev)
diff --git a/drivers/mmc/host/dw_mmc-pltfm.h b/drivers/mmc/host/dw_mmc-pltfm.h
index 6c065d9..301f245 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.h
+++ b/drivers/mmc/host/dw_mmc-pltfm.h
@@ -12,7 +12,8 @@
 #ifndef _DW_MMC_PLTFM_H_
 #define _DW_MMC_PLTFM_H_
 
-extern int dw_mci_pltfm_register(struct platform_device *pdev);
+extern int dw_mci_pltfm_register(struct platform_device *pdev,
+				struct dw_mci_drv_data *drv_data);
 extern int __devexit dw_mci_pltfm_remove(struct platform_device *pdev);
 extern const struct dev_pm_ops dw_mci_pltfm_pmops;
 
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 1308619..6104c0f 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -231,6 +231,7 @@ static void dw_mci_set_timeout(struct dw_mci *host)
 static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
 {
 	struct mmc_data	*data;
+	struct dw_mci_slot *slot = mmc_priv(mmc);
 	u32 cmdr;
 	cmd->error = -EINPROGRESS;
 
@@ -260,6 +261,9 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
 			cmdr |= SDMMC_CMD_DAT_WR;
 	}
 
+	if (slot->host->drv_data->prepare_command)
+		slot->host->drv_data->prepare_command(slot->host, &cmdr);
+
 	return cmdr;
 }
 
@@ -815,6 +819,9 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 		slot->clock = ios->clock;
 	}
 
+	if (slot->host->drv_data->set_ios)
+		slot->host->drv_data->set_ios(slot->host, ios);
+
 	switch (ios->power_mode) {
 	case MMC_POWER_UP:
 		set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
@@ -1820,6 +1827,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 {
 	struct mmc_host *mmc;
 	struct dw_mci_slot *slot;
+	int ctrl_id, ret;
 	u8 bus_width;
 
 	mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
@@ -1851,6 +1859,16 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 	if (host->pdata->caps)
 		mmc->caps = host->pdata->caps;
 
+	if (host->dev->of_node) {
+		ctrl_id = of_alias_get_id(host->dev->of_node, "mshc");
+		if (ctrl_id < 0)
+			ctrl_id = 0;
+	} else {
+		ctrl_id = to_platform_device(host->dev)->id;
+	}
+	if (host->drv_data && host->drv_data->caps)
+		mmc->caps |= host->drv_data->caps[ctrl_id];
+
 	if (host->pdata->caps2)
 		mmc->caps2 = host->pdata->caps2;
 
@@ -1861,6 +1879,14 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 	else
 		bus_width = 1;
 
+	if (host->drv_data->setup_bus) {
+		struct device_node *slot_np;
+		slot_np = dw_mci_of_find_slot_node(host->dev, slot->id);
+		ret = host->drv_data->setup_bus(host, slot_np, bus_width);
+		if (ret)
+			goto err_setup_bus;
+	}
+
 	switch (bus_width) {
 	case 8:
 		mmc->caps |= MMC_CAP_8_BIT_DATA;
@@ -1927,6 +1953,10 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 	queue_work(host->card_workqueue, &host->card_work);
 
 	return 0;
+
+err_setup_bus:
+	mmc_free_host(mmc);
+	return -EINVAL;
 }
 
 static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id)
@@ -2021,7 +2051,7 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
 	struct dw_mci_board *pdata;
 	struct device *dev = host->dev;
 	struct device_node *np = dev->of_node;
-	int idx;
+	int idx, ret;
 
 	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
 	if (!pdata) {
@@ -2048,6 +2078,12 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
 
 	of_property_read_u32(np, "card-detect-delay", &pdata->detect_delay_ms);
 
+	if (host->drv_data->parse_dt) {
+		ret = host->drv_data->parse_dt(host);
+		if (ret)
+			return ERR_PTR(ret);
+	}
+
 	return pdata;
 }
 
@@ -2095,6 +2131,15 @@ int dw_mci_probe(struct dw_mci *host)
 	else
 		host->bus_hz = clk_get_rate(host->ciu_clk);
 
+	if (host->drv_data->setup_clock) {
+		ret = host->drv_data->setup_clock(host);
+		if (ret) {
+			dev_err(host->dev,
+				"implementation specific clock setup failed\n");
+			return ret;
+		}
+	}
+
 	if (!host->bus_hz) {
 		dev_err(host->dev,
 			"Platform data must supply bus speed\n");
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 15c27e1..53b8fd9 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -182,4 +182,28 @@ extern int dw_mci_suspend(struct dw_mci *host);
 extern int dw_mci_resume(struct dw_mci *host);
 #endif
 
+/**
+ * dw_mci driver data - dw-mshc implementation specific driver data.
+ * @caps: mmc subsystem specified capabilities of the controller(s).
+ * @init: early implementation specific initialization.
+ * @setup_clock: implementation specific clock configuration.
+ * @prepare_command: handle CMD register extensions.
+ * @set_ios: handle bus specific extensions.
+ * @parse_dt: parse implementation specific device tree properties.
+ * @setup_bus: initialize io-interface
+ *
+ * Provide controller implementation specific extensions. The usage of this
+ * data structure is fully optional and usage of each member in this structure
+ * is optional as well.
+ */
+struct dw_mci_drv_data {
+	unsigned long	*caps;
+	int		(*init)(struct dw_mci *host);
+	int		(*setup_clock)(struct dw_mci *host);
+	void		(*prepare_command)(struct dw_mci *host, u32 *cmdr);
+	void		(*set_ios)(struct dw_mci *host, struct mmc_ios *ios);
+	int		(*parse_dt)(struct dw_mci *host);
+	int		(*setup_bus)(struct dw_mci *host,
+				struct device_node *slot_np, u8 bus_width);
+};
 #endif /* _DW_MMC_H_ */
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
index b72e4aa..6cb043e 100644
--- a/include/linux/mmc/dw_mmc.h
+++ b/include/linux/mmc/dw_mmc.h
@@ -78,6 +78,8 @@ struct mmc_data;
  * @data_offset: Set the offset of DATA register according to VERID.
  * @dev: Device associated with the MMC controller.
  * @pdata: Platform data associated with the MMC controller.
+ * @drv_data: Driver specific data for identified variant of the controller
+ * @priv: Implementation defined private data.
  * @biu_clk: Pointer to bus interface unit clock instance.
  * @ciu_clk: Pointer to card interface unit clock instance.
  * @slot: Slots sharing this MMC controller.
@@ -160,6 +162,8 @@ struct dw_mci {
 	u16			data_offset;
 	struct device		*dev;
 	struct dw_mci_board	*pdata;
+	struct dw_mci_drv_data	*drv_data;
+	void			*priv;
 	struct clk		*biu_clk;
 	struct clk		*ciu_clk;
 	struct dw_mci_slot	*slot[MAX_MCI_SLOTS];
-- 
1.6.6.rc2

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v5 9/9] mmc: dw_mmc: add support for exynos specific implementation of dw-mshc
  2012-08-29 10:48 ` [PATCH v5 9/9] mmc: dw_mmc: add support for exynos specific implementation of dw-mshc Thomas Abraham
@ 2012-09-04 19:48   ` Thomas Abraham
  2012-09-05 10:43     ` Seungwon Jeon
  0 siblings, 1 reply; 29+ messages in thread
From: Thomas Abraham @ 2012-09-04 19:48 UTC (permalink / raw)
  To: linux-mmc, devicetree-discuss
  Cc: will.newton, cjb, grant.likely, rob.herring, linux-samsung-soc,
	kgene.kim, girish.shivananjappa, jh80.chung, tgih.jun, patches

Samsung Exynos SoC's extend the dw-mshc controller for additional clock and bus
control. Add support for these extensions and include provide device tree based
discovery suppory as well.

Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
Acked-by: Will Newton <will.newton@imgtec.com>
---
 .../devicetree/bindings/mmc/exynos-dw-mshc.txt     |   86 +++++++
 drivers/mmc/host/Kconfig                           |    9 +
 drivers/mmc/host/Makefile                          |    1 +
 drivers/mmc/host/dw_mmc-exynos.c                   |  253 ++++++++++++++++++++
 4 files changed, 349 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
 create mode 100644 drivers/mmc/host/dw_mmc-exynos.c

diff --git a/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
new file mode 100644
index 0000000..323a891
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
@@ -0,0 +1,86 @@
+* Samsung Exynos specific extensions to the Synopsis Designware Mobile
+  Storage Host Controller
+
+The Synopsis designware mobile storage host controller is used to interface
+a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
+differences between the core Synopsis dw mshc controller properties described
+by synposis-dw-mshc.txt and the properties used by the Samsung Exynos specific
+extensions to the Synopsis Designware Mobile Storage Host Controller.
+
+Required Properties:
+
+* compatible: should be
+	- "samsung,exynos4210-dw-mshc": for controllers with Samsung Exynos4210
+	  specific extentions.
+	- "samsung,exynos4412-dw-mshc": for controllers with Samsung Exynos4412
+	  specific extentions.
+	- "samsung,exynos5250-dw-mshc": for controllers with Samsung Exynos5250
+	  specific extentions.
+
+* samsung,dw-mshc-ciu-div: Specifies the divider value for the card interface
+  unit (ciu) clock. This property is applicable only for Exynos5 SoC's and
+  ignored for Exynos4 SoC's. The valid range of divider value is 0 to 7.
+
+* samsung,dw-mshc-sdr-timing: Specifies the value of CIU clock phase shift value
+  in transmit mode and CIU clock phase shift value in receive mode for single
+  data rate mode operation. Refer notes below for the order of the cells and the
+  valid values.
+
+* samsung,dw-mshc-ddr-timing: Specifies the value of CUI clock phase shift value
+  in transmit mode and CIU clock phase shift value in receive mode for double
+  data rate mode operation. Refer notes below for the order of the cells and the
+  valid values.
+
+  Notes for the sdr-timing and ddr-timing values:
+
+    The order of the cells should be
+      - First Cell: CIU clock phase shift value for tx mode.
+      - Second Cell: CIU clock phase shift value for rx mode.
+
+    Valid values for SDR and DDR CIU clock timing for Exynos5250:
+      - valid value for tx phase shift and rx phase shift is 0 to 7.
+      - when CIU clock divider value is set to 3, all possible 8 phase shift
+        values can be used.
+      - if CIU clock divider value is 0 (that is divide by 1), both tx and rx
+        phase shift clocks should be 0.
+
+Required properties for a slot:
+
+* gpios: specifies a list of gpios used for command, clock and data bus. The
+  first gpio is the command line and the second gpio is the clock line. The
+  rest of the gpios (depending on the bus-width property) are the data lines in
+  no particular order. The format of the gpio specifier depends on the gpio
+  controller.
+
+Example:
+
+  The MSHC controller node can be split into two portions, SoC specific and
+  board specific portions as listed below.
+
+	dwmmc0@12200000 {
+		compatible = "samsung,exynos5250-dw-mshc";
+		reg = <0x12200000 0x1000>;
+		interrupts = <0 75 0>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+	};
+
+	dwmmc0@12200000 {
+		num-slots = <1>;
+		supports-highspeed;
+		broken-cd;
+		fifo-depth = <0x80>;
+		card-detect-delay = <200>;
+		samsung,dw-mshc-sdr-timing = <2 3 3>;
+		samsung,dw-mshc-ddr-timing = <1 2 3>;
+
+		slot@0 {
+			reg = <0>;
+			bus-width = <8>;
+			gpios = <&gpc0 0 2 0 3>, <&gpc0 1 2 0 3>,
+				<&gpc1 0 2 3 3>, <&gpc1 1 2 3 3>,
+				<&gpc1 2 2 3 3>, <&gpc1 3 2 3 3>,
+				<&gpc0 3 2 3 3>, <&gpc0 4 2 3 3>,
+				<&gpc0 5 2 3 3>, <&gpc0 6 2 3 3>;
+		};
+	};
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index aa131b3..9bf10e7 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -540,6 +540,15 @@ config MMC_DW_PLTFM
 
 	  If unsure, say Y.
 
+config MMC_DW_EXYNOS
+	tristate "Exynos specific extentions for Synopsys DW Memory Card Interface"
+	depends on MMC_DW
+	select MMC_DW_PLTFM
+	help
+	  This selects support for Samsung Exynos SoC specific extensions to the
+	  Synopsys DesignWare Memory Card Interface driver. Select this option
+	  for platforms based on Exynos4 and Exynos5 SoC's.
+
 config MMC_DW_PCI
 	tristate "Synopsys Designware MCI support on PCI bus"
 	depends on MMC_DW && PCI
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 8922b06..17ad0a7 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_MMC_VIA_SDMMC)	+= via-sdmmc.o
 obj-$(CONFIG_SDH_BFIN)		+= bfin_sdh.o
 obj-$(CONFIG_MMC_DW)		+= dw_mmc.o
 obj-$(CONFIG_MMC_DW_PLTFM)	+= dw_mmc-pltfm.o
+obj-$(CONFIG_MMC_DW_EXYNOS)	+= dw_mmc-exynos.o
 obj-$(CONFIG_MMC_DW_PCI)	+= dw_mmc-pci.o
 obj-$(CONFIG_MMC_SH_MMCIF)	+= sh_mmcif.o
 obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c
new file mode 100644
index 0000000..313f364
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc-exynos.c
@@ -0,0 +1,253 @@
+/*
+ * Exynos Specific Extensions for Synopsys DW Multimedia Card Interface driver
+ *
+ * Copyright (C) 2012, Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/dw_mmc.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+
+#include "dw_mmc.h"
+#include "dw_mmc-pltfm.h"
+
+#define NUM_PINS(x)			(x + 2)
+
+#define SDMMC_CLKSEL			0x09C
+#define SDMMC_CLKSEL_CCLK_SAMPLE(x)	(((x) & 7) << 0)
+#define SDMMC_CLKSEL_CCLK_DRIVE(x)	(((x) & 7) << 16)
+#define SDMMC_CLKSEL_CCLK_DIVIDER(x)	(((x) & 7) << 24)
+#define SDMMC_CLKSEL_GET_DRV_WD3(x)	(((x) >> 16) & 0x7)
+#define SDMMC_CLKSEL_TIMING(x, y, z)	(SDMMC_CLKSEL_CCLK_SAMPLE(x) |	\
+					SDMMC_CLKSEL_CCLK_DRIVE(y) |	\
+					SDMMC_CLKSEL_CCLK_DIVIDER(z))
+
+#define SDMMC_CMD_USE_HOLD_REG		BIT(29)
+
+#define EXYNOS4210_FIXED_CIU_CLK_DIV	2
+#define EXYNOS4412_FIXED_CIU_CLK_DIV	4
+
+/* Variations in Exynos specific dw-mshc controller */
+enum dw_mci_exynos_type {
+	DW_MCI_TYPE_EXYNOS4210,
+	DW_MCI_TYPE_EXYNOS4412,
+	DW_MCI_TYPE_EXYNOS5250,
+};
+
+/* Exynos implementation specific driver private data */
+struct dw_mci_exynos_priv_data {
+	enum dw_mci_exynos_type		ctrl_type;
+	u8				ciu_div;
+	u32				sdr_timing;
+	u32				ddr_timing;
+};
+
+static struct dw_mci_exynos_compatible {
+	char				*compatible;
+	enum dw_mci_exynos_type		ctrl_type;
+} exynos_compat[] = {
+	{
+		.compatible	= "samsung,exynos4210-dw-mshc",
+		.ctrl_type	= DW_MCI_TYPE_EXYNOS4210,
+	}, {
+		.compatible	= "samsung,exynos4412-dw-mshc",
+		.ctrl_type	= DW_MCI_TYPE_EXYNOS4412,
+	}, {
+		.compatible	= "samsung,exynos5250-dw-mshc",
+		.ctrl_type	= DW_MCI_TYPE_EXYNOS5250,
+	},
+};
+
+static int dw_mci_exynos_priv_init(struct dw_mci *host)
+{
+	struct dw_mci_exynos_priv_data *priv;
+	int idx;
+
+	priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		dev_err(host->dev, "mem alloc failed for private data\n");
+		return -ENOMEM;
+	}
+
+	for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
+		if (of_device_is_compatible(host->dev->of_node,
+					exynos_compat[idx].compatible))
+			priv->ctrl_type = exynos_compat[idx].ctrl_type;
+	}
+
+	host->priv = priv;
+	return 0;
+}
+
+static int dw_mci_exynos_setup_clock(struct dw_mci *host)
+{
+	struct dw_mci_exynos_priv_data *priv = host->priv;
+
+	if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5250)
+		host->bus_hz /= (priv->ciu_div + 1);
+	else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
+		host->bus_hz /= EXYNOS4412_FIXED_CIU_CLK_DIV;
+	else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
+		host->bus_hz /= EXYNOS4210_FIXED_CIU_CLK_DIV;
+
+	return 0;
+}
+
+static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
+{
+	/*
+	 * Exynos4412 and Exynos5250 extends the use of CMD register with the
+	 * use of bit 29 (which is reserved on standard MSHC controllers) for
+	 * optionally bypassing the HOLD register for command and data. The
+	 * HOLD register should be bypassed in case there is no phase shift
+	 * applied on CMD/DATA that is sent to the card.
+	 */
+	if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL)))
+		*cmdr |= SDMMC_CMD_USE_HOLD_REG;
+}
+
+static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
+{
+	struct dw_mci_exynos_priv_data *priv = host->priv;
+
+	if (ios->timing == MMC_TIMING_UHS_DDR50)
+		mci_writel(host, CLKSEL, priv->ddr_timing);
+	else
+		mci_writel(host, CLKSEL, priv->sdr_timing);
+}
+
+static int dw_mci_exynos_parse_dt(struct dw_mci *host)
+{
+	struct dw_mci_exynos_priv_data *priv = host->priv;
+	struct device_node *np = host->dev->of_node;
+	u32 timing[3];
+	u32 div = 0;
+	int ret;
+
+	of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
+	priv->ciu_div = div;
+
+	ret = of_property_read_u32_array(np,
+			"samsung,dw-mshc-sdr-timing", timing, 2);
+	if (ret)
+		return ret;
+
+	priv->sdr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
+
+	ret = of_property_read_u32_array(np,
+			"samsung,dw-mshc-ddr-timing", timing, 2);
+	if (ret)
+		return ret;
+
+	priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
+	return 0;
+}
+
+static int dw_mci_exynos_setup_bus(struct dw_mci *host,
+				struct device_node *slot_np, u8 bus_width)
+{
+	int idx, gpio, ret;
+
+	if (!slot_np)
+		return -EINVAL;
+
+	/* cmd + clock + bus-width pins */
+	for (idx = 0; idx < NUM_PINS(bus_width); idx++) {
+		gpio = of_get_gpio(slot_np, idx);
+		if (!gpio_is_valid(gpio)) {
+			dev_err(host->dev, "invalid gpio: %d\n", gpio);
+			return -EINVAL;
+		}
+
+		ret = devm_gpio_request(host->dev, gpio, "dw-mci-bus");
+		if (ret) {
+			dev_err(host->dev, "gpio [%d] request failed\n", gpio);
+			return -EBUSY;
+		}
+	}
+
+	gpio = of_get_named_gpio(slot_np, "wp-gpios", 0);
+	if (gpio_is_valid(gpio)) {
+		if (devm_gpio_request(host->dev, gpio, "dw-mci-wp"))
+			dev_info(host->dev, "gpio [%d] request failed\n",
+						gpio);
+	} else {
+		dev_info(host->dev, "wp gpio not available");
+		host->pdata->quirks |= DW_MCI_QUIRK_NO_WRITE_PROTECT;
+	}
+
+	if (host->pdata->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)
+		return 0;
+
+	gpio = of_get_named_gpio(slot_np, "samsung,cd-pinmux-gpio", 0);
+	if (gpio_is_valid(gpio)) {
+		if (devm_gpio_request(host->dev, gpio, "dw-mci-cd"))
+			dev_err(host->dev, "gpio [%d] request failed\n", gpio);
+	} else {
+		dev_info(host->dev, "cd gpio not available");
+	}
+
+	return 0;
+}
+
+/* Exynos5250 controller specific capabilities */
+static unsigned long exynos5250_dwmmc_caps[4] = {
+	MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR |
+		MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23,
+	MMC_CAP_CMD23,
+	MMC_CAP_CMD23,
+	MMC_CAP_CMD23,
+};
+
+static struct dw_mci_drv_data exynos5250_drv_data = {
+	.caps			= exynos5250_dwmmc_caps,
+	.init			= dw_mci_exynos_priv_init,
+	.setup_clock		= dw_mci_exynos_setup_clock,
+	.prepare_command	= dw_mci_exynos_prepare_command,
+	.set_ios		= dw_mci_exynos_set_ios,
+	.parse_dt		= dw_mci_exynos_parse_dt,
+	.setup_bus		= dw_mci_exynos_setup_bus,
+};
+
+static const struct of_device_id dw_mci_exynos_match[] = {
+	{ .compatible = "samsung,exynos5250-dw-mshc",
+			.data = (void *)&exynos5250_drv_data, },
+	{},
+};
+MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
+
+int dw_mci_exynos_probe(struct platform_device *pdev)
+{
+	struct dw_mci_drv_data *drv_data;
+	const struct of_device_id *match;
+
+	match = of_match_node(dw_mci_exynos_match, pdev->dev.of_node);
+	drv_data = match->data;
+	return dw_mci_pltfm_register(pdev, drv_data);
+}
+
+static struct platform_driver dw_mci_exynos_pltfm_driver = {
+	.probe		= dw_mci_exynos_probe,
+	.remove		= __exit_p(dw_mci_pltfm_remove),
+	.driver		= {
+		.name		= "dwmmc_exynos",
+		.of_match_table	= of_match_ptr(dw_mci_exynos_match),
+		.pm		= &dw_mci_pltfm_pmops,
+	},
+};
+
+module_platform_driver(dw_mci_exynos_pltfm_driver);
+
+MODULE_DESCRIPTION("Samsung Specific DW-MSHC Driver Extension");
+MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dwmmc-exynos");
-- 
1.6.6.rc2


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH v5 4/9] mmc: dw_mmc: lookup for optional biu and ciu clocks
  2012-09-04 19:46               ` Thomas Abraham
@ 2012-09-05  8:30                 ` Jaehoon Chung
  2012-09-07  6:26                   ` Thomas Abraham
  0 siblings, 1 reply; 29+ messages in thread
From: Jaehoon Chung @ 2012-09-05  8:30 UTC (permalink / raw)
  To: Thomas Abraham
  Cc: linux-mmc, devicetree-discuss, will.newton, cjb, grant.likely,
	rob.herring, linux-samsung-soc, kgene.kim, girish.shivananjappa,
	jh80.chung, tgih.jun, patches

On 09/05/2012 04:46 AM, Thomas Abraham wrote:
> Some platforms allow for clock gating and control of bus interface unit clock
> and card interface unit clock. Add support for clock lookup of optional biu
> and ciu clocks for clock gating and clock speed determination.
> 
> Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
> Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
> Acked-by: Will Newton <will.newton@imgtec.com>
> ---
>  drivers/mmc/host/dw_mmc.c  |   50 +++++++++++++++++++++++++++++++++++++++++--
>  include/linux/mmc/dw_mmc.h |    4 +++
>  2 files changed, 51 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
> index 227c42e..e8c8491 100644
> --- a/drivers/mmc/host/dw_mmc.c
> +++ b/drivers/mmc/host/dw_mmc.c
> @@ -1960,13 +1960,40 @@ int dw_mci_probe(struct dw_mci *host)
>  		return -ENODEV;
>  	}
>  
> -	if (!host->pdata->bus_hz) {
> +	host->biu_clk = clk_get(host->dev, "biu");
> +	if (IS_ERR(host->biu_clk)) {
> +		dev_dbg(host->dev, "biu clock not available\n");
> +	} else {
> +		ret = clk_prepare_enable(host->biu_clk);
> +		if (ret) {
> +			dev_err(host->dev, "failed to enable biu clock\n");
> +			return ret;
didn't clk_put() for biu_clk? 
> +		}
> +	}
> +
> +	host->ciu_clk = clk_get(host->dev, "ciu");
> +	if (IS_ERR(host->ciu_clk)) {
> +		dev_dbg(host->dev, "ciu clock not available\n");
> +	} else {
> +		ret = clk_prepare_enable(host->ciu_clk);
> +		if (ret) {
> +			dev_err(host->dev, "failed to enable ciu clock\n");
> +			goto err_clk_biu;
> +		}
> +	}
> +
> +	if (IS_ERR(host->ciu_clk))
> +		host->bus_hz = host->pdata->bus_hz;
> +	else
> +		host->bus_hz = clk_get_rate(host->ciu_clk);
> +
> +	if (!host->bus_hz) {
>  		dev_err(host->dev,
>  			"Platform data must supply bus speed\n");
> -		return -ENODEV;
> +		ret = -ENODEV;
> +		goto err_clk_ciu;
>  	}
>  
> -	host->bus_hz = host->pdata->bus_hz;
>  	host->quirks = host->pdata->quirks;
>  
>  	spin_lock_init(&host->lock);
> @@ -2116,6 +2143,17 @@ err_dmaunmap:
>  		regulator_disable(host->vmmc);
>  		regulator_put(host->vmmc);
>  	}
> +
> +err_clk_ciu:
> +	if (!IS_ERR(host->ciu_clk)) {
> +		clk_disable_unprepare(host->ciu_clk);
> +		clk_put(host->ciu_clk);
> +	}
> +err_clk_biu:
I think right that is located the clk_put(host->ciu_clk) at here
> +	if (!IS_ERR(host->biu_clk)) {
> +		clk_disable_unprepare(host->biu_clk);
> +		clk_put(host->biu_clk);
> +	}
>  	return ret;
>  }
>  EXPORT_SYMBOL(dw_mci_probe);
> @@ -2149,6 +2187,12 @@ void dw_mci_remove(struct dw_mci *host)
>  		regulator_put(host->vmmc);
>  	}
>  
> +	if (!IS_ERR(host->ciu_clk))
> +		clk_disable_unprepare(host->ciu_clk);
> +	if (!IS_ERR(host->biu_clk))
> +		clk_disable_unprepare(host->biu_clk);
> +	clk_put(host->ciu_clk);
> +	clk_put(host->biu_clk);
>  }
>  EXPORT_SYMBOL(dw_mci_remove);
>  
> diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
> index a37a573..787ad56 100644
> --- a/include/linux/mmc/dw_mmc.h
> +++ b/include/linux/mmc/dw_mmc.h
> @@ -78,6 +78,8 @@ struct mmc_data;
>   * @data_offset: Set the offset of DATA register according to VERID.
>   * @dev: Device associated with the MMC controller.
>   * @pdata: Platform data associated with the MMC controller.
> + * @biu_clk: Pointer to bus interface unit clock instance.
> + * @ciu_clk: Pointer to card interface unit clock instance.
>   * @slot: Slots sharing this MMC controller.
>   * @fifo_depth: depth of FIFO.
>   * @data_shift: log2 of FIFO item size.
> @@ -158,6 +160,8 @@ struct dw_mci {
>  	u16			data_offset;
>  	struct device		*dev;
>  	struct dw_mci_board	*pdata;
> +	struct clk		*biu_clk;
> +	struct clk		*ciu_clk;
>  	struct dw_mci_slot	*slot[MAX_MCI_SLOTS];
>  
>  	/* FIFO push and pull */
> 


^ permalink raw reply	[flat|nested] 29+ messages in thread

* RE: [PATCH v5 9/9] mmc: dw_mmc: add support for exynos specific implementation of dw-mshc
  2012-09-04 19:48   ` Thomas Abraham
@ 2012-09-05 10:43     ` Seungwon Jeon
  2012-09-07  7:15       ` Thomas Abraham
  0 siblings, 1 reply; 29+ messages in thread
From: Seungwon Jeon @ 2012-09-05 10:43 UTC (permalink / raw)
  To: 'Thomas Abraham', linux-mmc, devicetree-discuss
  Cc: will.newton, cjb, grant.likely, rob.herring, linux-samsung-soc,
	kgene.kim, girish.shivananjappa, jh80.chung, patches

On Wednesday, September 05, 2012, Thomas Abraham <thomas.abraham@linaro.org> wrote:
Version 6 is right?

> Samsung Exynos SoC's extend the dw-mshc controller for additional clock and bus
> control. Add support for these extensions and include provide device tree based
> discovery suppory as well.
> 
> Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
> Acked-by: Will Newton <will.newton@imgtec.com>
> ---
>  .../devicetree/bindings/mmc/exynos-dw-mshc.txt     |   86 +++++++
>  drivers/mmc/host/Kconfig                           |    9 +
>  drivers/mmc/host/Makefile                          |    1 +
>  drivers/mmc/host/dw_mmc-exynos.c                   |  253 ++++++++++++++++++++
>  4 files changed, 349 insertions(+), 0 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
>  create mode 100644 drivers/mmc/host/dw_mmc-exynos.c
> 
> diff --git a/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
> b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
> new file mode 100644
> index 0000000..323a891
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
> @@ -0,0 +1,86 @@
> +* Samsung Exynos specific extensions to the Synopsis Designware Mobile
> +  Storage Host Controller
> +
> +The Synopsis designware mobile storage host controller is used to interface
> +a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
> +differences between the core Synopsis dw mshc controller properties described
> +by synposis-dw-mshc.txt and the properties used by the Samsung Exynos specific
> +extensions to the Synopsis Designware Mobile Storage Host Controller.
> +
> +Required Properties:
> +
> +* compatible: should be
> +	- "samsung,exynos4210-dw-mshc": for controllers with Samsung Exynos4210
> +	  specific extentions.
> +	- "samsung,exynos4412-dw-mshc": for controllers with Samsung Exynos4412
> +	  specific extentions.
> +	- "samsung,exynos5250-dw-mshc": for controllers with Samsung Exynos5250
> +	  specific extentions.
> +
> +* samsung,dw-mshc-ciu-div: Specifies the divider value for the card interface
> +  unit (ciu) clock. This property is applicable only for Exynos5 SoC's and
> +  ignored for Exynos4 SoC's. The valid range of divider value is 0 to 7.
> +
> +* samsung,dw-mshc-sdr-timing: Specifies the value of CIU clock phase shift value
> +  in transmit mode and CIU clock phase shift value in receive mode for single
> +  data rate mode operation. Refer notes below for the order of the cells and the
> +  valid values.
> +
> +* samsung,dw-mshc-ddr-timing: Specifies the value of CUI clock phase shift value
> +  in transmit mode and CIU clock phase shift value in receive mode for double
> +  data rate mode operation. Refer notes below for the order of the cells and the
> +  valid values.
> +
> +  Notes for the sdr-timing and ddr-timing values:
> +
> +    The order of the cells should be
> +      - First Cell: CIU clock phase shift value for tx mode.
> +      - Second Cell: CIU clock phase shift value for rx mode.
> +
> +    Valid values for SDR and DDR CIU clock timing for Exynos5250:
> +      - valid value for tx phase shift and rx phase shift is 0 to 7.
> +      - when CIU clock divider value is set to 3, all possible 8 phase shift
> +        values can be used.
> +      - if CIU clock divider value is 0 (that is divide by 1), both tx and rx
> +        phase shift clocks should be 0.
> +
> +Required properties for a slot:
> +
> +* gpios: specifies a list of gpios used for command, clock and data bus. The
> +  first gpio is the command line and the second gpio is the clock line. The
> +  rest of the gpios (depending on the bus-width property) are the data lines in
> +  no particular order. The format of the gpio specifier depends on the gpio
> +  controller.
> +
> +Example:
> +
> +  The MSHC controller node can be split into two portions, SoC specific and
> +  board specific portions as listed below.
> +
> +	dwmmc0@12200000 {
> +		compatible = "samsung,exynos5250-dw-mshc";
> +		reg = <0x12200000 0x1000>;
> +		interrupts = <0 75 0>;
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +	};
> +
> +	dwmmc0@12200000 {
> +		num-slots = <1>;
> +		supports-highspeed;
> +		broken-cd;
> +		fifo-depth = <0x80>;
> +		card-detect-delay = <200>;
> +		samsung,dw-mshc-sdr-timing = <2 3 3>;
> +		samsung,dw-mshc-ddr-timing = <1 2 3>;
Third filed is still useful?


> +
> +		slot@0 {
> +			reg = <0>;
> +			bus-width = <8>;
> +			gpios = <&gpc0 0 2 0 3>, <&gpc0 1 2 0 3>,
> +				<&gpc1 0 2 3 3>, <&gpc1 1 2 3 3>,
> +				<&gpc1 2 2 3 3>, <&gpc1 3 2 3 3>,
> +				<&gpc0 3 2 3 3>, <&gpc0 4 2 3 3>,
> +				<&gpc0 5 2 3 3>, <&gpc0 6 2 3 3>;
> +		};
> +	};
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index aa131b3..9bf10e7 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -540,6 +540,15 @@ config MMC_DW_PLTFM
> 
>  	  If unsure, say Y.
> 
> +config MMC_DW_EXYNOS
> +	tristate "Exynos specific extentions for Synopsys DW Memory Card Interface"
> +	depends on MMC_DW
> +	select MMC_DW_PLTFM
> +	help
> +	  This selects support for Samsung Exynos SoC specific extensions to the
> +	  Synopsys DesignWare Memory Card Interface driver. Select this option
> +	  for platforms based on Exynos4 and Exynos5 SoC's.
> +
>  config MMC_DW_PCI
>  	tristate "Synopsys Designware MCI support on PCI bus"
>  	depends on MMC_DW && PCI
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index 8922b06..17ad0a7 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -39,6 +39,7 @@ obj-$(CONFIG_MMC_VIA_SDMMC)	+= via-sdmmc.o
>  obj-$(CONFIG_SDH_BFIN)		+= bfin_sdh.o
>  obj-$(CONFIG_MMC_DW)		+= dw_mmc.o
>  obj-$(CONFIG_MMC_DW_PLTFM)	+= dw_mmc-pltfm.o
> +obj-$(CONFIG_MMC_DW_EXYNOS)	+= dw_mmc-exynos.o
>  obj-$(CONFIG_MMC_DW_PCI)	+= dw_mmc-pci.o
>  obj-$(CONFIG_MMC_SH_MMCIF)	+= sh_mmcif.o
>  obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
> diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c
> new file mode 100644
> index 0000000..313f364
> --- /dev/null
> +++ b/drivers/mmc/host/dw_mmc-exynos.c
> @@ -0,0 +1,253 @@
> +/*
> + * Exynos Specific Extensions for Synopsys DW Multimedia Card Interface driver
> + *
> + * Copyright (C) 2012, Samsung Electronics Co., Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/clk.h>
> +#include <linux/mmc/host.h>
> +#include <linux/mmc/dw_mmc.h>
> +#include <linux/of.h>
> +#include <linux/of_gpio.h>
> +
> +#include "dw_mmc.h"
> +#include "dw_mmc-pltfm.h"
> +
> +#define NUM_PINS(x)			(x + 2)
> +
> +#define SDMMC_CLKSEL			0x09C
> +#define SDMMC_CLKSEL_CCLK_SAMPLE(x)	(((x) & 7) << 0)
> +#define SDMMC_CLKSEL_CCLK_DRIVE(x)	(((x) & 7) << 16)
> +#define SDMMC_CLKSEL_CCLK_DIVIDER(x)	(((x) & 7) << 24)
> +#define SDMMC_CLKSEL_GET_DRV_WD3(x)	(((x) >> 16) & 0x7)
> +#define SDMMC_CLKSEL_TIMING(x, y, z)	(SDMMC_CLKSEL_CCLK_SAMPLE(x) |	\
> +					SDMMC_CLKSEL_CCLK_DRIVE(y) |	\
> +					SDMMC_CLKSEL_CCLK_DIVIDER(z))
> +
> +#define SDMMC_CMD_USE_HOLD_REG		BIT(29)
> +
> +#define EXYNOS4210_FIXED_CIU_CLK_DIV	2
> +#define EXYNOS4412_FIXED_CIU_CLK_DIV	4
> +
> +/* Variations in Exynos specific dw-mshc controller */
> +enum dw_mci_exynos_type {
> +	DW_MCI_TYPE_EXYNOS4210,
> +	DW_MCI_TYPE_EXYNOS4412,
> +	DW_MCI_TYPE_EXYNOS5250,
> +};
> +
> +/* Exynos implementation specific driver private data */
> +struct dw_mci_exynos_priv_data {
> +	enum dw_mci_exynos_type		ctrl_type;
> +	u8				ciu_div;
> +	u32				sdr_timing;
> +	u32				ddr_timing;
> +};
> +
> +static struct dw_mci_exynos_compatible {
> +	char				*compatible;
> +	enum dw_mci_exynos_type		ctrl_type;
> +} exynos_compat[] = {
> +	{
> +		.compatible	= "samsung,exynos4210-dw-mshc",
> +		.ctrl_type	= DW_MCI_TYPE_EXYNOS4210,
> +	}, {
> +		.compatible	= "samsung,exynos4412-dw-mshc",
> +		.ctrl_type	= DW_MCI_TYPE_EXYNOS4412,
> +	}, {
> +		.compatible	= "samsung,exynos5250-dw-mshc",
> +		.ctrl_type	= DW_MCI_TYPE_EXYNOS5250,
> +	},
> +};
> +
> +static int dw_mci_exynos_priv_init(struct dw_mci *host)
> +{
> +	struct dw_mci_exynos_priv_data *priv;
> +	int idx;
> +
> +	priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv) {
> +		dev_err(host->dev, "mem alloc failed for private data\n");
> +		return -ENOMEM;
> +	}
> +
> +	for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
> +		if (of_device_is_compatible(host->dev->of_node,
> +					exynos_compat[idx].compatible))
> +			priv->ctrl_type = exynos_compat[idx].ctrl_type;
> +	}
> +
> +	host->priv = priv;
> +	return 0;
> +}
> +
> +static int dw_mci_exynos_setup_clock(struct dw_mci *host)
> +{
> +	struct dw_mci_exynos_priv_data *priv = host->priv;
> +
It assume that initial bus_hz is from clk_get_rate.
Can it be ensured? If value of bus_hz is set from platform, calculation is wrong below.

> +	if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5250)
> +		host->bus_hz /= (priv->ciu_div + 1);
> +	else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
> +		host->bus_hz /= EXYNOS4412_FIXED_CIU_CLK_DIV;
> +	else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
> +		host->bus_hz /= EXYNOS4210_FIXED_CIU_CLK_DIV;
> +
> +	return 0;
Here,  returning is any meaning?
There seems to be no error case.

> +}
> +
> +static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
> +{
> +	/*
> +	 * Exynos4412 and Exynos5250 extends the use of CMD register with the
> +	 * use of bit 29 (which is reserved on standard MSHC controllers) for
> +	 * optionally bypassing the HOLD register for command and data. The
> +	 * HOLD register should be bypassed in case there is no phase shift
> +	 * applied on CMD/DATA that is sent to the card.
> +	 */
> +	if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL)))
> +		*cmdr |= SDMMC_CMD_USE_HOLD_REG;
> +}
> +
> +static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
> +{
> +	struct dw_mci_exynos_priv_data *priv = host->priv;
> +
> +	if (ios->timing == MMC_TIMING_UHS_DDR50)
> +		mci_writel(host, CLKSEL, priv->ddr_timing);
> +	else
> +		mci_writel(host, CLKSEL, priv->sdr_timing);
> +}
> +
> +static int dw_mci_exynos_parse_dt(struct dw_mci *host)
> +{
> +	struct dw_mci_exynos_priv_data *priv = host->priv;
> +	struct device_node *np = host->dev->of_node;
> +	u32 timing[3];
u32 timing[2] is enough.

Thanks,
Seungwon Jeon

> +	u32 div = 0;
> +	int ret;
> +
> +	of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
> +	priv->ciu_div = div;
> +
> +	ret = of_property_read_u32_array(np,
> +			"samsung,dw-mshc-sdr-timing", timing, 2);
> +	if (ret)
> +		return ret;
> +
> +	priv->sdr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
> +
> +	ret = of_property_read_u32_array(np,
> +			"samsung,dw-mshc-ddr-timing", timing, 2);
> +	if (ret)
> +		return ret;
> +
> +	priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
> +	return 0;
> +}
> +
> +static int dw_mci_exynos_setup_bus(struct dw_mci *host,
> +				struct device_node *slot_np, u8 bus_width)
> +{
> +	int idx, gpio, ret;
> +
> +	if (!slot_np)
> +		return -EINVAL;
> +
> +	/* cmd + clock + bus-width pins */
> +	for (idx = 0; idx < NUM_PINS(bus_width); idx++) {
> +		gpio = of_get_gpio(slot_np, idx);
> +		if (!gpio_is_valid(gpio)) {
> +			dev_err(host->dev, "invalid gpio: %d\n", gpio);
> +			return -EINVAL;
> +		}
> +
> +		ret = devm_gpio_request(host->dev, gpio, "dw-mci-bus");
> +		if (ret) {
> +			dev_err(host->dev, "gpio [%d] request failed\n", gpio);
> +			return -EBUSY;
> +		}
> +	}
> +
> +	gpio = of_get_named_gpio(slot_np, "wp-gpios", 0);
> +	if (gpio_is_valid(gpio)) {
> +		if (devm_gpio_request(host->dev, gpio, "dw-mci-wp"))
> +			dev_info(host->dev, "gpio [%d] request failed\n",
> +						gpio);
> +	} else {
> +		dev_info(host->dev, "wp gpio not available");
> +		host->pdata->quirks |= DW_MCI_QUIRK_NO_WRITE_PROTECT;
> +	}
> +
> +	if (host->pdata->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)
> +		return 0;
> +
> +	gpio = of_get_named_gpio(slot_np, "samsung,cd-pinmux-gpio", 0);
> +	if (gpio_is_valid(gpio)) {
> +		if (devm_gpio_request(host->dev, gpio, "dw-mci-cd"))
> +			dev_err(host->dev, "gpio [%d] request failed\n", gpio);
> +	} else {
> +		dev_info(host->dev, "cd gpio not available");
> +	}
> +
> +	return 0;
> +}
> +
> +/* Exynos5250 controller specific capabilities */
> +static unsigned long exynos5250_dwmmc_caps[4] = {
> +	MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR |
> +		MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23,
> +	MMC_CAP_CMD23,
> +	MMC_CAP_CMD23,
> +	MMC_CAP_CMD23,
> +};
> +
> +static struct dw_mci_drv_data exynos5250_drv_data = {
> +	.caps			= exynos5250_dwmmc_caps,
> +	.init			= dw_mci_exynos_priv_init,
> +	.setup_clock		= dw_mci_exynos_setup_clock,
> +	.prepare_command	= dw_mci_exynos_prepare_command,
> +	.set_ios		= dw_mci_exynos_set_ios,
> +	.parse_dt		= dw_mci_exynos_parse_dt,
> +	.setup_bus		= dw_mci_exynos_setup_bus,
> +};
> +
> +static const struct of_device_id dw_mci_exynos_match[] = {
> +	{ .compatible = "samsung,exynos5250-dw-mshc",
> +			.data = (void *)&exynos5250_drv_data, },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
> +
> +int dw_mci_exynos_probe(struct platform_device *pdev)
> +{
> +	struct dw_mci_drv_data *drv_data;
> +	const struct of_device_id *match;
> +
> +	match = of_match_node(dw_mci_exynos_match, pdev->dev.of_node);
> +	drv_data = match->data;
> +	return dw_mci_pltfm_register(pdev, drv_data);
> +}
> +
> +static struct platform_driver dw_mci_exynos_pltfm_driver = {
> +	.probe		= dw_mci_exynos_probe,
> +	.remove		= __exit_p(dw_mci_pltfm_remove),
> +	.driver		= {
> +		.name		= "dwmmc_exynos",
> +		.of_match_table	= of_match_ptr(dw_mci_exynos_match),
> +		.pm		= &dw_mci_pltfm_pmops,
> +	},
> +};
> +
> +module_platform_driver(dw_mci_exynos_pltfm_driver);
> +
> +MODULE_DESCRIPTION("Samsung Specific DW-MSHC Driver Extension");
> +MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:dwmmc-exynos");
> --
> 1.6.6.rc2
> 
> --
> 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

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v5 4/9] mmc: dw_mmc: lookup for optional biu and ciu clocks
  2012-09-05  8:30                 ` Jaehoon Chung
@ 2012-09-07  6:26                   ` Thomas Abraham
  2012-09-07  9:39                     ` Thomas Abraham
  0 siblings, 1 reply; 29+ messages in thread
From: Thomas Abraham @ 2012-09-07  6:26 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: linux-mmc, devicetree-discuss, will.newton, cjb, grant.likely,
	rob.herring, linux-samsung-soc, kgene.kim, girish.shivananjappa,
	tgih.jun, patches

Hi Jaehoon,

Thanks for reviewing the patch.

On 5 September 2012 14:00, Jaehoon Chung <jh80.chung@samsung.com> wrote:
> On 09/05/2012 04:46 AM, Thomas Abraham wrote:
>> Some platforms allow for clock gating and control of bus interface unit clock
>> and card interface unit clock. Add support for clock lookup of optional biu
>> and ciu clocks for clock gating and clock speed determination.
>>
>> Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
>> Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
>> Acked-by: Will Newton <will.newton@imgtec.com>
>> ---
>>  drivers/mmc/host/dw_mmc.c  |   50 +++++++++++++++++++++++++++++++++++++++++--
>>  include/linux/mmc/dw_mmc.h |    4 +++
>>  2 files changed, 51 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
>> index 227c42e..e8c8491 100644
>> --- a/drivers/mmc/host/dw_mmc.c
>> +++ b/drivers/mmc/host/dw_mmc.c
>> @@ -1960,13 +1960,40 @@ int dw_mci_probe(struct dw_mci *host)
>>               return -ENODEV;
>>       }
>>
>> -     if (!host->pdata->bus_hz) {
>> +     host->biu_clk = clk_get(host->dev, "biu");
>> +     if (IS_ERR(host->biu_clk)) {
>> +             dev_dbg(host->dev, "biu clock not available\n");
>> +     } else {
>> +             ret = clk_prepare_enable(host->biu_clk);
>> +             if (ret) {
>> +                     dev_err(host->dev, "failed to enable biu clock\n");
>> +                     return ret;
> didn't clk_put() for biu_clk?

Yes, I missed that. Thanks for pointing this out. I will fix this.

>> +             }
>> +     }
>> +
>> +     host->ciu_clk = clk_get(host->dev, "ciu");
>> +     if (IS_ERR(host->ciu_clk)) {
>> +             dev_dbg(host->dev, "ciu clock not available\n");
>> +     } else {
>> +             ret = clk_prepare_enable(host->ciu_clk);
>> +             if (ret) {
>> +                     dev_err(host->dev, "failed to enable ciu clock\n");
>> +                     goto err_clk_biu;
>> +             }
>> +     }
>> +
>> +     if (IS_ERR(host->ciu_clk))
>> +             host->bus_hz = host->pdata->bus_hz;
>> +     else
>> +             host->bus_hz = clk_get_rate(host->ciu_clk);
>> +
>> +     if (!host->bus_hz) {
>>               dev_err(host->dev,
>>                       "Platform data must supply bus speed\n");
>> -             return -ENODEV;
>> +             ret = -ENODEV;
>> +             goto err_clk_ciu;
>>       }
>>
>> -     host->bus_hz = host->pdata->bus_hz;
>>       host->quirks = host->pdata->quirks;
>>
>>       spin_lock_init(&host->lock);
>> @@ -2116,6 +2143,17 @@ err_dmaunmap:
>>               regulator_disable(host->vmmc);
>>               regulator_put(host->vmmc);
>>       }
>> +
>> +err_clk_ciu:
>> +     if (!IS_ERR(host->ciu_clk)) {
>> +             clk_disable_unprepare(host->ciu_clk);
>> +             clk_put(host->ciu_clk);
>> +     }
>> +err_clk_biu:
> I think right that is located the clk_put(host->ciu_clk) at here

In the next version of this patch, if clk_prepare_enable() fails for
host->ciu_clk, the put(host->ciu_clk) is also done before returning.

Thanks,
Thomas.

>> +     if (!IS_ERR(host->biu_clk)) {
>> +             clk_disable_unprepare(host->biu_clk);
>> +             clk_put(host->biu_clk);
>> +     }
>>       return ret;
>>  }
>>  EXPORT_SYMBOL(dw_mci_probe);
>> @@ -2149,6 +2187,12 @@ void dw_mci_remove(struct dw_mci *host)
>>               regulator_put(host->vmmc);
>>       }
>>
>> +     if (!IS_ERR(host->ciu_clk))
>> +             clk_disable_unprepare(host->ciu_clk);
>> +     if (!IS_ERR(host->biu_clk))
>> +             clk_disable_unprepare(host->biu_clk);
>> +     clk_put(host->ciu_clk);
>> +     clk_put(host->biu_clk);
>>  }
>>  EXPORT_SYMBOL(dw_mci_remove);
>>
>> diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
>> index a37a573..787ad56 100644
>> --- a/include/linux/mmc/dw_mmc.h
>> +++ b/include/linux/mmc/dw_mmc.h
>> @@ -78,6 +78,8 @@ struct mmc_data;
>>   * @data_offset: Set the offset of DATA register according to VERID.
>>   * @dev: Device associated with the MMC controller.
>>   * @pdata: Platform data associated with the MMC controller.
>> + * @biu_clk: Pointer to bus interface unit clock instance.
>> + * @ciu_clk: Pointer to card interface unit clock instance.
>>   * @slot: Slots sharing this MMC controller.
>>   * @fifo_depth: depth of FIFO.
>>   * @data_shift: log2 of FIFO item size.
>> @@ -158,6 +160,8 @@ struct dw_mci {
>>       u16                     data_offset;
>>       struct device           *dev;
>>       struct dw_mci_board     *pdata;
>> +     struct clk              *biu_clk;
>> +     struct clk              *ciu_clk;
>>       struct dw_mci_slot      *slot[MAX_MCI_SLOTS];
>>
>>       /* FIFO push and pull */
>>
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v5 9/9] mmc: dw_mmc: add support for exynos specific implementation of dw-mshc
  2012-09-05 10:43     ` Seungwon Jeon
@ 2012-09-07  7:15       ` Thomas Abraham
  2012-09-07  9:41         ` Thomas Abraham
  2012-09-07 10:51         ` Seungwon Jeon
  0 siblings, 2 replies; 29+ messages in thread
From: Thomas Abraham @ 2012-09-07  7:15 UTC (permalink / raw)
  To: Seungwon Jeon
  Cc: linux-mmc, devicetree-discuss, will.newton, cjb, grant.likely,
	rob.herring, linux-samsung-soc, kgene.kim, girish.shivananjappa,
	jh80.chung, patches

Hi Seungwon,

Thanks for reviewing the patch.

On 5 September 2012 16:13, Seungwon Jeon <tgih.jun@samsung.com> wrote:
> On Wednesday, September 05, 2012, Thomas Abraham <thomas.abraham@linaro.org> wrote:
> Version 6 is right?
>
>> Samsung Exynos SoC's extend the dw-mshc controller for additional clock and bus
>> control. Add support for these extensions and include provide device tree based
>> discovery suppory as well.
>>
>> Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
>> Acked-by: Will Newton <will.newton@imgtec.com>
>> ---
>>  .../devicetree/bindings/mmc/exynos-dw-mshc.txt     |   86 +++++++
>>  drivers/mmc/host/Kconfig                           |    9 +
>>  drivers/mmc/host/Makefile                          |    1 +
>>  drivers/mmc/host/dw_mmc-exynos.c                   |  253 ++++++++++++++++++++
>>  4 files changed, 349 insertions(+), 0 deletions(-)
>>  create mode 100644 Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
>>  create mode 100644 drivers/mmc/host/dw_mmc-exynos.c
>>
>> diff --git a/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
>> b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
>> new file mode 100644
>> index 0000000..323a891
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
>> @@ -0,0 +1,86 @@
>> +* Samsung Exynos specific extensions to the Synopsis Designware Mobile
>> +  Storage Host Controller
>> +
>> +The Synopsis designware mobile storage host controller is used to interface
>> +a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
>> +differences between the core Synopsis dw mshc controller properties described
>> +by synposis-dw-mshc.txt and the properties used by the Samsung Exynos specific
>> +extensions to the Synopsis Designware Mobile Storage Host Controller.
>> +
>> +Required Properties:
>> +
>> +* compatible: should be
>> +     - "samsung,exynos4210-dw-mshc": for controllers with Samsung Exynos4210
>> +       specific extentions.
>> +     - "samsung,exynos4412-dw-mshc": for controllers with Samsung Exynos4412
>> +       specific extentions.
>> +     - "samsung,exynos5250-dw-mshc": for controllers with Samsung Exynos5250
>> +       specific extentions.
>> +
>> +* samsung,dw-mshc-ciu-div: Specifies the divider value for the card interface
>> +  unit (ciu) clock. This property is applicable only for Exynos5 SoC's and
>> +  ignored for Exynos4 SoC's. The valid range of divider value is 0 to 7.
>> +
>> +* samsung,dw-mshc-sdr-timing: Specifies the value of CIU clock phase shift value
>> +  in transmit mode and CIU clock phase shift value in receive mode for single
>> +  data rate mode operation. Refer notes below for the order of the cells and the
>> +  valid values.
>> +
>> +* samsung,dw-mshc-ddr-timing: Specifies the value of CUI clock phase shift value
>> +  in transmit mode and CIU clock phase shift value in receive mode for double
>> +  data rate mode operation. Refer notes below for the order of the cells and the
>> +  valid values.
>> +
>> +  Notes for the sdr-timing and ddr-timing values:
>> +
>> +    The order of the cells should be
>> +      - First Cell: CIU clock phase shift value for tx mode.
>> +      - Second Cell: CIU clock phase shift value for rx mode.
>> +
>> +    Valid values for SDR and DDR CIU clock timing for Exynos5250:
>> +      - valid value for tx phase shift and rx phase shift is 0 to 7.
>> +      - when CIU clock divider value is set to 3, all possible 8 phase shift
>> +        values can be used.
>> +      - if CIU clock divider value is 0 (that is divide by 1), both tx and rx
>> +        phase shift clocks should be 0.
>> +
>> +Required properties for a slot:
>> +
>> +* gpios: specifies a list of gpios used for command, clock and data bus. The
>> +  first gpio is the command line and the second gpio is the clock line. The
>> +  rest of the gpios (depending on the bus-width property) are the data lines in
>> +  no particular order. The format of the gpio specifier depends on the gpio
>> +  controller.
>> +
>> +Example:
>> +
>> +  The MSHC controller node can be split into two portions, SoC specific and
>> +  board specific portions as listed below.
>> +
>> +     dwmmc0@12200000 {
>> +             compatible = "samsung,exynos5250-dw-mshc";
>> +             reg = <0x12200000 0x1000>;
>> +             interrupts = <0 75 0>;
>> +             #address-cells = <1>;
>> +             #size-cells = <0>;
>> +     };
>> +
>> +     dwmmc0@12200000 {
>> +             num-slots = <1>;
>> +             supports-highspeed;
>> +             broken-cd;
>> +             fifo-depth = <0x80>;
>> +             card-detect-delay = <200>;
>> +             samsung,dw-mshc-sdr-timing = <2 3 3>;
>> +             samsung,dw-mshc-ddr-timing = <1 2 3>;
> Third filed is still useful?

No, it is not used anymore. Thanks for pointing this out.

>
>
>> +
>> +             slot@0 {
>> +                     reg = <0>;
>> +                     bus-width = <8>;
>> +                     gpios = <&gpc0 0 2 0 3>, <&gpc0 1 2 0 3>,
>> +                             <&gpc1 0 2 3 3>, <&gpc1 1 2 3 3>,
>> +                             <&gpc1 2 2 3 3>, <&gpc1 3 2 3 3>,
>> +                             <&gpc0 3 2 3 3>, <&gpc0 4 2 3 3>,
>> +                             <&gpc0 5 2 3 3>, <&gpc0 6 2 3 3>;
>> +             };
>> +     };
>> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
>> index aa131b3..9bf10e7 100644
>> --- a/drivers/mmc/host/Kconfig
>> +++ b/drivers/mmc/host/Kconfig
>> @@ -540,6 +540,15 @@ config MMC_DW_PLTFM
>>
>>         If unsure, say Y.
>>
>> +config MMC_DW_EXYNOS
>> +     tristate "Exynos specific extentions for Synopsys DW Memory Card Interface"
>> +     depends on MMC_DW
>> +     select MMC_DW_PLTFM
>> +     help
>> +       This selects support for Samsung Exynos SoC specific extensions to the
>> +       Synopsys DesignWare Memory Card Interface driver. Select this option
>> +       for platforms based on Exynos4 and Exynos5 SoC's.
>> +
>>  config MMC_DW_PCI
>>       tristate "Synopsys Designware MCI support on PCI bus"
>>       depends on MMC_DW && PCI
>> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
>> index 8922b06..17ad0a7 100644
>> --- a/drivers/mmc/host/Makefile
>> +++ b/drivers/mmc/host/Makefile
>> @@ -39,6 +39,7 @@ obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o
>>  obj-$(CONFIG_SDH_BFIN)               += bfin_sdh.o
>>  obj-$(CONFIG_MMC_DW)         += dw_mmc.o
>>  obj-$(CONFIG_MMC_DW_PLTFM)   += dw_mmc-pltfm.o
>> +obj-$(CONFIG_MMC_DW_EXYNOS)  += dw_mmc-exynos.o
>>  obj-$(CONFIG_MMC_DW_PCI)     += dw_mmc-pci.o
>>  obj-$(CONFIG_MMC_SH_MMCIF)   += sh_mmcif.o
>>  obj-$(CONFIG_MMC_JZ4740)     += jz4740_mmc.o
>> diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c
>> new file mode 100644
>> index 0000000..313f364
>> --- /dev/null
>> +++ b/drivers/mmc/host/dw_mmc-exynos.c
>> @@ -0,0 +1,253 @@
>> +/*
>> + * Exynos Specific Extensions for Synopsys DW Multimedia Card Interface driver
>> + *
>> + * Copyright (C) 2012, Samsung Electronics Co., Ltd.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/clk.h>
>> +#include <linux/mmc/host.h>
>> +#include <linux/mmc/dw_mmc.h>
>> +#include <linux/of.h>
>> +#include <linux/of_gpio.h>
>> +
>> +#include "dw_mmc.h"
>> +#include "dw_mmc-pltfm.h"
>> +
>> +#define NUM_PINS(x)                  (x + 2)
>> +
>> +#define SDMMC_CLKSEL                 0x09C
>> +#define SDMMC_CLKSEL_CCLK_SAMPLE(x)  (((x) & 7) << 0)
>> +#define SDMMC_CLKSEL_CCLK_DRIVE(x)   (((x) & 7) << 16)
>> +#define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24)
>> +#define SDMMC_CLKSEL_GET_DRV_WD3(x)  (((x) >> 16) & 0x7)
>> +#define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) |  \
>> +                                     SDMMC_CLKSEL_CCLK_DRIVE(y) |    \
>> +                                     SDMMC_CLKSEL_CCLK_DIVIDER(z))
>> +
>> +#define SDMMC_CMD_USE_HOLD_REG               BIT(29)
>> +
>> +#define EXYNOS4210_FIXED_CIU_CLK_DIV 2
>> +#define EXYNOS4412_FIXED_CIU_CLK_DIV 4
>> +
>> +/* Variations in Exynos specific dw-mshc controller */
>> +enum dw_mci_exynos_type {
>> +     DW_MCI_TYPE_EXYNOS4210,
>> +     DW_MCI_TYPE_EXYNOS4412,
>> +     DW_MCI_TYPE_EXYNOS5250,
>> +};
>> +
>> +/* Exynos implementation specific driver private data */
>> +struct dw_mci_exynos_priv_data {
>> +     enum dw_mci_exynos_type         ctrl_type;
>> +     u8                              ciu_div;
>> +     u32                             sdr_timing;
>> +     u32                             ddr_timing;
>> +};
>> +
>> +static struct dw_mci_exynos_compatible {
>> +     char                            *compatible;
>> +     enum dw_mci_exynos_type         ctrl_type;
>> +} exynos_compat[] = {
>> +     {
>> +             .compatible     = "samsung,exynos4210-dw-mshc",
>> +             .ctrl_type      = DW_MCI_TYPE_EXYNOS4210,
>> +     }, {
>> +             .compatible     = "samsung,exynos4412-dw-mshc",
>> +             .ctrl_type      = DW_MCI_TYPE_EXYNOS4412,
>> +     }, {
>> +             .compatible     = "samsung,exynos5250-dw-mshc",
>> +             .ctrl_type      = DW_MCI_TYPE_EXYNOS5250,
>> +     },
>> +};
>> +
>> +static int dw_mci_exynos_priv_init(struct dw_mci *host)
>> +{
>> +     struct dw_mci_exynos_priv_data *priv;
>> +     int idx;
>> +
>> +     priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
>> +     if (!priv) {
>> +             dev_err(host->dev, "mem alloc failed for private data\n");
>> +             return -ENOMEM;
>> +     }
>> +
>> +     for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
>> +             if (of_device_is_compatible(host->dev->of_node,
>> +                                     exynos_compat[idx].compatible))
>> +                     priv->ctrl_type = exynos_compat[idx].ctrl_type;
>> +     }
>> +
>> +     host->priv = priv;
>> +     return 0;
>> +}
>> +
>> +static int dw_mci_exynos_setup_clock(struct dw_mci *host)
>> +{
>> +     struct dw_mci_exynos_priv_data *priv = host->priv;
>> +
> It assume that initial bus_hz is from clk_get_rate.
> Can it be ensured? If value of bus_hz is set from platform, calculation is wrong below.

In case of legacy (non-dt) platforms, if bus_hz is being specified
using platform_data, the clock rate of bus_hz should be set to the
clock rate at the cclk_in pad of the dwmmc controller. platform_data
need to calculate the internal clock speed of ciu_clk.

>
>> +     if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5250)
>> +             host->bus_hz /= (priv->ciu_div + 1);
>> +     else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
>> +             host->bus_hz /= EXYNOS4412_FIXED_CIU_CLK_DIV;
>> +     else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
>> +             host->bus_hz /= EXYNOS4210_FIXED_CIU_CLK_DIV;
>> +
>> +     return 0;
> Here,  returning is any meaning?
> There seems to be no error case.

The implementation specific callbacks have been designed to be generic
and usable on multiple platforms. The return value allows the
implementation specific callback to return any error code to the
caller.

>
>> +}
>> +
>> +static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
>> +{
>> +     /*
>> +      * Exynos4412 and Exynos5250 extends the use of CMD register with the
>> +      * use of bit 29 (which is reserved on standard MSHC controllers) for
>> +      * optionally bypassing the HOLD register for command and data. The
>> +      * HOLD register should be bypassed in case there is no phase shift
>> +      * applied on CMD/DATA that is sent to the card.
>> +      */
>> +     if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL)))
>> +             *cmdr |= SDMMC_CMD_USE_HOLD_REG;
>> +}
>> +
>> +static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
>> +{
>> +     struct dw_mci_exynos_priv_data *priv = host->priv;
>> +
>> +     if (ios->timing == MMC_TIMING_UHS_DDR50)
>> +             mci_writel(host, CLKSEL, priv->ddr_timing);
>> +     else
>> +             mci_writel(host, CLKSEL, priv->sdr_timing);
>> +}
>> +
>> +static int dw_mci_exynos_parse_dt(struct dw_mci *host)
>> +{
>> +     struct dw_mci_exynos_priv_data *priv = host->priv;
>> +     struct device_node *np = host->dev->of_node;
>> +     u32 timing[3];
> u32 timing[2] is enough.

Yes, that is right. I will change it.

Thanks,
Thomas.

>
> Thanks,
> Seungwon Jeon
>
>> +     u32 div = 0;
>> +     int ret;
>> +
>> +     of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
>> +     priv->ciu_div = div;
>> +
>> +     ret = of_property_read_u32_array(np,
>> +                     "samsung,dw-mshc-sdr-timing", timing, 2);
>> +     if (ret)
>> +             return ret;
>> +
>> +     priv->sdr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
>> +
>> +     ret = of_property_read_u32_array(np,
>> +                     "samsung,dw-mshc-ddr-timing", timing, 2);
>> +     if (ret)
>> +             return ret;
>> +
>> +     priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
>> +     return 0;
>> +}
>> +
>> +static int dw_mci_exynos_setup_bus(struct dw_mci *host,
>> +                             struct device_node *slot_np, u8 bus_width)
>> +{
>> +     int idx, gpio, ret;
>> +
>> +     if (!slot_np)
>> +             return -EINVAL;
>> +
>> +     /* cmd + clock + bus-width pins */
>> +     for (idx = 0; idx < NUM_PINS(bus_width); idx++) {
>> +             gpio = of_get_gpio(slot_np, idx);
>> +             if (!gpio_is_valid(gpio)) {
>> +                     dev_err(host->dev, "invalid gpio: %d\n", gpio);
>> +                     return -EINVAL;
>> +             }
>> +
>> +             ret = devm_gpio_request(host->dev, gpio, "dw-mci-bus");
>> +             if (ret) {
>> +                     dev_err(host->dev, "gpio [%d] request failed\n", gpio);
>> +                     return -EBUSY;
>> +             }
>> +     }
>> +
>> +     gpio = of_get_named_gpio(slot_np, "wp-gpios", 0);
>> +     if (gpio_is_valid(gpio)) {
>> +             if (devm_gpio_request(host->dev, gpio, "dw-mci-wp"))
>> +                     dev_info(host->dev, "gpio [%d] request failed\n",
>> +                                             gpio);
>> +     } else {
>> +             dev_info(host->dev, "wp gpio not available");
>> +             host->pdata->quirks |= DW_MCI_QUIRK_NO_WRITE_PROTECT;
>> +     }
>> +
>> +     if (host->pdata->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)
>> +             return 0;
>> +
>> +     gpio = of_get_named_gpio(slot_np, "samsung,cd-pinmux-gpio", 0);
>> +     if (gpio_is_valid(gpio)) {
>> +             if (devm_gpio_request(host->dev, gpio, "dw-mci-cd"))
>> +                     dev_err(host->dev, "gpio [%d] request failed\n", gpio);
>> +     } else {
>> +             dev_info(host->dev, "cd gpio not available");
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +/* Exynos5250 controller specific capabilities */
>> +static unsigned long exynos5250_dwmmc_caps[4] = {
>> +     MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR |
>> +             MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23,
>> +     MMC_CAP_CMD23,
>> +     MMC_CAP_CMD23,
>> +     MMC_CAP_CMD23,
>> +};
>> +
>> +static struct dw_mci_drv_data exynos5250_drv_data = {
>> +     .caps                   = exynos5250_dwmmc_caps,
>> +     .init                   = dw_mci_exynos_priv_init,
>> +     .setup_clock            = dw_mci_exynos_setup_clock,
>> +     .prepare_command        = dw_mci_exynos_prepare_command,
>> +     .set_ios                = dw_mci_exynos_set_ios,
>> +     .parse_dt               = dw_mci_exynos_parse_dt,
>> +     .setup_bus              = dw_mci_exynos_setup_bus,
>> +};
>> +
>> +static const struct of_device_id dw_mci_exynos_match[] = {
>> +     { .compatible = "samsung,exynos5250-dw-mshc",
>> +                     .data = (void *)&exynos5250_drv_data, },
>> +     {},
>> +};
>> +MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
>> +
>> +int dw_mci_exynos_probe(struct platform_device *pdev)
>> +{
>> +     struct dw_mci_drv_data *drv_data;
>> +     const struct of_device_id *match;
>> +
>> +     match = of_match_node(dw_mci_exynos_match, pdev->dev.of_node);
>> +     drv_data = match->data;
>> +     return dw_mci_pltfm_register(pdev, drv_data);
>> +}
>> +
>> +static struct platform_driver dw_mci_exynos_pltfm_driver = {
>> +     .probe          = dw_mci_exynos_probe,
>> +     .remove         = __exit_p(dw_mci_pltfm_remove),
>> +     .driver         = {
>> +             .name           = "dwmmc_exynos",
>> +             .of_match_table = of_match_ptr(dw_mci_exynos_match),
>> +             .pm             = &dw_mci_pltfm_pmops,
>> +     },
>> +};
>> +
>> +module_platform_driver(dw_mci_exynos_pltfm_driver);
>> +
>> +MODULE_DESCRIPTION("Samsung Specific DW-MSHC Driver Extension");
>> +MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_ALIAS("platform:dwmmc-exynos");
>> --
>> 1.6.6.rc2
>>
>> --
>> 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
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH v5 4/9] mmc: dw_mmc: lookup for optional biu and ciu clocks
  2012-09-07  6:26                   ` Thomas Abraham
@ 2012-09-07  9:39                     ` Thomas Abraham
  0 siblings, 0 replies; 29+ messages in thread
From: Thomas Abraham @ 2012-09-07  9:39 UTC (permalink / raw)
  To: linux-mmc, devicetree-discuss
  Cc: will.newton, cjb, grant.likely, rob.herring, linux-samsung-soc,
	kgene.kim, girish.shivananjappa, jh80.chung, tgih.jun, patches

Some platforms allow for clock gating and control of bus interface unit clock
and card interface unit clock. Add support for clock lookup of optional biu
and ciu clocks for clock gating and clock speed determination.

Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
Acked-by: Will Newton <will.newton@imgtec.com>
---
 drivers/mmc/host/dw_mmc.c  |   52 +++++++++++++++++++++++++++++++++++++++++--
 include/linux/mmc/dw_mmc.h |    4 +++
 2 files changed, 53 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 227c42e..de45ad2 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -1960,13 +1960,42 @@ int dw_mci_probe(struct dw_mci *host)
 		return -ENODEV;
 	}
 
-	if (!host->pdata->bus_hz) {
+	host->biu_clk = clk_get(host->dev, "biu");
+	if (IS_ERR(host->biu_clk)) {
+		dev_dbg(host->dev, "biu clock not available\n");
+	} else {
+		ret = clk_prepare_enable(host->biu_clk);
+		if (ret) {
+			dev_err(host->dev, "failed to enable biu clock\n");
+			clk_put(host->biu_clk);
+			return ret;
+		}
+	}
+
+	host->ciu_clk = clk_get(host->dev, "ciu");
+	if (IS_ERR(host->ciu_clk)) {
+		dev_dbg(host->dev, "ciu clock not available\n");
+	} else {
+		ret = clk_prepare_enable(host->ciu_clk);
+		if (ret) {
+			dev_err(host->dev, "failed to enable ciu clock\n");
+			clk_put(host->ciu_clk);
+			goto err_clk_biu;
+		}
+	}
+
+	if (IS_ERR(host->ciu_clk))
+		host->bus_hz = host->pdata->bus_hz;
+	else
+		host->bus_hz = clk_get_rate(host->ciu_clk);
+
+	if (!host->bus_hz) {
 		dev_err(host->dev,
 			"Platform data must supply bus speed\n");
-		return -ENODEV;
+		ret = -ENODEV;
+		goto err_clk_ciu;
 	}
 
-	host->bus_hz = host->pdata->bus_hz;
 	host->quirks = host->pdata->quirks;
 
 	spin_lock_init(&host->lock);
@@ -2116,6 +2145,17 @@ err_dmaunmap:
 		regulator_disable(host->vmmc);
 		regulator_put(host->vmmc);
 	}
+
+err_clk_ciu:
+	if (!IS_ERR(host->ciu_clk)) {
+		clk_disable_unprepare(host->ciu_clk);
+		clk_put(host->ciu_clk);
+	}
+err_clk_biu:
+	if (!IS_ERR(host->biu_clk)) {
+		clk_disable_unprepare(host->biu_clk);
+		clk_put(host->biu_clk);
+	}
 	return ret;
 }
 EXPORT_SYMBOL(dw_mci_probe);
@@ -2149,6 +2189,12 @@ void dw_mci_remove(struct dw_mci *host)
 		regulator_put(host->vmmc);
 	}
 
+	if (!IS_ERR(host->ciu_clk))
+		clk_disable_unprepare(host->ciu_clk);
+	if (!IS_ERR(host->biu_clk))
+		clk_disable_unprepare(host->biu_clk);
+	clk_put(host->ciu_clk);
+	clk_put(host->biu_clk);
 }
 EXPORT_SYMBOL(dw_mci_remove);
 
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
index a37a573..787ad56 100644
--- a/include/linux/mmc/dw_mmc.h
+++ b/include/linux/mmc/dw_mmc.h
@@ -78,6 +78,8 @@ struct mmc_data;
  * @data_offset: Set the offset of DATA register according to VERID.
  * @dev: Device associated with the MMC controller.
  * @pdata: Platform data associated with the MMC controller.
+ * @biu_clk: Pointer to bus interface unit clock instance.
+ * @ciu_clk: Pointer to card interface unit clock instance.
  * @slot: Slots sharing this MMC controller.
  * @fifo_depth: depth of FIFO.
  * @data_shift: log2 of FIFO item size.
@@ -158,6 +160,8 @@ struct dw_mci {
 	u16			data_offset;
 	struct device		*dev;
 	struct dw_mci_board	*pdata;
+	struct clk		*biu_clk;
+	struct clk		*ciu_clk;
 	struct dw_mci_slot	*slot[MAX_MCI_SLOTS];
 
 	/* FIFO push and pull */
-- 
1.6.6.rc2


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v5 8/9] mmc: dw_mmc: add support for implementation specific callbacks
  2012-08-29 10:48 ` [PATCH v5 8/9] mmc: dw_mmc: add support for implementation specific callbacks Thomas Abraham
  2012-09-04 19:47   ` Thomas Abraham
@ 2012-09-07  9:40   ` Thomas Abraham
  1 sibling, 0 replies; 29+ messages in thread
From: Thomas Abraham @ 2012-09-07  9:40 UTC (permalink / raw)
  To: linux-mmc, devicetree-discuss
  Cc: will.newton, cjb, grant.likely, rob.herring, linux-samsung-soc,
	kgene.kim, girish.shivananjappa, jh80.chung, tgih.jun, patches

The core dw-mshc controller driver can let platform specific implementations
of the dw-mshc controller to control the hardware as required by such
implementations. This is acheived by invoking implementation specific (optional)
callbacks. Define the list of callbacks supported the add invocation points
for the same.

Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
Acked-by: Will Newton <will.newton@imgtec.com>
---
 drivers/mmc/host/dw_mmc-pltfm.c |   12 ++++++++-
 drivers/mmc/host/dw_mmc-pltfm.h |    3 +-
 drivers/mmc/host/dw_mmc.c       |   47 ++++++++++++++++++++++++++++++++++++++-
 drivers/mmc/host/dw_mmc.h       |   24 ++++++++++++++++++++
 include/linux/mmc/dw_mmc.h      |    4 +++
 5 files changed, 86 insertions(+), 4 deletions(-)

diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index e17da91..c960ca7 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -23,7 +23,8 @@
 
 #include "dw_mmc.h"
 
-int dw_mci_pltfm_register(struct platform_device *pdev)
+int dw_mci_pltfm_register(struct platform_device *pdev,
+				struct dw_mci_drv_data *drv_data)
 {
 	struct dw_mci *host;
 	struct resource	*regs;
@@ -41,6 +42,7 @@ int dw_mci_pltfm_register(struct platform_device *pdev)
 	if (host->irq < 0)
 		return host->irq;
 
+	host->drv_data = drv_data;
 	host->dev = &pdev->dev;
 	host->irq_flags = 0;
 	host->pdata = pdev->dev.platform_data;
@@ -48,6 +50,12 @@ int dw_mci_pltfm_register(struct platform_device *pdev)
 	if (!host->regs)
 		return -ENOMEM;
 
+	if (host->drv_data->init) {
+		ret = host->drv_data->init(host);
+		if (ret)
+			return ret;
+	}
+
 	platform_set_drvdata(pdev, host);
 	ret = dw_mci_probe(host);
 	return ret;
@@ -56,7 +64,7 @@ EXPORT_SYMBOL_GPL(dw_mci_pltfm_register);
 
 static int __devinit dw_mci_pltfm_probe(struct platform_device *pdev)
 {
-	return dw_mci_pltfm_register(pdev);
+	return dw_mci_pltfm_register(pdev, NULL);
 }
 
 static int __devexit dw_mci_pltfm_remove(struct platform_device *pdev)
diff --git a/drivers/mmc/host/dw_mmc-pltfm.h b/drivers/mmc/host/dw_mmc-pltfm.h
index 6c065d9..301f245 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.h
+++ b/drivers/mmc/host/dw_mmc-pltfm.h
@@ -12,7 +12,8 @@
 #ifndef _DW_MMC_PLTFM_H_
 #define _DW_MMC_PLTFM_H_
 
-extern int dw_mci_pltfm_register(struct platform_device *pdev);
+extern int dw_mci_pltfm_register(struct platform_device *pdev,
+				struct dw_mci_drv_data *drv_data);
 extern int __devexit dw_mci_pltfm_remove(struct platform_device *pdev);
 extern const struct dev_pm_ops dw_mci_pltfm_pmops;
 
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index c792466..9f8e487 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -231,6 +231,7 @@ static void dw_mci_set_timeout(struct dw_mci *host)
 static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
 {
 	struct mmc_data	*data;
+	struct dw_mci_slot *slot = mmc_priv(mmc);
 	u32 cmdr;
 	cmd->error = -EINPROGRESS;
 
@@ -260,6 +261,9 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
 			cmdr |= SDMMC_CMD_DAT_WR;
 	}
 
+	if (slot->host->drv_data->prepare_command)
+		slot->host->drv_data->prepare_command(slot->host, &cmdr);
+
 	return cmdr;
 }
 
@@ -815,6 +819,9 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 		slot->clock = ios->clock;
 	}
 
+	if (slot->host->drv_data->set_ios)
+		slot->host->drv_data->set_ios(slot->host, ios);
+
 	switch (ios->power_mode) {
 	case MMC_POWER_UP:
 		set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
@@ -1820,6 +1827,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 {
 	struct mmc_host *mmc;
 	struct dw_mci_slot *slot;
+	int ctrl_id, ret;
 	u8 bus_width;
 
 	mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
@@ -1851,6 +1859,16 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 	if (host->pdata->caps)
 		mmc->caps = host->pdata->caps;
 
+	if (host->dev->of_node) {
+		ctrl_id = of_alias_get_id(host->dev->of_node, "mshc");
+		if (ctrl_id < 0)
+			ctrl_id = 0;
+	} else {
+		ctrl_id = to_platform_device(host->dev)->id;
+	}
+	if (host->drv_data && host->drv_data->caps)
+		mmc->caps |= host->drv_data->caps[ctrl_id];
+
 	if (host->pdata->caps2)
 		mmc->caps2 = host->pdata->caps2;
 
@@ -1861,6 +1879,14 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 	else
 		bus_width = 1;
 
+	if (host->drv_data->setup_bus) {
+		struct device_node *slot_np;
+		slot_np = dw_mci_of_find_slot_node(host->dev, slot->id);
+		ret = host->drv_data->setup_bus(host, slot_np, bus_width);
+		if (ret)
+			goto err_setup_bus;
+	}
+
 	switch (bus_width) {
 	case 8:
 		mmc->caps |= MMC_CAP_8_BIT_DATA;
@@ -1927,6 +1953,10 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 	queue_work(host->card_workqueue, &host->card_work);
 
 	return 0;
+
+err_setup_bus:
+	mmc_free_host(mmc);
+	return -EINVAL;
 }
 
 static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id)
@@ -2021,7 +2051,7 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
 	struct dw_mci_board *pdata;
 	struct device *dev = host->dev;
 	struct device_node *np = dev->of_node;
-	int idx;
+	int idx, ret;
 
 	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
 	if (!pdata) {
@@ -2048,6 +2078,12 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
 
 	of_property_read_u32(np, "card-detect-delay", &pdata->detect_delay_ms);
 
+	if (host->drv_data->parse_dt) {
+		ret = host->drv_data->parse_dt(host);
+		if (ret)
+			return ERR_PTR(ret);
+	}
+
 	return pdata;
 }
 
@@ -2107,6 +2143,15 @@ int dw_mci_probe(struct dw_mci *host)
 	else
 		host->bus_hz = clk_get_rate(host->ciu_clk);
 
+	if (host->drv_data->setup_clock) {
+		ret = host->drv_data->setup_clock(host);
+		if (ret) {
+			dev_err(host->dev,
+				"implementation specific clock setup failed\n");
+			goto err_clk_ciu;
+		}
+	}
+
 	if (!host->bus_hz) {
 		dev_err(host->dev,
 			"Platform data must supply bus speed\n");
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 15c27e1..53b8fd9 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -182,4 +182,28 @@ extern int dw_mci_suspend(struct dw_mci *host);
 extern int dw_mci_resume(struct dw_mci *host);
 #endif
 
+/**
+ * dw_mci driver data - dw-mshc implementation specific driver data.
+ * @caps: mmc subsystem specified capabilities of the controller(s).
+ * @init: early implementation specific initialization.
+ * @setup_clock: implementation specific clock configuration.
+ * @prepare_command: handle CMD register extensions.
+ * @set_ios: handle bus specific extensions.
+ * @parse_dt: parse implementation specific device tree properties.
+ * @setup_bus: initialize io-interface
+ *
+ * Provide controller implementation specific extensions. The usage of this
+ * data structure is fully optional and usage of each member in this structure
+ * is optional as well.
+ */
+struct dw_mci_drv_data {
+	unsigned long	*caps;
+	int		(*init)(struct dw_mci *host);
+	int		(*setup_clock)(struct dw_mci *host);
+	void		(*prepare_command)(struct dw_mci *host, u32 *cmdr);
+	void		(*set_ios)(struct dw_mci *host, struct mmc_ios *ios);
+	int		(*parse_dt)(struct dw_mci *host);
+	int		(*setup_bus)(struct dw_mci *host,
+				struct device_node *slot_np, u8 bus_width);
+};
 #endif /* _DW_MMC_H_ */
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
index b72e4aa..6cb043e 100644
--- a/include/linux/mmc/dw_mmc.h
+++ b/include/linux/mmc/dw_mmc.h
@@ -78,6 +78,8 @@ struct mmc_data;
  * @data_offset: Set the offset of DATA register according to VERID.
  * @dev: Device associated with the MMC controller.
  * @pdata: Platform data associated with the MMC controller.
+ * @drv_data: Driver specific data for identified variant of the controller
+ * @priv: Implementation defined private data.
  * @biu_clk: Pointer to bus interface unit clock instance.
  * @ciu_clk: Pointer to card interface unit clock instance.
  * @slot: Slots sharing this MMC controller.
@@ -160,6 +162,8 @@ struct dw_mci {
 	u16			data_offset;
 	struct device		*dev;
 	struct dw_mci_board	*pdata;
+	struct dw_mci_drv_data	*drv_data;
+	void			*priv;
 	struct clk		*biu_clk;
 	struct clk		*ciu_clk;
 	struct dw_mci_slot	*slot[MAX_MCI_SLOTS];
-- 
1.6.6.rc2

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v5 9/9] mmc: dw_mmc: add support for exynos specific implementation of dw-mshc
  2012-09-07  7:15       ` Thomas Abraham
@ 2012-09-07  9:41         ` Thomas Abraham
  2012-09-07 10:51         ` Seungwon Jeon
  1 sibling, 0 replies; 29+ messages in thread
From: Thomas Abraham @ 2012-09-07  9:41 UTC (permalink / raw)
  To: linux-mmc, devicetree-discuss
  Cc: will.newton, cjb, grant.likely, rob.herring, linux-samsung-soc,
	kgene.kim, girish.shivananjappa, jh80.chung, tgih.jun, patches

Samsung Exynos SoC's extend the dw-mshc controller for additional clock and bus
control. Add support for these extensions and include provide device tree based
discovery suppory as well.

Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
Acked-by: Will Newton <will.newton@imgtec.com>
---
 .../devicetree/bindings/mmc/exynos-dw-mshc.txt     |   87 +++++++
 drivers/mmc/host/Kconfig                           |    9 +
 drivers/mmc/host/Makefile                          |    1 +
 drivers/mmc/host/dw_mmc-exynos.c                   |  253 ++++++++++++++++++++
 4 files changed, 350 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
 create mode 100644 drivers/mmc/host/dw_mmc-exynos.c

diff --git a/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
new file mode 100644
index 0000000..7927689
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
@@ -0,0 +1,87 @@
+* Samsung Exynos specific extensions to the Synopsis Designware Mobile
+  Storage Host Controller
+
+The Synopsis designware mobile storage host controller is used to interface
+a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
+differences between the core Synopsis dw mshc controller properties described
+by synposis-dw-mshc.txt and the properties used by the Samsung Exynos specific
+extensions to the Synopsis Designware Mobile Storage Host Controller.
+
+Required Properties:
+
+* compatible: should be
+	- "samsung,exynos4210-dw-mshc": for controllers with Samsung Exynos4210
+	  specific extentions.
+	- "samsung,exynos4412-dw-mshc": for controllers with Samsung Exynos4412
+	  specific extentions.
+	- "samsung,exynos5250-dw-mshc": for controllers with Samsung Exynos5250
+	  specific extentions.
+
+* samsung,dw-mshc-ciu-div: Specifies the divider value for the card interface
+  unit (ciu) clock. This property is applicable only for Exynos5 SoC's and
+  ignored for Exynos4 SoC's. The valid range of divider value is 0 to 7.
+
+* samsung,dw-mshc-sdr-timing: Specifies the value of CIU clock phase shift value
+  in transmit mode and CIU clock phase shift value in receive mode for single
+  data rate mode operation. Refer notes below for the order of the cells and the
+  valid values.
+
+* samsung,dw-mshc-ddr-timing: Specifies the value of CUI clock phase shift value
+  in transmit mode and CIU clock phase shift value in receive mode for double
+  data rate mode operation. Refer notes below for the order of the cells and the
+  valid values.
+
+  Notes for the sdr-timing and ddr-timing values:
+
+    The order of the cells should be
+      - First Cell: CIU clock phase shift value for tx mode.
+      - Second Cell: CIU clock phase shift value for rx mode.
+
+    Valid values for SDR and DDR CIU clock timing for Exynos5250:
+      - valid value for tx phase shift and rx phase shift is 0 to 7.
+      - when CIU clock divider value is set to 3, all possible 8 phase shift
+        values can be used.
+      - if CIU clock divider value is 0 (that is divide by 1), both tx and rx
+        phase shift clocks should be 0.
+
+Required properties for a slot:
+
+* gpios: specifies a list of gpios used for command, clock and data bus. The
+  first gpio is the command line and the second gpio is the clock line. The
+  rest of the gpios (depending on the bus-width property) are the data lines in
+  no particular order. The format of the gpio specifier depends on the gpio
+  controller.
+
+Example:
+
+  The MSHC controller node can be split into two portions, SoC specific and
+  board specific portions as listed below.
+
+	dwmmc0@12200000 {
+		compatible = "samsung,exynos5250-dw-mshc";
+		reg = <0x12200000 0x1000>;
+		interrupts = <0 75 0>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+	};
+
+	dwmmc0@12200000 {
+		num-slots = <1>;
+		supports-highspeed;
+		broken-cd;
+		fifo-depth = <0x80>;
+		card-detect-delay = <200>;
+		samsung,dw-mshc-ciu-div = <3>;
+		samsung,dw-mshc-sdr-timing = <2 3>;
+		samsung,dw-mshc-ddr-timing = <1 2>;
+
+		slot@0 {
+			reg = <0>;
+			bus-width = <8>;
+			gpios = <&gpc0 0 2 0 3>, <&gpc0 1 2 0 3>,
+				<&gpc1 0 2 3 3>, <&gpc1 1 2 3 3>,
+				<&gpc1 2 2 3 3>, <&gpc1 3 2 3 3>,
+				<&gpc0 3 2 3 3>, <&gpc0 4 2 3 3>,
+				<&gpc0 5 2 3 3>, <&gpc0 6 2 3 3>;
+		};
+	};
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index aa131b3..9bf10e7 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -540,6 +540,15 @@ config MMC_DW_PLTFM
 
 	  If unsure, say Y.
 
+config MMC_DW_EXYNOS
+	tristate "Exynos specific extentions for Synopsys DW Memory Card Interface"
+	depends on MMC_DW
+	select MMC_DW_PLTFM
+	help
+	  This selects support for Samsung Exynos SoC specific extensions to the
+	  Synopsys DesignWare Memory Card Interface driver. Select this option
+	  for platforms based on Exynos4 and Exynos5 SoC's.
+
 config MMC_DW_PCI
 	tristate "Synopsys Designware MCI support on PCI bus"
 	depends on MMC_DW && PCI
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 8922b06..17ad0a7 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_MMC_VIA_SDMMC)	+= via-sdmmc.o
 obj-$(CONFIG_SDH_BFIN)		+= bfin_sdh.o
 obj-$(CONFIG_MMC_DW)		+= dw_mmc.o
 obj-$(CONFIG_MMC_DW_PLTFM)	+= dw_mmc-pltfm.o
+obj-$(CONFIG_MMC_DW_EXYNOS)	+= dw_mmc-exynos.o
 obj-$(CONFIG_MMC_DW_PCI)	+= dw_mmc-pci.o
 obj-$(CONFIG_MMC_SH_MMCIF)	+= sh_mmcif.o
 obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c
new file mode 100644
index 0000000..660bbc5
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc-exynos.c
@@ -0,0 +1,253 @@
+/*
+ * Exynos Specific Extensions for Synopsys DW Multimedia Card Interface driver
+ *
+ * Copyright (C) 2012, Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/dw_mmc.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+
+#include "dw_mmc.h"
+#include "dw_mmc-pltfm.h"
+
+#define NUM_PINS(x)			(x + 2)
+
+#define SDMMC_CLKSEL			0x09C
+#define SDMMC_CLKSEL_CCLK_SAMPLE(x)	(((x) & 7) << 0)
+#define SDMMC_CLKSEL_CCLK_DRIVE(x)	(((x) & 7) << 16)
+#define SDMMC_CLKSEL_CCLK_DIVIDER(x)	(((x) & 7) << 24)
+#define SDMMC_CLKSEL_GET_DRV_WD3(x)	(((x) >> 16) & 0x7)
+#define SDMMC_CLKSEL_TIMING(x, y, z)	(SDMMC_CLKSEL_CCLK_SAMPLE(x) |	\
+					SDMMC_CLKSEL_CCLK_DRIVE(y) |	\
+					SDMMC_CLKSEL_CCLK_DIVIDER(z))
+
+#define SDMMC_CMD_USE_HOLD_REG		BIT(29)
+
+#define EXYNOS4210_FIXED_CIU_CLK_DIV	2
+#define EXYNOS4412_FIXED_CIU_CLK_DIV	4
+
+/* Variations in Exynos specific dw-mshc controller */
+enum dw_mci_exynos_type {
+	DW_MCI_TYPE_EXYNOS4210,
+	DW_MCI_TYPE_EXYNOS4412,
+	DW_MCI_TYPE_EXYNOS5250,
+};
+
+/* Exynos implementation specific driver private data */
+struct dw_mci_exynos_priv_data {
+	enum dw_mci_exynos_type		ctrl_type;
+	u8				ciu_div;
+	u32				sdr_timing;
+	u32				ddr_timing;
+};
+
+static struct dw_mci_exynos_compatible {
+	char				*compatible;
+	enum dw_mci_exynos_type		ctrl_type;
+} exynos_compat[] = {
+	{
+		.compatible	= "samsung,exynos4210-dw-mshc",
+		.ctrl_type	= DW_MCI_TYPE_EXYNOS4210,
+	}, {
+		.compatible	= "samsung,exynos4412-dw-mshc",
+		.ctrl_type	= DW_MCI_TYPE_EXYNOS4412,
+	}, {
+		.compatible	= "samsung,exynos5250-dw-mshc",
+		.ctrl_type	= DW_MCI_TYPE_EXYNOS5250,
+	},
+};
+
+static int dw_mci_exynos_priv_init(struct dw_mci *host)
+{
+	struct dw_mci_exynos_priv_data *priv;
+	int idx;
+
+	priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		dev_err(host->dev, "mem alloc failed for private data\n");
+		return -ENOMEM;
+	}
+
+	for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
+		if (of_device_is_compatible(host->dev->of_node,
+					exynos_compat[idx].compatible))
+			priv->ctrl_type = exynos_compat[idx].ctrl_type;
+	}
+
+	host->priv = priv;
+	return 0;
+}
+
+static int dw_mci_exynos_setup_clock(struct dw_mci *host)
+{
+	struct dw_mci_exynos_priv_data *priv = host->priv;
+
+	if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5250)
+		host->bus_hz /= (priv->ciu_div + 1);
+	else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
+		host->bus_hz /= EXYNOS4412_FIXED_CIU_CLK_DIV;
+	else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
+		host->bus_hz /= EXYNOS4210_FIXED_CIU_CLK_DIV;
+
+	return 0;
+}
+
+static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
+{
+	/*
+	 * Exynos4412 and Exynos5250 extends the use of CMD register with the
+	 * use of bit 29 (which is reserved on standard MSHC controllers) for
+	 * optionally bypassing the HOLD register for command and data. The
+	 * HOLD register should be bypassed in case there is no phase shift
+	 * applied on CMD/DATA that is sent to the card.
+	 */
+	if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL)))
+		*cmdr |= SDMMC_CMD_USE_HOLD_REG;
+}
+
+static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
+{
+	struct dw_mci_exynos_priv_data *priv = host->priv;
+
+	if (ios->timing == MMC_TIMING_UHS_DDR50)
+		mci_writel(host, CLKSEL, priv->ddr_timing);
+	else
+		mci_writel(host, CLKSEL, priv->sdr_timing);
+}
+
+static int dw_mci_exynos_parse_dt(struct dw_mci *host)
+{
+	struct dw_mci_exynos_priv_data *priv = host->priv;
+	struct device_node *np = host->dev->of_node;
+	u32 timing[2];
+	u32 div = 0;
+	int ret;
+
+	of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
+	priv->ciu_div = div;
+
+	ret = of_property_read_u32_array(np,
+			"samsung,dw-mshc-sdr-timing", timing, 2);
+	if (ret)
+		return ret;
+
+	priv->sdr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
+
+	ret = of_property_read_u32_array(np,
+			"samsung,dw-mshc-ddr-timing", timing, 2);
+	if (ret)
+		return ret;
+
+	priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
+	return 0;
+}
+
+static int dw_mci_exynos_setup_bus(struct dw_mci *host,
+				struct device_node *slot_np, u8 bus_width)
+{
+	int idx, gpio, ret;
+
+	if (!slot_np)
+		return -EINVAL;
+
+	/* cmd + clock + bus-width pins */
+	for (idx = 0; idx < NUM_PINS(bus_width); idx++) {
+		gpio = of_get_gpio(slot_np, idx);
+		if (!gpio_is_valid(gpio)) {
+			dev_err(host->dev, "invalid gpio: %d\n", gpio);
+			return -EINVAL;
+		}
+
+		ret = devm_gpio_request(host->dev, gpio, "dw-mci-bus");
+		if (ret) {
+			dev_err(host->dev, "gpio [%d] request failed\n", gpio);
+			return -EBUSY;
+		}
+	}
+
+	gpio = of_get_named_gpio(slot_np, "wp-gpios", 0);
+	if (gpio_is_valid(gpio)) {
+		if (devm_gpio_request(host->dev, gpio, "dw-mci-wp"))
+			dev_info(host->dev, "gpio [%d] request failed\n",
+						gpio);
+	} else {
+		dev_info(host->dev, "wp gpio not available");
+		host->pdata->quirks |= DW_MCI_QUIRK_NO_WRITE_PROTECT;
+	}
+
+	if (host->pdata->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)
+		return 0;
+
+	gpio = of_get_named_gpio(slot_np, "samsung,cd-pinmux-gpio", 0);
+	if (gpio_is_valid(gpio)) {
+		if (devm_gpio_request(host->dev, gpio, "dw-mci-cd"))
+			dev_err(host->dev, "gpio [%d] request failed\n", gpio);
+	} else {
+		dev_info(host->dev, "cd gpio not available");
+	}
+
+	return 0;
+}
+
+/* Exynos5250 controller specific capabilities */
+static unsigned long exynos5250_dwmmc_caps[4] = {
+	MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR |
+		MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23,
+	MMC_CAP_CMD23,
+	MMC_CAP_CMD23,
+	MMC_CAP_CMD23,
+};
+
+static struct dw_mci_drv_data exynos5250_drv_data = {
+	.caps			= exynos5250_dwmmc_caps,
+	.init			= dw_mci_exynos_priv_init,
+	.setup_clock		= dw_mci_exynos_setup_clock,
+	.prepare_command	= dw_mci_exynos_prepare_command,
+	.set_ios		= dw_mci_exynos_set_ios,
+	.parse_dt		= dw_mci_exynos_parse_dt,
+	.setup_bus		= dw_mci_exynos_setup_bus,
+};
+
+static const struct of_device_id dw_mci_exynos_match[] = {
+	{ .compatible = "samsung,exynos5250-dw-mshc",
+			.data = (void *)&exynos5250_drv_data, },
+	{},
+};
+MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
+
+int dw_mci_exynos_probe(struct platform_device *pdev)
+{
+	struct dw_mci_drv_data *drv_data;
+	const struct of_device_id *match;
+
+	match = of_match_node(dw_mci_exynos_match, pdev->dev.of_node);
+	drv_data = match->data;
+	return dw_mci_pltfm_register(pdev, drv_data);
+}
+
+static struct platform_driver dw_mci_exynos_pltfm_driver = {
+	.probe		= dw_mci_exynos_probe,
+	.remove		= __exit_p(dw_mci_pltfm_remove),
+	.driver		= {
+		.name		= "dwmmc_exynos",
+		.of_match_table	= of_match_ptr(dw_mci_exynos_match),
+		.pm		= &dw_mci_pltfm_pmops,
+	},
+};
+
+module_platform_driver(dw_mci_exynos_pltfm_driver);
+
+MODULE_DESCRIPTION("Samsung Specific DW-MSHC Driver Extension");
+MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dwmmc-exynos");
-- 
1.6.6.rc2

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* RE: [PATCH v5 9/9] mmc: dw_mmc: add support for exynos specific implementation of dw-mshc
  2012-09-07  7:15       ` Thomas Abraham
  2012-09-07  9:41         ` Thomas Abraham
@ 2012-09-07 10:51         ` Seungwon Jeon
       [not found]           ` <00a601cd8ce6$ca599e50$5f0cdaf0$%jun-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
  1 sibling, 1 reply; 29+ messages in thread
From: Seungwon Jeon @ 2012-09-07 10:51 UTC (permalink / raw)
  To: 'Thomas Abraham'
  Cc: linux-mmc, devicetree-discuss, will.newton, cjb, grant.likely,
	rob.herring, linux-samsung-soc, kgene.kim, girish.shivananjappa,
	jh80.chung, patches

On Friday, September 07, 2012, Thomas Abraham <thomas.abraham@linaro.org> wrote:
> Hi Seungwon,
> 
> Thanks for reviewing the patch.
> 
> On 5 September 2012 16:13, Seungwon Jeon <tgih.jun@samsung.com> wrote:
> > On Wednesday, September 05, 2012, Thomas Abraham <thomas.abraham@linaro.org> wrote:
> > Version 6 is right?
> >
> >> Samsung Exynos SoC's extend the dw-mshc controller for additional clock and bus
> >> control. Add support for these extensions and include provide device tree based
> >> discovery suppory as well.
> >>
> >> Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
> >> Acked-by: Will Newton <will.newton@imgtec.com>
> >> ---
> >>  .../devicetree/bindings/mmc/exynos-dw-mshc.txt     |   86 +++++++
> >>  drivers/mmc/host/Kconfig                           |    9 +
> >>  drivers/mmc/host/Makefile                          |    1 +
> >>  drivers/mmc/host/dw_mmc-exynos.c                   |  253 ++++++++++++++++++++
> >>  4 files changed, 349 insertions(+), 0 deletions(-)
> >>  create mode 100644 Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
> >>  create mode 100644 drivers/mmc/host/dw_mmc-exynos.c
> >>
> >> diff --git a/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
> >> b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
> >> new file mode 100644
> >> index 0000000..323a891
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
> >> @@ -0,0 +1,86 @@
> >> +* Samsung Exynos specific extensions to the Synopsis Designware Mobile
> >> +  Storage Host Controller
> >> +
> >> +The Synopsis designware mobile storage host controller is used to interface
> >> +a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
> >> +differences between the core Synopsis dw mshc controller properties described
> >> +by synposis-dw-mshc.txt and the properties used by the Samsung Exynos specific
> >> +extensions to the Synopsis Designware Mobile Storage Host Controller.
> >> +
> >> +Required Properties:
> >> +
> >> +* compatible: should be
> >> +     - "samsung,exynos4210-dw-mshc": for controllers with Samsung Exynos4210
> >> +       specific extentions.
> >> +     - "samsung,exynos4412-dw-mshc": for controllers with Samsung Exynos4412
> >> +       specific extentions.
> >> +     - "samsung,exynos5250-dw-mshc": for controllers with Samsung Exynos5250
> >> +       specific extentions.
> >> +
> >> +* samsung,dw-mshc-ciu-div: Specifies the divider value for the card interface
> >> +  unit (ciu) clock. This property is applicable only for Exynos5 SoC's and
> >> +  ignored for Exynos4 SoC's. The valid range of divider value is 0 to 7.
> >> +
> >> +* samsung,dw-mshc-sdr-timing: Specifies the value of CIU clock phase shift value
> >> +  in transmit mode and CIU clock phase shift value in receive mode for single
> >> +  data rate mode operation. Refer notes below for the order of the cells and the
> >> +  valid values.
> >> +
> >> +* samsung,dw-mshc-ddr-timing: Specifies the value of CUI clock phase shift value
> >> +  in transmit mode and CIU clock phase shift value in receive mode for double
> >> +  data rate mode operation. Refer notes below for the order of the cells and the
> >> +  valid values.
> >> +
> >> +  Notes for the sdr-timing and ddr-timing values:
> >> +
> >> +    The order of the cells should be
> >> +      - First Cell: CIU clock phase shift value for tx mode.
> >> +      - Second Cell: CIU clock phase shift value for rx mode.
> >> +
> >> +    Valid values for SDR and DDR CIU clock timing for Exynos5250:
> >> +      - valid value for tx phase shift and rx phase shift is 0 to 7.
> >> +      - when CIU clock divider value is set to 3, all possible 8 phase shift
> >> +        values can be used.
> >> +      - if CIU clock divider value is 0 (that is divide by 1), both tx and rx
> >> +        phase shift clocks should be 0.
> >> +
> >> +Required properties for a slot:
> >> +
> >> +* gpios: specifies a list of gpios used for command, clock and data bus. The
> >> +  first gpio is the command line and the second gpio is the clock line. The
> >> +  rest of the gpios (depending on the bus-width property) are the data lines in
> >> +  no particular order. The format of the gpio specifier depends on the gpio
> >> +  controller.
> >> +
> >> +Example:
> >> +
> >> +  The MSHC controller node can be split into two portions, SoC specific and
> >> +  board specific portions as listed below.
> >> +
> >> +     dwmmc0@12200000 {
> >> +             compatible = "samsung,exynos5250-dw-mshc";
> >> +             reg = <0x12200000 0x1000>;
> >> +             interrupts = <0 75 0>;
> >> +             #address-cells = <1>;
> >> +             #size-cells = <0>;
> >> +     };
> >> +
> >> +     dwmmc0@12200000 {
> >> +             num-slots = <1>;
> >> +             supports-highspeed;
> >> +             broken-cd;
> >> +             fifo-depth = <0x80>;
> >> +             card-detect-delay = <200>;
> >> +             samsung,dw-mshc-sdr-timing = <2 3 3>;
> >> +             samsung,dw-mshc-ddr-timing = <1 2 3>;
> > Third filed is still useful?
> 
> No, it is not used anymore. Thanks for pointing this out.
> 
> >
> >
> >> +
> >> +             slot@0 {
> >> +                     reg = <0>;
> >> +                     bus-width = <8>;
> >> +                     gpios = <&gpc0 0 2 0 3>, <&gpc0 1 2 0 3>,
> >> +                             <&gpc1 0 2 3 3>, <&gpc1 1 2 3 3>,
> >> +                             <&gpc1 2 2 3 3>, <&gpc1 3 2 3 3>,
> >> +                             <&gpc0 3 2 3 3>, <&gpc0 4 2 3 3>,
> >> +                             <&gpc0 5 2 3 3>, <&gpc0 6 2 3 3>;
> >> +             };
> >> +     };
> >> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> >> index aa131b3..9bf10e7 100644
> >> --- a/drivers/mmc/host/Kconfig
> >> +++ b/drivers/mmc/host/Kconfig
> >> @@ -540,6 +540,15 @@ config MMC_DW_PLTFM
> >>
> >>         If unsure, say Y.
> >>
> >> +config MMC_DW_EXYNOS
> >> +     tristate "Exynos specific extentions for Synopsys DW Memory Card Interface"
> >> +     depends on MMC_DW
> >> +     select MMC_DW_PLTFM
> >> +     help
> >> +       This selects support for Samsung Exynos SoC specific extensions to the
> >> +       Synopsys DesignWare Memory Card Interface driver. Select this option
> >> +       for platforms based on Exynos4 and Exynos5 SoC's.
> >> +
> >>  config MMC_DW_PCI
> >>       tristate "Synopsys Designware MCI support on PCI bus"
> >>       depends on MMC_DW && PCI
> >> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> >> index 8922b06..17ad0a7 100644
> >> --- a/drivers/mmc/host/Makefile
> >> +++ b/drivers/mmc/host/Makefile
> >> @@ -39,6 +39,7 @@ obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o
> >>  obj-$(CONFIG_SDH_BFIN)               += bfin_sdh.o
> >>  obj-$(CONFIG_MMC_DW)         += dw_mmc.o
> >>  obj-$(CONFIG_MMC_DW_PLTFM)   += dw_mmc-pltfm.o
> >> +obj-$(CONFIG_MMC_DW_EXYNOS)  += dw_mmc-exynos.o
> >>  obj-$(CONFIG_MMC_DW_PCI)     += dw_mmc-pci.o
> >>  obj-$(CONFIG_MMC_SH_MMCIF)   += sh_mmcif.o
> >>  obj-$(CONFIG_MMC_JZ4740)     += jz4740_mmc.o
> >> diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c
> >> new file mode 100644
> >> index 0000000..313f364
> >> --- /dev/null
> >> +++ b/drivers/mmc/host/dw_mmc-exynos.c
> >> @@ -0,0 +1,253 @@
> >> +/*
> >> + * Exynos Specific Extensions for Synopsys DW Multimedia Card Interface driver
> >> + *
> >> + * Copyright (C) 2012, Samsung Electronics Co., Ltd.
> >> + *
> >> + * This program is free software; you can redistribute it and/or modify
> >> + * it under the terms of the GNU General Public License as published by
> >> + * the Free Software Foundation; either version 2 of the License, or
> >> + * (at your option) any later version.
> >> + */
> >> +
> >> +#include <linux/module.h>
> >> +#include <linux/platform_device.h>
> >> +#include <linux/clk.h>
> >> +#include <linux/mmc/host.h>
> >> +#include <linux/mmc/dw_mmc.h>
> >> +#include <linux/of.h>
> >> +#include <linux/of_gpio.h>
> >> +
> >> +#include "dw_mmc.h"
> >> +#include "dw_mmc-pltfm.h"
> >> +
> >> +#define NUM_PINS(x)                  (x + 2)
> >> +
> >> +#define SDMMC_CLKSEL                 0x09C
> >> +#define SDMMC_CLKSEL_CCLK_SAMPLE(x)  (((x) & 7) << 0)
> >> +#define SDMMC_CLKSEL_CCLK_DRIVE(x)   (((x) & 7) << 16)
> >> +#define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24)
> >> +#define SDMMC_CLKSEL_GET_DRV_WD3(x)  (((x) >> 16) & 0x7)
> >> +#define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) |  \
> >> +                                     SDMMC_CLKSEL_CCLK_DRIVE(y) |    \
> >> +                                     SDMMC_CLKSEL_CCLK_DIVIDER(z))
> >> +
> >> +#define SDMMC_CMD_USE_HOLD_REG               BIT(29)
> >> +
> >> +#define EXYNOS4210_FIXED_CIU_CLK_DIV 2
> >> +#define EXYNOS4412_FIXED_CIU_CLK_DIV 4
> >> +
> >> +/* Variations in Exynos specific dw-mshc controller */
> >> +enum dw_mci_exynos_type {
> >> +     DW_MCI_TYPE_EXYNOS4210,
> >> +     DW_MCI_TYPE_EXYNOS4412,
> >> +     DW_MCI_TYPE_EXYNOS5250,
> >> +};
> >> +
> >> +/* Exynos implementation specific driver private data */
> >> +struct dw_mci_exynos_priv_data {
> >> +     enum dw_mci_exynos_type         ctrl_type;
> >> +     u8                              ciu_div;
> >> +     u32                             sdr_timing;
> >> +     u32                             ddr_timing;
> >> +};
> >> +
> >> +static struct dw_mci_exynos_compatible {
> >> +     char                            *compatible;
> >> +     enum dw_mci_exynos_type         ctrl_type;
> >> +} exynos_compat[] = {
> >> +     {
> >> +             .compatible     = "samsung,exynos4210-dw-mshc",
> >> +             .ctrl_type      = DW_MCI_TYPE_EXYNOS4210,
> >> +     }, {
> >> +             .compatible     = "samsung,exynos4412-dw-mshc",
> >> +             .ctrl_type      = DW_MCI_TYPE_EXYNOS4412,
> >> +     }, {
> >> +             .compatible     = "samsung,exynos5250-dw-mshc",
> >> +             .ctrl_type      = DW_MCI_TYPE_EXYNOS5250,
> >> +     },
> >> +};
> >> +
> >> +static int dw_mci_exynos_priv_init(struct dw_mci *host)
> >> +{
> >> +     struct dw_mci_exynos_priv_data *priv;
> >> +     int idx;
> >> +
> >> +     priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
> >> +     if (!priv) {
> >> +             dev_err(host->dev, "mem alloc failed for private data\n");
> >> +             return -ENOMEM;
> >> +     }
> >> +
> >> +     for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
> >> +             if (of_device_is_compatible(host->dev->of_node,
> >> +                                     exynos_compat[idx].compatible))
> >> +                     priv->ctrl_type = exynos_compat[idx].ctrl_type;
> >> +     }
> >> +
> >> +     host->priv = priv;
> >> +     return 0;
> >> +}
> >> +
> >> +static int dw_mci_exynos_setup_clock(struct dw_mci *host)
> >> +{
> >> +     struct dw_mci_exynos_priv_data *priv = host->priv;
> >> +
> > It assume that initial bus_hz is from clk_get_rate.
> > Can it be ensured? If value of bus_hz is set from platform, calculation is wrong below.
> 
> In case of legacy (non-dt) platforms, if bus_hz is being specified
> using platform_data, the clock rate of bus_hz should be set to the
> clock rate at the cclk_in pad of the dwmmc controller. platform_data
> need to calculate the internal clock speed of ciu_clk.
Until now, bus_hz coming from platform_data has the value of which calculation is finished for cclk_in.
We should pay attention to use bus_hz with setup_clock callback.
If you remain some comment anywhere for the bus_hz, it would be helpful.

And latest version is v5 or v6?
It is difficult to distinguish it from previous one.

Thanks,
Seungwon Jeon

> 
> >
> >> +     if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5250)
> >> +             host->bus_hz /= (priv->ciu_div + 1);
> >> +     else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
> >> +             host->bus_hz /= EXYNOS4412_FIXED_CIU_CLK_DIV;
> >> +     else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
> >> +             host->bus_hz /= EXYNOS4210_FIXED_CIU_CLK_DIV;
> >> +
> >> +     return 0;
> > Here,  returning is any meaning?
> > There seems to be no error case.
> 
> The implementation specific callbacks have been designed to be generic
> and usable on multiple platforms. The return value allows the
> implementation specific callback to return any error code to the
> caller.
> 
> >
> >> +}
> >> +
> >> +static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
> >> +{
> >> +     /*
> >> +      * Exynos4412 and Exynos5250 extends the use of CMD register with the
> >> +      * use of bit 29 (which is reserved on standard MSHC controllers) for
> >> +      * optionally bypassing the HOLD register for command and data. The
> >> +      * HOLD register should be bypassed in case there is no phase shift
> >> +      * applied on CMD/DATA that is sent to the card.
> >> +      */
> >> +     if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL)))
> >> +             *cmdr |= SDMMC_CMD_USE_HOLD_REG;
> >> +}
> >> +
> >> +static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
> >> +{
> >> +     struct dw_mci_exynos_priv_data *priv = host->priv;
> >> +
> >> +     if (ios->timing == MMC_TIMING_UHS_DDR50)
> >> +             mci_writel(host, CLKSEL, priv->ddr_timing);
> >> +     else
> >> +             mci_writel(host, CLKSEL, priv->sdr_timing);
> >> +}
> >> +
> >> +static int dw_mci_exynos_parse_dt(struct dw_mci *host)
> >> +{
> >> +     struct dw_mci_exynos_priv_data *priv = host->priv;
> >> +     struct device_node *np = host->dev->of_node;
> >> +     u32 timing[3];
> > u32 timing[2] is enough.
> 
> Yes, that is right. I will change it.
> 
> Thanks,
> Thomas.
> 
> >
> > Thanks,
> > Seungwon Jeon
> >
> >> +     u32 div = 0;
> >> +     int ret;
> >> +
> >> +     of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
> >> +     priv->ciu_div = div;
> >> +
> >> +     ret = of_property_read_u32_array(np,
> >> +                     "samsung,dw-mshc-sdr-timing", timing, 2);
> >> +     if (ret)
> >> +             return ret;
> >> +
> >> +     priv->sdr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
> >> +
> >> +     ret = of_property_read_u32_array(np,
> >> +                     "samsung,dw-mshc-ddr-timing", timing, 2);
> >> +     if (ret)
> >> +             return ret;
> >> +
> >> +     priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
> >> +     return 0;
> >> +}
> >> +
> >> +static int dw_mci_exynos_setup_bus(struct dw_mci *host,
> >> +                             struct device_node *slot_np, u8 bus_width)
> >> +{
> >> +     int idx, gpio, ret;
> >> +
> >> +     if (!slot_np)
> >> +             return -EINVAL;
> >> +
> >> +     /* cmd + clock + bus-width pins */
> >> +     for (idx = 0; idx < NUM_PINS(bus_width); idx++) {
> >> +             gpio = of_get_gpio(slot_np, idx);
> >> +             if (!gpio_is_valid(gpio)) {
> >> +                     dev_err(host->dev, "invalid gpio: %d\n", gpio);
> >> +                     return -EINVAL;
> >> +             }
> >> +
> >> +             ret = devm_gpio_request(host->dev, gpio, "dw-mci-bus");
> >> +             if (ret) {
> >> +                     dev_err(host->dev, "gpio [%d] request failed\n", gpio);
> >> +                     return -EBUSY;
> >> +             }
> >> +     }
> >> +
> >> +     gpio = of_get_named_gpio(slot_np, "wp-gpios", 0);
> >> +     if (gpio_is_valid(gpio)) {
> >> +             if (devm_gpio_request(host->dev, gpio, "dw-mci-wp"))
> >> +                     dev_info(host->dev, "gpio [%d] request failed\n",
> >> +                                             gpio);
> >> +     } else {
> >> +             dev_info(host->dev, "wp gpio not available");
> >> +             host->pdata->quirks |= DW_MCI_QUIRK_NO_WRITE_PROTECT;
> >> +     }
> >> +
> >> +     if (host->pdata->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)
> >> +             return 0;
> >> +
> >> +     gpio = of_get_named_gpio(slot_np, "samsung,cd-pinmux-gpio", 0);
> >> +     if (gpio_is_valid(gpio)) {
> >> +             if (devm_gpio_request(host->dev, gpio, "dw-mci-cd"))
> >> +                     dev_err(host->dev, "gpio [%d] request failed\n", gpio);
> >> +     } else {
> >> +             dev_info(host->dev, "cd gpio not available");
> >> +     }
> >> +
> >> +     return 0;
> >> +}
> >> +
> >> +/* Exynos5250 controller specific capabilities */
> >> +static unsigned long exynos5250_dwmmc_caps[4] = {
> >> +     MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR |
> >> +             MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23,
> >> +     MMC_CAP_CMD23,
> >> +     MMC_CAP_CMD23,
> >> +     MMC_CAP_CMD23,
> >> +};
> >> +
> >> +static struct dw_mci_drv_data exynos5250_drv_data = {
> >> +     .caps                   = exynos5250_dwmmc_caps,
> >> +     .init                   = dw_mci_exynos_priv_init,
> >> +     .setup_clock            = dw_mci_exynos_setup_clock,
> >> +     .prepare_command        = dw_mci_exynos_prepare_command,
> >> +     .set_ios                = dw_mci_exynos_set_ios,
> >> +     .parse_dt               = dw_mci_exynos_parse_dt,
> >> +     .setup_bus              = dw_mci_exynos_setup_bus,
> >> +};
> >> +
> >> +static const struct of_device_id dw_mci_exynos_match[] = {
> >> +     { .compatible = "samsung,exynos5250-dw-mshc",
> >> +                     .data = (void *)&exynos5250_drv_data, },
> >> +     {},
> >> +};
> >> +MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
> >> +
> >> +int dw_mci_exynos_probe(struct platform_device *pdev)
> >> +{
> >> +     struct dw_mci_drv_data *drv_data;
> >> +     const struct of_device_id *match;
> >> +
> >> +     match = of_match_node(dw_mci_exynos_match, pdev->dev.of_node);
> >> +     drv_data = match->data;
> >> +     return dw_mci_pltfm_register(pdev, drv_data);
> >> +}
> >> +
> >> +static struct platform_driver dw_mci_exynos_pltfm_driver = {
> >> +     .probe          = dw_mci_exynos_probe,
> >> +     .remove         = __exit_p(dw_mci_pltfm_remove),
> >> +     .driver         = {
> >> +             .name           = "dwmmc_exynos",
> >> +             .of_match_table = of_match_ptr(dw_mci_exynos_match),
> >> +             .pm             = &dw_mci_pltfm_pmops,
> >> +     },
> >> +};
> >> +
> >> +module_platform_driver(dw_mci_exynos_pltfm_driver);
> >> +
> >> +MODULE_DESCRIPTION("Samsung Specific DW-MSHC Driver Extension");
> >> +MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com");
> >> +MODULE_LICENSE("GPL v2");
> >> +MODULE_ALIAS("platform:dwmmc-exynos");
> >> --
> >> 1.6.6.rc2
> >>
> >> --
> >> 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
> >
> --
> 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

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v5 9/9] mmc: dw_mmc: add support for exynos specific implementation of dw-mshc
       [not found]           ` <00a601cd8ce6$ca599e50$5f0cdaf0$%jun-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
@ 2012-09-07 16:13             ` Thomas Abraham
  0 siblings, 0 replies; 29+ messages in thread
From: Thomas Abraham @ 2012-09-07 16:13 UTC (permalink / raw)
  To: Seungwon Jeon
  Cc: jh80.chung-Sze3O3UU22JBDgjK7y7TUQ,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	patches-QSEj5FYQhm4dnm+yROfE0A,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA,
	rob.herring-bsGFqQB8/DxBDgjK7y7TUQ,
	will.newton-1AXoQHu6uovQT0dZR+AlfA,
	kgene.kim-Sze3O3UU22JBDgjK7y7TUQ,
	girish.shivananjappa-QSEj5FYQhm4dnm+yROfE0A,
	cjb-2X9k7bc8m7Mdnm+yROfE0A

On 7 September 2012 16:21, Seungwon Jeon <tgih.jun-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org> wrote:
> On Friday, September 07, 2012, Thomas Abraham <thomas.abraham-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
>> Hi Seungwon,
>>
>> Thanks for reviewing the patch.
>>
>> On 5 September 2012 16:13, Seungwon Jeon <tgih.jun-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org> wrote:
>> > On Wednesday, September 05, 2012, Thomas Abraham <thomas.abraham-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
>> > Version 6 is right?
>> >
>> >> Samsung Exynos SoC's extend the dw-mshc controller for additional clock and bus
>> >> control. Add support for these extensions and include provide device tree based
>> >> discovery suppory as well.
>> >>
>> >> Signed-off-by: Thomas Abraham <thomas.abraham-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> >> Acked-by: Will Newton <will.newton-1AXoQHu6uovQT0dZR+AlfA@public.gmane.org>
>> >> ---
>> >>  .../devicetree/bindings/mmc/exynos-dw-mshc.txt     |   86 +++++++
>> >>  drivers/mmc/host/Kconfig                           |    9 +
>> >>  drivers/mmc/host/Makefile                          |    1 +
>> >>  drivers/mmc/host/dw_mmc-exynos.c                   |  253 ++++++++++++++++++++
>> >>  4 files changed, 349 insertions(+), 0 deletions(-)
>> >>  create mode 100644 Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
>> >>  create mode 100644 drivers/mmc/host/dw_mmc-exynos.c
>> >>
>> >> diff --git a/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
>> >> b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
>> >> new file mode 100644
>> >> index 0000000..323a891
>> >> --- /dev/null
>> >> +++ b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
>> >> @@ -0,0 +1,86 @@
>> >> +* Samsung Exynos specific extensions to the Synopsis Designware Mobile
>> >> +  Storage Host Controller
>> >> +
>> >> +The Synopsis designware mobile storage host controller is used to interface
>> >> +a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
>> >> +differences between the core Synopsis dw mshc controller properties described
>> >> +by synposis-dw-mshc.txt and the properties used by the Samsung Exynos specific
>> >> +extensions to the Synopsis Designware Mobile Storage Host Controller.
>> >> +
>> >> +Required Properties:
>> >> +
>> >> +* compatible: should be
>> >> +     - "samsung,exynos4210-dw-mshc": for controllers with Samsung Exynos4210
>> >> +       specific extentions.
>> >> +     - "samsung,exynos4412-dw-mshc": for controllers with Samsung Exynos4412
>> >> +       specific extentions.
>> >> +     - "samsung,exynos5250-dw-mshc": for controllers with Samsung Exynos5250
>> >> +       specific extentions.
>> >> +
>> >> +* samsung,dw-mshc-ciu-div: Specifies the divider value for the card interface
>> >> +  unit (ciu) clock. This property is applicable only for Exynos5 SoC's and
>> >> +  ignored for Exynos4 SoC's. The valid range of divider value is 0 to 7.
>> >> +
>> >> +* samsung,dw-mshc-sdr-timing: Specifies the value of CIU clock phase shift value
>> >> +  in transmit mode and CIU clock phase shift value in receive mode for single
>> >> +  data rate mode operation. Refer notes below for the order of the cells and the
>> >> +  valid values.
>> >> +
>> >> +* samsung,dw-mshc-ddr-timing: Specifies the value of CUI clock phase shift value
>> >> +  in transmit mode and CIU clock phase shift value in receive mode for double
>> >> +  data rate mode operation. Refer notes below for the order of the cells and the
>> >> +  valid values.
>> >> +
>> >> +  Notes for the sdr-timing and ddr-timing values:
>> >> +
>> >> +    The order of the cells should be
>> >> +      - First Cell: CIU clock phase shift value for tx mode.
>> >> +      - Second Cell: CIU clock phase shift value for rx mode.
>> >> +
>> >> +    Valid values for SDR and DDR CIU clock timing for Exynos5250:
>> >> +      - valid value for tx phase shift and rx phase shift is 0 to 7.
>> >> +      - when CIU clock divider value is set to 3, all possible 8 phase shift
>> >> +        values can be used.
>> >> +      - if CIU clock divider value is 0 (that is divide by 1), both tx and rx
>> >> +        phase shift clocks should be 0.
>> >> +
>> >> +Required properties for a slot:
>> >> +
>> >> +* gpios: specifies a list of gpios used for command, clock and data bus. The
>> >> +  first gpio is the command line and the second gpio is the clock line. The
>> >> +  rest of the gpios (depending on the bus-width property) are the data lines in
>> >> +  no particular order. The format of the gpio specifier depends on the gpio
>> >> +  controller.
>> >> +
>> >> +Example:
>> >> +
>> >> +  The MSHC controller node can be split into two portions, SoC specific and
>> >> +  board specific portions as listed below.
>> >> +
>> >> +     dwmmc0@12200000 {
>> >> +             compatible = "samsung,exynos5250-dw-mshc";
>> >> +             reg = <0x12200000 0x1000>;
>> >> +             interrupts = <0 75 0>;
>> >> +             #address-cells = <1>;
>> >> +             #size-cells = <0>;
>> >> +     };
>> >> +
>> >> +     dwmmc0@12200000 {
>> >> +             num-slots = <1>;
>> >> +             supports-highspeed;
>> >> +             broken-cd;
>> >> +             fifo-depth = <0x80>;
>> >> +             card-detect-delay = <200>;
>> >> +             samsung,dw-mshc-sdr-timing = <2 3 3>;
>> >> +             samsung,dw-mshc-ddr-timing = <1 2 3>;
>> > Third filed is still useful?
>>
>> No, it is not used anymore. Thanks for pointing this out.
>>
>> >
>> >
>> >> +
>> >> +             slot@0 {
>> >> +                     reg = <0>;
>> >> +                     bus-width = <8>;
>> >> +                     gpios = <&gpc0 0 2 0 3>, <&gpc0 1 2 0 3>,
>> >> +                             <&gpc1 0 2 3 3>, <&gpc1 1 2 3 3>,
>> >> +                             <&gpc1 2 2 3 3>, <&gpc1 3 2 3 3>,
>> >> +                             <&gpc0 3 2 3 3>, <&gpc0 4 2 3 3>,
>> >> +                             <&gpc0 5 2 3 3>, <&gpc0 6 2 3 3>;
>> >> +             };
>> >> +     };
>> >> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
>> >> index aa131b3..9bf10e7 100644
>> >> --- a/drivers/mmc/host/Kconfig
>> >> +++ b/drivers/mmc/host/Kconfig
>> >> @@ -540,6 +540,15 @@ config MMC_DW_PLTFM
>> >>
>> >>         If unsure, say Y.
>> >>
>> >> +config MMC_DW_EXYNOS
>> >> +     tristate "Exynos specific extentions for Synopsys DW Memory Card Interface"
>> >> +     depends on MMC_DW
>> >> +     select MMC_DW_PLTFM
>> >> +     help
>> >> +       This selects support for Samsung Exynos SoC specific extensions to the
>> >> +       Synopsys DesignWare Memory Card Interface driver. Select this option
>> >> +       for platforms based on Exynos4 and Exynos5 SoC's.
>> >> +
>> >>  config MMC_DW_PCI
>> >>       tristate "Synopsys Designware MCI support on PCI bus"
>> >>       depends on MMC_DW && PCI
>> >> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
>> >> index 8922b06..17ad0a7 100644
>> >> --- a/drivers/mmc/host/Makefile
>> >> +++ b/drivers/mmc/host/Makefile
>> >> @@ -39,6 +39,7 @@ obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o
>> >>  obj-$(CONFIG_SDH_BFIN)               += bfin_sdh.o
>> >>  obj-$(CONFIG_MMC_DW)         += dw_mmc.o
>> >>  obj-$(CONFIG_MMC_DW_PLTFM)   += dw_mmc-pltfm.o
>> >> +obj-$(CONFIG_MMC_DW_EXYNOS)  += dw_mmc-exynos.o
>> >>  obj-$(CONFIG_MMC_DW_PCI)     += dw_mmc-pci.o
>> >>  obj-$(CONFIG_MMC_SH_MMCIF)   += sh_mmcif.o
>> >>  obj-$(CONFIG_MMC_JZ4740)     += jz4740_mmc.o
>> >> diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c
>> >> new file mode 100644
>> >> index 0000000..313f364
>> >> --- /dev/null
>> >> +++ b/drivers/mmc/host/dw_mmc-exynos.c
>> >> @@ -0,0 +1,253 @@
>> >> +/*
>> >> + * Exynos Specific Extensions for Synopsys DW Multimedia Card Interface driver
>> >> + *
>> >> + * Copyright (C) 2012, Samsung Electronics Co., Ltd.
>> >> + *
>> >> + * This program is free software; you can redistribute it and/or modify
>> >> + * it under the terms of the GNU General Public License as published by
>> >> + * the Free Software Foundation; either version 2 of the License, or
>> >> + * (at your option) any later version.
>> >> + */
>> >> +
>> >> +#include <linux/module.h>
>> >> +#include <linux/platform_device.h>
>> >> +#include <linux/clk.h>
>> >> +#include <linux/mmc/host.h>
>> >> +#include <linux/mmc/dw_mmc.h>
>> >> +#include <linux/of.h>
>> >> +#include <linux/of_gpio.h>
>> >> +
>> >> +#include "dw_mmc.h"
>> >> +#include "dw_mmc-pltfm.h"
>> >> +
>> >> +#define NUM_PINS(x)                  (x + 2)
>> >> +
>> >> +#define SDMMC_CLKSEL                 0x09C
>> >> +#define SDMMC_CLKSEL_CCLK_SAMPLE(x)  (((x) & 7) << 0)
>> >> +#define SDMMC_CLKSEL_CCLK_DRIVE(x)   (((x) & 7) << 16)
>> >> +#define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24)
>> >> +#define SDMMC_CLKSEL_GET_DRV_WD3(x)  (((x) >> 16) & 0x7)
>> >> +#define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) |  \
>> >> +                                     SDMMC_CLKSEL_CCLK_DRIVE(y) |    \
>> >> +                                     SDMMC_CLKSEL_CCLK_DIVIDER(z))
>> >> +
>> >> +#define SDMMC_CMD_USE_HOLD_REG               BIT(29)
>> >> +
>> >> +#define EXYNOS4210_FIXED_CIU_CLK_DIV 2
>> >> +#define EXYNOS4412_FIXED_CIU_CLK_DIV 4
>> >> +
>> >> +/* Variations in Exynos specific dw-mshc controller */
>> >> +enum dw_mci_exynos_type {
>> >> +     DW_MCI_TYPE_EXYNOS4210,
>> >> +     DW_MCI_TYPE_EXYNOS4412,
>> >> +     DW_MCI_TYPE_EXYNOS5250,
>> >> +};
>> >> +
>> >> +/* Exynos implementation specific driver private data */
>> >> +struct dw_mci_exynos_priv_data {
>> >> +     enum dw_mci_exynos_type         ctrl_type;
>> >> +     u8                              ciu_div;
>> >> +     u32                             sdr_timing;
>> >> +     u32                             ddr_timing;
>> >> +};
>> >> +
>> >> +static struct dw_mci_exynos_compatible {
>> >> +     char                            *compatible;
>> >> +     enum dw_mci_exynos_type         ctrl_type;
>> >> +} exynos_compat[] = {
>> >> +     {
>> >> +             .compatible     = "samsung,exynos4210-dw-mshc",
>> >> +             .ctrl_type      = DW_MCI_TYPE_EXYNOS4210,
>> >> +     }, {
>> >> +             .compatible     = "samsung,exynos4412-dw-mshc",
>> >> +             .ctrl_type      = DW_MCI_TYPE_EXYNOS4412,
>> >> +     }, {
>> >> +             .compatible     = "samsung,exynos5250-dw-mshc",
>> >> +             .ctrl_type      = DW_MCI_TYPE_EXYNOS5250,
>> >> +     },
>> >> +};
>> >> +
>> >> +static int dw_mci_exynos_priv_init(struct dw_mci *host)
>> >> +{
>> >> +     struct dw_mci_exynos_priv_data *priv;
>> >> +     int idx;
>> >> +
>> >> +     priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
>> >> +     if (!priv) {
>> >> +             dev_err(host->dev, "mem alloc failed for private data\n");
>> >> +             return -ENOMEM;
>> >> +     }
>> >> +
>> >> +     for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
>> >> +             if (of_device_is_compatible(host->dev->of_node,
>> >> +                                     exynos_compat[idx].compatible))
>> >> +                     priv->ctrl_type = exynos_compat[idx].ctrl_type;
>> >> +     }
>> >> +
>> >> +     host->priv = priv;
>> >> +     return 0;
>> >> +}
>> >> +
>> >> +static int dw_mci_exynos_setup_clock(struct dw_mci *host)
>> >> +{
>> >> +     struct dw_mci_exynos_priv_data *priv = host->priv;
>> >> +
>> > It assume that initial bus_hz is from clk_get_rate.
>> > Can it be ensured? If value of bus_hz is set from platform, calculation is wrong below.
>>
>> In case of legacy (non-dt) platforms, if bus_hz is being specified
>> using platform_data, the clock rate of bus_hz should be set to the
>> clock rate at the cclk_in pad of the dwmmc controller. platform_data
>> need to calculate the internal clock speed of ciu_clk.
> Until now, bus_hz coming from platform_data has the value of which calculation is finished for cclk_in.
> We should pay attention to use bus_hz with setup_clock callback.
> If you remain some comment anywhere for the bus_hz, it would be helpful.

Okay, I will add this point in the documentation for platform_data
structure of dwmmc driver.

>
> And latest version is v5 or v6?
> It is difficult to distinguish it from previous one.

Yes, I did not increment the version number of the patch, sorry about
that. Only three patches out of the nine patches were modified to
address the comments. And since the changes were minor, I wanted to
retain the same number for the reworked patches and maintain the
version number with the rest of the patches in this series. If there
are no more comments on this patch series, I can repost the full
series with a incremented version number.

Thanks,
Thomas.

[...]

^ permalink raw reply	[flat|nested] 29+ messages in thread

end of thread, other threads:[~2012-09-07 16:13 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-08-29 10:48 [PATCH v5 0/9] mmc: dw_mmc: add support for device tree based instantiation Thomas Abraham
2012-08-29 10:48 ` [PATCH v5 1/9] mmc: dw_mmc: convert copy of struct device in struct dw_mci to a reference Thomas Abraham
2012-08-29 10:48 ` [PATCH v5 2/9] mmc: dw_mmc: Use devm_* functions in dw_mmc platform driver Thomas Abraham
2012-08-29 10:48 ` [PATCH v5 3/9] mmc: dw_mmc: allow probe to succeed even if one slot is initialized Thomas Abraham
2012-08-29 10:48 ` [PATCH v5 4/9] mmc: dw_mmc: lookup for optional biu and ciu clocks Thomas Abraham
2012-08-30  8:48   ` Jaehoon Chung
2012-08-31  5:29     ` Thomas Abraham
2012-08-31  6:02       ` Jaehoon Chung
2012-08-31  6:32         ` Thomas Abraham
2012-08-31  6:32         ` Thomas Abraham
2012-08-31  7:59           ` Jaehoon Chung
2012-08-31  8:48             ` Thomas Abraham
2012-09-04 19:46               ` Thomas Abraham
2012-09-05  8:30                 ` Jaehoon Chung
2012-09-07  6:26                   ` Thomas Abraham
2012-09-07  9:39                     ` Thomas Abraham
2012-08-29 10:48 ` [PATCH v5 5/9] mmc: dw_mmc: add quirk to indicate missing write protect line Thomas Abraham
2012-08-29 10:48 ` [PATCH v5 6/9] mmc: dw_mmc: add device tree support Thomas Abraham
2012-08-29 10:48 ` [PATCH v5 7/9] mmc: dw_mmc: prepare functions in dw_mmc-pltfm for reuse Thomas Abraham
2012-08-29 10:48 ` [PATCH v5 8/9] mmc: dw_mmc: add support for implementation specific callbacks Thomas Abraham
2012-09-04 19:47   ` Thomas Abraham
2012-09-07  9:40   ` Thomas Abraham
2012-08-29 10:48 ` [PATCH v5 9/9] mmc: dw_mmc: add support for exynos specific implementation of dw-mshc Thomas Abraham
2012-09-04 19:48   ` Thomas Abraham
2012-09-05 10:43     ` Seungwon Jeon
2012-09-07  7:15       ` Thomas Abraham
2012-09-07  9:41         ` Thomas Abraham
2012-09-07 10:51         ` Seungwon Jeon
     [not found]           ` <00a601cd8ce6$ca599e50$5f0cdaf0$%jun-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
2012-09-07 16:13             ` Thomas Abraham

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.