mmc: dw_mmc: add support for implementation specific callbacks

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>
Signed-off-by: Chris Ball <cjb@laptop.org>
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 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 @@
 			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 @@
 		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 @@
 {
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 
 	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 @@
 	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");