|
@@ -33,6 +33,7 @@
|
|
#include <linux/bitops.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/workqueue.h>
|
|
|
|
+#include <linux/of.h>
|
|
|
|
|
|
#include "dw_mmc.h"
|
|
#include "dw_mmc.h"
|
|
|
|
|
|
@@ -230,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)
|
|
static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
|
|
{
|
|
{
|
|
struct mmc_data *data;
|
|
struct mmc_data *data;
|
|
|
|
+ struct dw_mci_slot *slot = mmc_priv(mmc);
|
|
u32 cmdr;
|
|
u32 cmdr;
|
|
cmd->error = -EINPROGRESS;
|
|
cmd->error = -EINPROGRESS;
|
|
|
|
|
|
@@ -259,6 +261,9 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
|
|
cmdr |= SDMMC_CMD_DAT_WR;
|
|
cmdr |= SDMMC_CMD_DAT_WR;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (slot->host->drv_data->prepare_command)
|
|
|
|
+ slot->host->drv_data->prepare_command(slot->host, &cmdr);
|
|
|
|
+
|
|
return cmdr;
|
|
return cmdr;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -266,7 +271,7 @@ static void dw_mci_start_command(struct dw_mci *host,
|
|
struct mmc_command *cmd, u32 cmd_flags)
|
|
struct mmc_command *cmd, u32 cmd_flags)
|
|
{
|
|
{
|
|
host->cmd = cmd;
|
|
host->cmd = cmd;
|
|
- dev_vdbg(&host->dev,
|
|
|
|
|
|
+ dev_vdbg(host->dev,
|
|
"start command: ARGR=0x%08x CMDR=0x%08x\n",
|
|
"start command: ARGR=0x%08x CMDR=0x%08x\n",
|
|
cmd->arg, cmd_flags);
|
|
cmd->arg, cmd_flags);
|
|
|
|
|
|
@@ -308,7 +313,7 @@ static void dw_mci_dma_cleanup(struct dw_mci *host)
|
|
|
|
|
|
if (data)
|
|
if (data)
|
|
if (!data->host_cookie)
|
|
if (!data->host_cookie)
|
|
- dma_unmap_sg(&host->dev,
|
|
|
|
|
|
+ dma_unmap_sg(host->dev,
|
|
data->sg,
|
|
data->sg,
|
|
data->sg_len,
|
|
data->sg_len,
|
|
dw_mci_get_dma_dir(data));
|
|
dw_mci_get_dma_dir(data));
|
|
@@ -334,7 +339,7 @@ static void dw_mci_idmac_complete_dma(struct dw_mci *host)
|
|
{
|
|
{
|
|
struct mmc_data *data = host->data;
|
|
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);
|
|
host->dma_ops->cleanup(host);
|
|
|
|
|
|
@@ -405,23 +410,11 @@ static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len)
|
|
static int dw_mci_idmac_init(struct dw_mci *host)
|
|
static int dw_mci_idmac_init(struct dw_mci *host)
|
|
{
|
|
{
|
|
struct idmac_desc *p;
|
|
struct idmac_desc *p;
|
|
- int i, dma_support;
|
|
|
|
|
|
+ int i;
|
|
|
|
|
|
/* Number of descriptors in the ring buffer */
|
|
/* Number of descriptors in the ring buffer */
|
|
host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
|
|
host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
|
|
|
|
|
|
- /* Check if Hardware Configuration Register has support for DMA */
|
|
|
|
- dma_support = (mci_readl(host, HCON) >> 16) & 0x3;
|
|
|
|
-
|
|
|
|
- if (!dma_support || dma_support > 2) {
|
|
|
|
- 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");
|
|
|
|
-
|
|
|
|
/* Forward link the descriptor list */
|
|
/* Forward link the descriptor list */
|
|
for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
|
|
for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
|
|
p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1));
|
|
p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1));
|
|
@@ -476,7 +469,7 @@ static int dw_mci_pre_dma_transfer(struct dw_mci *host,
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
|
|
- sg_len = dma_map_sg(&host->dev,
|
|
|
|
|
|
+ sg_len = dma_map_sg(host->dev,
|
|
data->sg,
|
|
data->sg,
|
|
data->sg_len,
|
|
data->sg_len,
|
|
dw_mci_get_dma_dir(data));
|
|
dw_mci_get_dma_dir(data));
|
|
@@ -519,7 +512,7 @@ static void dw_mci_post_req(struct mmc_host *mmc,
|
|
return;
|
|
return;
|
|
|
|
|
|
if (data->host_cookie)
|
|
if (data->host_cookie)
|
|
- dma_unmap_sg(&slot->host->dev,
|
|
|
|
|
|
+ dma_unmap_sg(slot->host->dev,
|
|
data->sg,
|
|
data->sg,
|
|
data->sg_len,
|
|
data->sg_len,
|
|
dw_mci_get_dma_dir(data));
|
|
dw_mci_get_dma_dir(data));
|
|
@@ -545,7 +538,7 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
|
|
|
|
|
|
host->using_dma = 1;
|
|
host->using_dma = 1;
|
|
|
|
|
|
- dev_vdbg(&host->dev,
|
|
|
|
|
|
+ dev_vdbg(host->dev,
|
|
"sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n",
|
|
"sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n",
|
|
(unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
|
|
(unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
|
|
sg_len);
|
|
sg_len);
|
|
@@ -814,6 +807,9 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
slot->clock = ios->clock;
|
|
slot->clock = ios->clock;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (slot->host->drv_data->set_ios)
|
|
|
|
+ slot->host->drv_data->set_ios(slot->host, ios);
|
|
|
|
+
|
|
switch (ios->power_mode) {
|
|
switch (ios->power_mode) {
|
|
case MMC_POWER_UP:
|
|
case MMC_POWER_UP:
|
|
set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
|
|
set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
|
|
@@ -830,7 +826,9 @@ static int dw_mci_get_ro(struct mmc_host *mmc)
|
|
struct dw_mci_board *brd = slot->host->pdata;
|
|
struct dw_mci_board *brd = slot->host->pdata;
|
|
|
|
|
|
/* Use platform get_ro function, else try on board write protect */
|
|
/* 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);
|
|
read_only = brd->get_ro(slot->id);
|
|
else
|
|
else
|
|
read_only =
|
|
read_only =
|
|
@@ -939,12 +937,12 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
|
|
slot = list_entry(host->queue.next,
|
|
slot = list_entry(host->queue.next,
|
|
struct dw_mci_slot, queue_node);
|
|
struct dw_mci_slot, queue_node);
|
|
list_del(&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));
|
|
mmc_hostname(slot->mmc));
|
|
host->state = STATE_SENDING_CMD;
|
|
host->state = STATE_SENDING_CMD;
|
|
dw_mci_start_request(host, slot);
|
|
dw_mci_start_request(host, slot);
|
|
} else {
|
|
} else {
|
|
- dev_vdbg(&host->dev, "list empty\n");
|
|
|
|
|
|
+ dev_vdbg(host->dev, "list empty\n");
|
|
host->state = STATE_IDLE;
|
|
host->state = STATE_IDLE;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1083,7 +1081,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
|
|
data->bytes_xfered = 0;
|
|
data->bytes_xfered = 0;
|
|
data->error = -ETIMEDOUT;
|
|
data->error = -ETIMEDOUT;
|
|
} else {
|
|
} else {
|
|
- dev_err(&host->dev,
|
|
|
|
|
|
+ dev_err(host->dev,
|
|
"data FIFO error "
|
|
"data FIFO error "
|
|
"(status=%08x)\n",
|
|
"(status=%08x)\n",
|
|
status);
|
|
status);
|
|
@@ -1767,12 +1765,60 @@ static void dw_mci_work_routine_card(struct work_struct *work)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|
|
|
|
|
+#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 mmc_host *mmc;
|
|
struct dw_mci_slot *slot;
|
|
struct dw_mci_slot *slot;
|
|
|
|
+ int ctrl_id, ret;
|
|
|
|
+ u8 bus_width;
|
|
|
|
|
|
- mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), &host->dev);
|
|
|
|
|
|
+ mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
|
|
if (!mmc)
|
|
if (!mmc)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
|
|
|
|
@@ -1780,6 +1826,7 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|
slot->id = id;
|
|
slot->id = id;
|
|
slot->mmc = mmc;
|
|
slot->mmc = mmc;
|
|
slot->host = host;
|
|
slot->host = host;
|
|
|
|
+ host->slot[id] = slot;
|
|
|
|
|
|
mmc->ops = &dw_mci_ops;
|
|
mmc->ops = &dw_mci_ops;
|
|
mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510);
|
|
mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510);
|
|
@@ -1800,21 +1847,44 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|
if (host->pdata->caps)
|
|
if (host->pdata->caps)
|
|
mmc->caps = 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)
|
|
if (host->pdata->caps2)
|
|
mmc->caps2 = 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;
|
|
|
|
|
|
+ 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;
|
|
|
|
+
|
|
|
|
+ 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;
|
|
|
|
+ case 4:
|
|
|
|
+ mmc->caps |= MMC_CAP_4_BIT_DATA;
|
|
|
|
+ }
|
|
|
|
|
|
if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
|
|
if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
|
|
mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
|
|
mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
|
|
|
|
|
|
- if (mmc->caps2 & MMC_CAP2_POWEROFF_NOTIFY)
|
|
|
|
- mmc->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT;
|
|
|
|
- else
|
|
|
|
- mmc->power_notify_type = MMC_HOST_PW_NOTIFY_NONE;
|
|
|
|
-
|
|
|
|
if (host->pdata->blk_settings) {
|
|
if (host->pdata->blk_settings) {
|
|
mmc->max_segs = host->pdata->blk_settings->max_segs;
|
|
mmc->max_segs = host->pdata->blk_settings->max_segs;
|
|
mmc->max_blk_size = host->pdata->blk_settings->max_blk_size;
|
|
mmc->max_blk_size = host->pdata->blk_settings->max_blk_size;
|
|
@@ -1850,7 +1920,6 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|
else
|
|
else
|
|
clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
|
|
clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
|
|
|
|
|
|
- host->slot[id] = slot;
|
|
|
|
mmc_add_host(mmc);
|
|
mmc_add_host(mmc);
|
|
|
|
|
|
#if defined(CONFIG_DEBUG_FS)
|
|
#if defined(CONFIG_DEBUG_FS)
|
|
@@ -1867,6 +1936,10 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|
queue_work(host->card_workqueue, &host->card_work);
|
|
queue_work(host->card_workqueue, &host->card_work);
|
|
|
|
|
|
return 0;
|
|
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)
|
|
static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id)
|
|
@@ -1884,10 +1957,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)
|
|
static void dw_mci_init_dma(struct dw_mci *host)
|
|
{
|
|
{
|
|
/* Alloc memory for sg translation */
|
|
/* 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);
|
|
&host->sg_dma, GFP_KERNEL);
|
|
if (!host->sg_cpu) {
|
|
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__);
|
|
__func__);
|
|
goto no_dma;
|
|
goto no_dma;
|
|
}
|
|
}
|
|
@@ -1895,6 +1968,7 @@ static void dw_mci_init_dma(struct dw_mci *host)
|
|
/* Determine which DMA interface to use */
|
|
/* Determine which DMA interface to use */
|
|
#ifdef CONFIG_MMC_DW_IDMAC
|
|
#ifdef CONFIG_MMC_DW_IDMAC
|
|
host->dma_ops = &dw_mci_idmac_ops;
|
|
host->dma_ops = &dw_mci_idmac_ops;
|
|
|
|
+ dev_info(&host->dev, "Using internal DMA controller.\n");
|
|
#endif
|
|
#endif
|
|
|
|
|
|
if (!host->dma_ops)
|
|
if (!host->dma_ops)
|
|
@@ -1903,12 +1977,12 @@ static void dw_mci_init_dma(struct dw_mci *host)
|
|
if (host->dma_ops->init && host->dma_ops->start &&
|
|
if (host->dma_ops->init && host->dma_ops->start &&
|
|
host->dma_ops->stop && host->dma_ops->cleanup) {
|
|
host->dma_ops->stop && host->dma_ops->cleanup) {
|
|
if (host->dma_ops->init(host)) {
|
|
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__);
|
|
"DMA Controller.\n", __func__);
|
|
goto no_dma;
|
|
goto no_dma;
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
- dev_err(&host->dev, "DMA initialization not found.\n");
|
|
|
|
|
|
+ dev_err(host->dev, "DMA initialization not found.\n");
|
|
goto no_dma;
|
|
goto no_dma;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1916,7 +1990,7 @@ static void dw_mci_init_dma(struct dw_mci *host)
|
|
return;
|
|
return;
|
|
|
|
|
|
no_dma:
|
|
no_dma:
|
|
- dev_info(&host->dev, "Using PIO mode.\n");
|
|
|
|
|
|
+ dev_info(host->dev, "Using PIO mode.\n");
|
|
host->use_dma = 0;
|
|
host->use_dma = 0;
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
@@ -1942,30 +2016,133 @@ static bool mci_wait_reset(struct device *dev, struct dw_mci *host)
|
|
return false;
|
|
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, ret;
|
|
|
|
+
|
|
|
|
+ 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);
|
|
|
|
+
|
|
|
|
+ if (host->drv_data->parse_dt) {
|
|
|
|
+ ret = host->drv_data->parse_dt(host);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ERR_PTR(ret);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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 dw_mci_probe(struct dw_mci *host)
|
|
{
|
|
{
|
|
int width, i, ret = 0;
|
|
int width, i, ret = 0;
|
|
u32 fifo_size;
|
|
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) {
|
|
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");
|
|
"Platform data must supply select_slot function\n");
|
|
return -ENODEV;
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
|
|
|
|
- if (!host->pdata->bus_hz) {
|
|
|
|
- dev_err(&host->dev,
|
|
|
|
|
|
+ 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->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");
|
|
"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;
|
|
host->quirks = host->pdata->quirks;
|
|
|
|
|
|
spin_lock_init(&host->lock);
|
|
spin_lock_init(&host->lock);
|
|
@@ -1998,7 +2175,7 @@ int dw_mci_probe(struct dw_mci *host)
|
|
}
|
|
}
|
|
|
|
|
|
/* Reset all blocks */
|
|
/* Reset all blocks */
|
|
- if (!mci_wait_reset(&host->dev, host))
|
|
|
|
|
|
+ if (!mci_wait_reset(host->dev, host))
|
|
return -ENODEV;
|
|
return -ENODEV;
|
|
|
|
|
|
host->dma_ops = host->pdata->dma_ops;
|
|
host->dma_ops = host->pdata->dma_ops;
|
|
@@ -2054,10 +2231,18 @@ int dw_mci_probe(struct dw_mci *host)
|
|
/* We need at least one slot to succeed */
|
|
/* We need at least one slot to succeed */
|
|
for (i = 0; i < host->num_slots; i++) {
|
|
for (i = 0; i < host->num_slots; i++) {
|
|
ret = dw_mci_init_slot(host, 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;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -2065,7 +2250,7 @@ int dw_mci_probe(struct dw_mci *host)
|
|
* Need to check the version-id and set data-offset for DATA register.
|
|
* Need to check the version-id and set data-offset for DATA register.
|
|
*/
|
|
*/
|
|
host->verid = SDMMC_GET_VERID(mci_readl(host, VERID));
|
|
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)
|
|
if (host->verid < DW_MMC_240A)
|
|
host->data_offset = DATA_OFFSET;
|
|
host->data_offset = DATA_OFFSET;
|
|
@@ -2082,22 +2267,16 @@ int dw_mci_probe(struct dw_mci *host)
|
|
DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
|
|
DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
|
|
mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */
|
|
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, "
|
|
"%d bit host data width, "
|
|
"%u deep fifo\n",
|
|
"%u deep fifo\n",
|
|
host->irq, width, fifo_size);
|
|
host->irq, width, fifo_size);
|
|
if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
|
|
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;
|
|
return 0;
|
|
|
|
|
|
err_init_slot:
|
|
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);
|
|
free_irq(host->irq, host);
|
|
|
|
|
|
err_workqueue:
|
|
err_workqueue:
|
|
@@ -2106,13 +2285,24 @@ err_workqueue:
|
|
err_dmaunmap:
|
|
err_dmaunmap:
|
|
if (host->use_dma && host->dma_ops->exit)
|
|
if (host->use_dma && host->dma_ops->exit)
|
|
host->dma_ops->exit(host);
|
|
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);
|
|
host->sg_cpu, host->sg_dma);
|
|
|
|
|
|
if (host->vmmc) {
|
|
if (host->vmmc) {
|
|
regulator_disable(host->vmmc);
|
|
regulator_disable(host->vmmc);
|
|
regulator_put(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;
|
|
return ret;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(dw_mci_probe);
|
|
EXPORT_SYMBOL(dw_mci_probe);
|
|
@@ -2125,7 +2315,7 @@ void dw_mci_remove(struct dw_mci *host)
|
|
mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
|
|
mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
|
|
|
|
|
|
for (i = 0; i < host->num_slots; i++) {
|
|
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])
|
|
if (host->slot[i])
|
|
dw_mci_cleanup_slot(host->slot[i], i);
|
|
dw_mci_cleanup_slot(host->slot[i], i);
|
|
}
|
|
}
|
|
@@ -2136,7 +2326,7 @@ void dw_mci_remove(struct dw_mci *host)
|
|
|
|
|
|
free_irq(host->irq, host);
|
|
free_irq(host->irq, host);
|
|
destroy_workqueue(host->card_workqueue);
|
|
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)
|
|
if (host->use_dma && host->dma_ops->exit)
|
|
host->dma_ops->exit(host);
|
|
host->dma_ops->exit(host);
|
|
@@ -2146,6 +2336,12 @@ void dw_mci_remove(struct dw_mci *host)
|
|
regulator_put(host->vmmc);
|
|
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);
|
|
EXPORT_SYMBOL(dw_mci_remove);
|
|
|
|
|
|
@@ -2188,7 +2384,7 @@ int dw_mci_resume(struct dw_mci *host)
|
|
if (host->vmmc)
|
|
if (host->vmmc)
|
|
regulator_enable(host->vmmc);
|
|
regulator_enable(host->vmmc);
|
|
|
|
|
|
- if (!mci_wait_reset(&host->dev, host)) {
|
|
|
|
|
|
+ if (!mci_wait_reset(host->dev, host)) {
|
|
ret = -ENODEV;
|
|
ret = -ENODEV;
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|