All of lore.kernel.org
 help / color / mirror / Atom feed
From: Thomas Abraham <thomas.abraham@linaro.org>
To: linux-mmc@vger.kernel.org, devicetree-discuss@lists.ozlabs.org
Cc: linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org, cjb@laptop.org,
	grant.likely@secretlab.ca, rob.herring@calxeda.com,
	linux-samsung-soc@vger.kernel.org, kgene.kim@samsung.com,
	patches@linaro.org
Subject: [PATCH v3 5/6] mmc: dw_mmc: add device tree support
Date: Thu, 12 Jul 2012 18:24:27 +0530	[thread overview]
Message-ID: <1342097668-25243-6-git-send-email-thomas.abraham@linaro.org> (raw)
In-Reply-To: <1342097668-25243-1-git-send-email-thomas.abraham@linaro.org>

Add device tree based discovery support.

Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
---
 .../devicetree/bindings/mmc/synposis-dw-mshc.txt   |  108 +++++++++++
 drivers/mmc/host/dw_mmc-pltfm.c                    |   24 +++
 drivers/mmc/host/dw_mmc.c                          |  201 +++++++++++++++++++-
 drivers/mmc/host/dw_mmc.h                          |    9 +
 include/linux/mmc/dw_mmc.h                         |    2 +
 5 files changed, 338 insertions(+), 6 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..3acd6c9
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/synposis-dw-mshc.txt
@@ -0,0 +1,108 @@
+* 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.
+
+Required Properties:
+
+* compatible: should be one of the following
+	- snps,dw-mshc: for controllers compliant with synopsis dw-mshc.
+
+* reg: physical base address of the dw-mshc controller and size of its memory
+  region.
+
+* interrupts: interrupt specifier for the controller. The format and value of
+  the interrupt specifier depends on the interrupt parent for the controller.
+
+* #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: specifies the width of the data bus connected from the
+	  controller to the card slot. The value should be 1, 4 or 8. In case
+	  this property is not specified, a default value of 1 is assumed for
+	  this property.
+
+	* cd-gpios: specifies the card detect gpio line. The format of the
+	  gpio specifier depends on the gpio controller.
+
+	* wp-gpios: specifies the write protect gpio line. The format of the
+	  gpio specifier depends on the gpio controller.
+
+	* 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.
+
+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)
+
+* card-detection-broken: The card detection functionality is not available on
+  any of the slots.
+
+* no-write-protect: The write protect pad of the controller is not connected
+  to the write protect pin on the slot.
+
+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;
+		card-detection-broken;
+		no-write-protect;
+		fifo-depth = <0x80>;
+		card-detect-delay = <200>;
+
+		slot@0 {
+			reg = <0>;
+			bus-width = <8>;
+			cd-gpios = <&gpc0 2 2 3 3>;
+			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/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index 9a63299..8d24f6d 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -19,8 +19,24 @@
 #include <linux/mmc/host.h>
 #include <linux/mmc/mmc.h>
 #include <linux/mmc/dw_mmc.h>
+#include <linux/of.h>
 #include "dw_mmc.h"
 
+#ifdef CONFIG_OF
+static struct dw_mci_drv_data synopsis_drv_data = {
+	.ctrl_type	= DW_MCI_TYPE_SYNOPSIS,
+};
+
+static const struct of_device_id dw_mci_pltfm_match[] = {
+	{ .compatible = "snps,dw-mshc",
+			.data = (void *)&synopsis_drv_data, },
+	{},
+};
+MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
+#else
+static const struct of_device_id dw_mci_pltfm_match[];
+#endif
+
 static int dw_mci_pltfm_probe(struct platform_device *pdev)
 {
 	struct dw_mci *host;
@@ -51,6 +67,13 @@ static int dw_mci_pltfm_probe(struct platform_device *pdev)
 	if (!host->regs)
 		goto err_free;
 	platform_set_drvdata(pdev, host);
+
+	if (pdev->dev.of_node) {
+		const struct of_device_id *match;
+		match = of_match_node(dw_mci_pltfm_match, pdev->dev.of_node);
+		host->drv_data = match->data;
+	}
+
 	ret = dw_mci_probe(host);
 	if (ret)
 		goto err_out;
@@ -111,6 +134,7 @@ 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 075f89d..3bc276d 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -33,9 +33,13 @@
 #include <linux/bitops.h>
 #include <linux/regulator/consumer.h>
 #include <linux/workqueue.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
 
 #include "dw_mmc.h"
 
+#define NUM_PINS(x) (x + 2)
+
 /* Common flag combinations */
 #define DW_MCI_DATA_ERROR_FLAGS	(SDMMC_INT_DTO | SDMMC_INT_DCRC | \
 				 SDMMC_INT_HTO | SDMMC_INT_SBE  | \
@@ -86,6 +90,8 @@ struct idmac_desc {
 struct dw_mci_slot {
 	struct mmc_host		*mmc;
 	struct dw_mci		*host;
+	int			wp_gpio;
+	int			cd_gpio;
 
 	u32			ctype;
 
@@ -1762,10 +1768,106 @@ static void dw_mci_work_routine_card(struct work_struct *work)
 	}
 }
 
+#ifdef CONFIG_OF
+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;
+}
+
+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;
+}
+
+static int dw_mci_of_setup_bus(struct dw_mci *host, u8 slot, u32 bus_wd)
+{
+	struct device_node *np = dw_mci_of_find_slot_node(host->dev, slot);
+	int idx, gpio, ret;
+
+	if (!np)
+		return -EINVAL;
+
+	for (idx = 0; idx < NUM_PINS(bus_wd); idx++) {
+		gpio = of_get_gpio(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;
+		}
+	}
+
+	host->slot[slot]->wp_gpio = -1;
+	gpio = of_get_named_gpio(np, "wp-gpios", 0);
+	if (!gpio_is_valid(gpio)) {
+		dev_info(host->dev, "wp gpio not available");
+	} else {
+		ret = devm_gpio_request(host->dev, gpio, "dw-mci-wp");
+		if (ret)
+			dev_info(host->dev, "gpio [%d] request failed\n",
+						gpio);
+		else
+			host->slot[slot]->wp_gpio = gpio;
+	}
+
+	host->slot[slot]->cd_gpio = -1;
+	gpio = of_get_named_gpio(np, "cd-gpios", 0);
+	if (!gpio_is_valid(gpio)) {
+		dev_info(host->dev, "cd gpio not available");
+	} else {
+		ret = devm_gpio_request(host->dev, gpio, "dw-mci-cd");
+		if (ret)
+			dev_err(host->dev, "gpio [%d] request failed\n", gpio);
+		else
+			host->slot[slot]->cd_gpio = gpio;
+	}
+
+	return 0;
+}
+#else /* CONFIG_OF */
+static u32 dw_mci_of_get_bus_wd(struct device *dev, u8 slot)
+{
+	return 1;
+}
+
+static int dw_mci_of_setup_bus(struct dw_mci *host, u8 slot, u32 bus_wd)
+{
+	return -EINVAL;
+}
+#endif /* CONFIG_OF */
+
 static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 {
 	struct mmc_host *mmc;
 	struct dw_mci_slot *slot;
+	int ctrl_id, ret;
 
 	mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
 	if (!mmc)
@@ -1775,6 +1877,7 @@ static int __init 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);
@@ -1795,12 +1898,33 @@ static int __init 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;
+	}
+	if (host->drv_data->caps)
+		mmc->caps |= host->drv_data->caps[ctrl_id];
+
 	if (host->pdata->caps2)
 		mmc->caps2 = host->pdata->caps2;
 
-	if (host->pdata->get_bus_wd)
+	if (host->pdata->get_bus_wd) {
 		if (host->pdata->get_bus_wd(slot->id) >= 4)
 			mmc->caps |= MMC_CAP_4_BIT_DATA;
+	} else if (host->dev->of_node) {
+		unsigned int bus_width;
+		bus_width = dw_mci_of_get_bus_wd(host->dev, slot->id);
+		switch (bus_width) {
+		case 8:
+			mmc->caps |= MMC_CAP_8_BIT_DATA;
+		case 4:
+			mmc->caps |= MMC_CAP_4_BIT_DATA;
+		}
+		ret = dw_mci_of_setup_bus(host, slot->id, bus_width);
+		if (ret)
+			goto err_setup_bus;
+	}
 
 	if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
 		mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
@@ -1845,7 +1969,6 @@ static int __init 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)
@@ -1862,6 +1985,10 @@ static int __init 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)
@@ -1937,16 +2064,78 @@ 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_quriks[] = {
+	{
+		.quirk	= "supports-highspeed",
+		.id	= DW_MCI_QUIRK_HIGHSPEED,
+	}, {
+		.quirk	= "card-detection-broken",
+		.id	= DW_MCI_QUIRK_BROKEN_CARD_DETECTION,
+	}, {
+		.quirk	= "no-write-protect",
+		.id	= DW_MCI_QUIRK_NO_WRITE_PROTECT,
+	}
+};
+
+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, cnt;
+
+	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 */
+	cnt = sizeof(of_quriks) / sizeof(struct dw_mci_of_quirks);
+	for (idx = 0; idx < cnt; idx++)
+		if (of_get_property(np, of_quriks[idx].quirk, NULL))
+			pdata->quirks |= of_quriks[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) {
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 15c27e1..1ecaa02 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -182,4 +182,13 @@ extern int dw_mci_suspend(struct dw_mci *host);
 extern int dw_mci_resume(struct dw_mci *host);
 #endif
 
+/* Variations in the dw_mci controller */
+#define DW_MCI_TYPE_SYNOPSIS		0
+
+/* dw_mci platform driver data */
+struct dw_mci_drv_data {
+	unsigned long		ctrl_type;
+	unsigned long		*caps;
+};
+
 #endif /* _DW_MMC_H_ */
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
index b72e4aa..ae45e4f 100644
--- a/include/linux/mmc/dw_mmc.h
+++ b/include/linux/mmc/dw_mmc.h
@@ -78,6 +78,7 @@ 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
  * @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 +161,7 @@ struct dw_mci {
 	u16			data_offset;
 	struct device		*dev;
 	struct dw_mci_board	*pdata;
+	struct dw_mci_drv_data	*drv_data;
 	struct clk		*biu_clk;
 	struct clk		*ciu_clk;
 	struct dw_mci_slot	*slot[MAX_MCI_SLOTS];
-- 
1.6.6.rc2


WARNING: multiple messages have this Message-ID (diff)
From: thomas.abraham@linaro.org (Thomas Abraham)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v3 5/6] mmc: dw_mmc: add device tree support
Date: Thu, 12 Jul 2012 18:24:27 +0530	[thread overview]
Message-ID: <1342097668-25243-6-git-send-email-thomas.abraham@linaro.org> (raw)
In-Reply-To: <1342097668-25243-1-git-send-email-thomas.abraham@linaro.org>

Add device tree based discovery support.

Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
---
 .../devicetree/bindings/mmc/synposis-dw-mshc.txt   |  108 +++++++++++
 drivers/mmc/host/dw_mmc-pltfm.c                    |   24 +++
 drivers/mmc/host/dw_mmc.c                          |  201 +++++++++++++++++++-
 drivers/mmc/host/dw_mmc.h                          |    9 +
 include/linux/mmc/dw_mmc.h                         |    2 +
 5 files changed, 338 insertions(+), 6 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..3acd6c9
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/synposis-dw-mshc.txt
@@ -0,0 +1,108 @@
+* 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.
+
+Required Properties:
+
+* compatible: should be one of the following
+	- snps,dw-mshc: for controllers compliant with synopsis dw-mshc.
+
+* reg: physical base address of the dw-mshc controller and size of its memory
+  region.
+
+* interrupts: interrupt specifier for the controller. The format and value of
+  the interrupt specifier depends on the interrupt parent for the controller.
+
+* #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 at 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: specifies the width of the data bus connected from the
+	  controller to the card slot. The value should be 1, 4 or 8. In case
+	  this property is not specified, a default value of 1 is assumed for
+	  this property.
+
+	* cd-gpios: specifies the card detect gpio line. The format of the
+	  gpio specifier depends on the gpio controller.
+
+	* wp-gpios: specifies the write protect gpio line. The format of the
+	  gpio specifier depends on the gpio controller.
+
+	* 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.
+
+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)
+
+* card-detection-broken: The card detection functionality is not available on
+  any of the slots.
+
+* no-write-protect: The write protect pad of the controller is not connected
+  to the write protect pin on the slot.
+
+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 at 12200000 {
+		compatible = "snps,dw-mshc";
+		reg = <0x12200000 0x1000>;
+		interrupts = <0 75 0>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+	};
+
+	dwmmc0 at 12200000 {
+		num-slots = <1>;
+		supports-highspeed;
+		card-detection-broken;
+		no-write-protect;
+		fifo-depth = <0x80>;
+		card-detect-delay = <200>;
+
+		slot at 0 {
+			reg = <0>;
+			bus-width = <8>;
+			cd-gpios = <&gpc0 2 2 3 3>;
+			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/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index 9a63299..8d24f6d 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -19,8 +19,24 @@
 #include <linux/mmc/host.h>
 #include <linux/mmc/mmc.h>
 #include <linux/mmc/dw_mmc.h>
+#include <linux/of.h>
 #include "dw_mmc.h"
 
+#ifdef CONFIG_OF
+static struct dw_mci_drv_data synopsis_drv_data = {
+	.ctrl_type	= DW_MCI_TYPE_SYNOPSIS,
+};
+
+static const struct of_device_id dw_mci_pltfm_match[] = {
+	{ .compatible = "snps,dw-mshc",
+			.data = (void *)&synopsis_drv_data, },
+	{},
+};
+MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
+#else
+static const struct of_device_id dw_mci_pltfm_match[];
+#endif
+
 static int dw_mci_pltfm_probe(struct platform_device *pdev)
 {
 	struct dw_mci *host;
@@ -51,6 +67,13 @@ static int dw_mci_pltfm_probe(struct platform_device *pdev)
 	if (!host->regs)
 		goto err_free;
 	platform_set_drvdata(pdev, host);
+
+	if (pdev->dev.of_node) {
+		const struct of_device_id *match;
+		match = of_match_node(dw_mci_pltfm_match, pdev->dev.of_node);
+		host->drv_data = match->data;
+	}
+
 	ret = dw_mci_probe(host);
 	if (ret)
 		goto err_out;
@@ -111,6 +134,7 @@ 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 075f89d..3bc276d 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -33,9 +33,13 @@
 #include <linux/bitops.h>
 #include <linux/regulator/consumer.h>
 #include <linux/workqueue.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
 
 #include "dw_mmc.h"
 
+#define NUM_PINS(x) (x + 2)
+
 /* Common flag combinations */
 #define DW_MCI_DATA_ERROR_FLAGS	(SDMMC_INT_DTO | SDMMC_INT_DCRC | \
 				 SDMMC_INT_HTO | SDMMC_INT_SBE  | \
@@ -86,6 +90,8 @@ struct idmac_desc {
 struct dw_mci_slot {
 	struct mmc_host		*mmc;
 	struct dw_mci		*host;
+	int			wp_gpio;
+	int			cd_gpio;
 
 	u32			ctype;
 
@@ -1762,10 +1768,106 @@ static void dw_mci_work_routine_card(struct work_struct *work)
 	}
 }
 
+#ifdef CONFIG_OF
+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;
+}
+
+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;
+}
+
+static int dw_mci_of_setup_bus(struct dw_mci *host, u8 slot, u32 bus_wd)
+{
+	struct device_node *np = dw_mci_of_find_slot_node(host->dev, slot);
+	int idx, gpio, ret;
+
+	if (!np)
+		return -EINVAL;
+
+	for (idx = 0; idx < NUM_PINS(bus_wd); idx++) {
+		gpio = of_get_gpio(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;
+		}
+	}
+
+	host->slot[slot]->wp_gpio = -1;
+	gpio = of_get_named_gpio(np, "wp-gpios", 0);
+	if (!gpio_is_valid(gpio)) {
+		dev_info(host->dev, "wp gpio not available");
+	} else {
+		ret = devm_gpio_request(host->dev, gpio, "dw-mci-wp");
+		if (ret)
+			dev_info(host->dev, "gpio [%d] request failed\n",
+						gpio);
+		else
+			host->slot[slot]->wp_gpio = gpio;
+	}
+
+	host->slot[slot]->cd_gpio = -1;
+	gpio = of_get_named_gpio(np, "cd-gpios", 0);
+	if (!gpio_is_valid(gpio)) {
+		dev_info(host->dev, "cd gpio not available");
+	} else {
+		ret = devm_gpio_request(host->dev, gpio, "dw-mci-cd");
+		if (ret)
+			dev_err(host->dev, "gpio [%d] request failed\n", gpio);
+		else
+			host->slot[slot]->cd_gpio = gpio;
+	}
+
+	return 0;
+}
+#else /* CONFIG_OF */
+static u32 dw_mci_of_get_bus_wd(struct device *dev, u8 slot)
+{
+	return 1;
+}
+
+static int dw_mci_of_setup_bus(struct dw_mci *host, u8 slot, u32 bus_wd)
+{
+	return -EINVAL;
+}
+#endif /* CONFIG_OF */
+
 static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 {
 	struct mmc_host *mmc;
 	struct dw_mci_slot *slot;
+	int ctrl_id, ret;
 
 	mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
 	if (!mmc)
@@ -1775,6 +1877,7 @@ static int __init 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);
@@ -1795,12 +1898,33 @@ static int __init 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;
+	}
+	if (host->drv_data->caps)
+		mmc->caps |= host->drv_data->caps[ctrl_id];
+
 	if (host->pdata->caps2)
 		mmc->caps2 = host->pdata->caps2;
 
-	if (host->pdata->get_bus_wd)
+	if (host->pdata->get_bus_wd) {
 		if (host->pdata->get_bus_wd(slot->id) >= 4)
 			mmc->caps |= MMC_CAP_4_BIT_DATA;
+	} else if (host->dev->of_node) {
+		unsigned int bus_width;
+		bus_width = dw_mci_of_get_bus_wd(host->dev, slot->id);
+		switch (bus_width) {
+		case 8:
+			mmc->caps |= MMC_CAP_8_BIT_DATA;
+		case 4:
+			mmc->caps |= MMC_CAP_4_BIT_DATA;
+		}
+		ret = dw_mci_of_setup_bus(host, slot->id, bus_width);
+		if (ret)
+			goto err_setup_bus;
+	}
 
 	if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
 		mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
@@ -1845,7 +1969,6 @@ static int __init 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)
@@ -1862,6 +1985,10 @@ static int __init 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)
@@ -1937,16 +2064,78 @@ 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_quriks[] = {
+	{
+		.quirk	= "supports-highspeed",
+		.id	= DW_MCI_QUIRK_HIGHSPEED,
+	}, {
+		.quirk	= "card-detection-broken",
+		.id	= DW_MCI_QUIRK_BROKEN_CARD_DETECTION,
+	}, {
+		.quirk	= "no-write-protect",
+		.id	= DW_MCI_QUIRK_NO_WRITE_PROTECT,
+	}
+};
+
+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, cnt;
+
+	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 */
+	cnt = sizeof(of_quriks) / sizeof(struct dw_mci_of_quirks);
+	for (idx = 0; idx < cnt; idx++)
+		if (of_get_property(np, of_quriks[idx].quirk, NULL))
+			pdata->quirks |= of_quriks[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) {
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 15c27e1..1ecaa02 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -182,4 +182,13 @@ extern int dw_mci_suspend(struct dw_mci *host);
 extern int dw_mci_resume(struct dw_mci *host);
 #endif
 
+/* Variations in the dw_mci controller */
+#define DW_MCI_TYPE_SYNOPSIS		0
+
+/* dw_mci platform driver data */
+struct dw_mci_drv_data {
+	unsigned long		ctrl_type;
+	unsigned long		*caps;
+};
+
 #endif /* _DW_MMC_H_ */
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
index b72e4aa..ae45e4f 100644
--- a/include/linux/mmc/dw_mmc.h
+++ b/include/linux/mmc/dw_mmc.h
@@ -78,6 +78,7 @@ 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
  * @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 +161,7 @@ struct dw_mci {
 	u16			data_offset;
 	struct device		*dev;
 	struct dw_mci_board	*pdata;
+	struct dw_mci_drv_data	*drv_data;
 	struct clk		*biu_clk;
 	struct clk		*ciu_clk;
 	struct dw_mci_slot	*slot[MAX_MCI_SLOTS];
-- 
1.6.6.rc2

  parent reply	other threads:[~2012-07-12 12:41 UTC|newest]

Thread overview: 53+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-07-12 12:54 [PATCH v3 0/6] mmc: dw_mmc: add support for device tree based instantiation Thomas Abraham
2012-07-12 12:54 ` Thomas Abraham
2012-07-12 12:54 ` [PATCH v3 1/6] mmc: dw_mmc: convert copy of struct device in struct dw_mci to a reference Thomas Abraham
2012-07-12 12:54   ` Thomas Abraham
2012-07-12 12:54 ` [PATCH v3 2/6] mmc: dw_mmc: allow probe to succeed even if one slot is initialized Thomas Abraham
2012-07-12 12:54   ` Thomas Abraham
2012-07-12 12:54   ` Thomas Abraham
2012-07-12 12:54 ` [PATCH v3 3/6] mmc: dw_mmc: lookup for optional biu and ciu clocks Thomas Abraham
2012-07-12 12:54   ` Thomas Abraham
2012-07-12 12:54   ` Thomas Abraham
2012-07-13  9:11   ` Girish K S
2012-07-13  9:11     ` Girish K S
2012-07-17 10:38     ` [PATCH 3/9] " Thomas Abraham
2012-07-17 10:38       ` Thomas Abraham
2012-07-19 10:27       ` Seungwon Jeon
2012-07-19 10:27         ` Seungwon Jeon
2012-07-12 12:54 ` [PATCH v3 4/6] mmc: dw_mmc: add quirk to indicate missing write protect line Thomas Abraham
2012-07-12 12:54   ` Thomas Abraham
2012-07-12 12:54 ` Thomas Abraham [this message]
2012-07-12 12:54   ` [PATCH v3 5/6] mmc: dw_mmc: add device tree support Thomas Abraham
2012-07-16  4:15   ` Girish K S
2012-07-16  4:15     ` Girish K S
2012-07-16  4:30     ` Girish K S
2012-07-16  4:30       ` Girish K S
2012-07-17 10:19     ` Thomas Abraham
2012-07-17 10:19       ` Thomas Abraham
2012-07-16  4:23   ` Chris Ball
2012-07-16  4:23     ` Chris Ball
2012-07-17 10:21     ` Thomas Abraham
2012-07-17 10:21       ` Thomas Abraham
2012-07-17 10:21       ` Thomas Abraham
2012-07-17 10:37       ` [PATCH 5/9] " Thomas Abraham
2012-07-17 10:37         ` Thomas Abraham
2012-07-12 12:54 ` [PATCH v3 6/6] mmc: dw_mmc: add samsung exynos5250 specific extentions Thomas Abraham
2012-07-12 12:54   ` Thomas Abraham
2012-07-19  3:51   ` Seungwon Jeon
2012-07-19  3:51     ` Seungwon Jeon
2012-07-19 18:48     ` Thomas Abraham
2012-07-19 18:48       ` Thomas Abraham
2012-07-20 10:38       ` Seungwon Jeon
2012-07-20 10:38         ` Seungwon Jeon
2012-07-23  6:17         ` Thomas Abraham
2012-07-23  6:17           ` Thomas Abraham
2012-07-23  9:59           ` Seungwon Jeon
2012-07-23  9:59             ` Seungwon Jeon
2012-07-19 15:28 ` [PATCH v3 0/6] mmc: dw_mmc: add support for device tree based instantiation Jaehoon Chung
2012-07-19 15:28   ` Jaehoon Chung
2012-07-19 18:59   ` Thomas Abraham
2012-07-19 18:59     ` Thomas Abraham
2012-07-19 22:47     ` Kukjin Kim
2012-07-19 22:47       ` Kukjin Kim
2012-07-23  1:57       ` Jaehoon Chung
2012-07-23  1:57         ` Jaehoon Chung

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1342097668-25243-6-git-send-email-thomas.abraham@linaro.org \
    --to=thomas.abraham@linaro.org \
    --cc=cjb@laptop.org \
    --cc=devicetree-discuss@lists.ozlabs.org \
    --cc=grant.likely@secretlab.ca \
    --cc=kgene.kim@samsung.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mmc@vger.kernel.org \
    --cc=linux-samsung-soc@vger.kernel.org \
    --cc=patches@linaro.org \
    --cc=rob.herring@calxeda.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.