#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
-#include <linux/workqueue.h>
#include <linux/timer.h>
#include <linux/clk.h>
#include <linux/mmc/host.h>
*/
struct regulator *vcc;
struct regulator *vcc_aux;
- struct work_struct mmc_carddetect_work;
void __iomem *base;
resource_size_t mapbase;
spinlock_t irq_lock; /* Prevent races with irq handler */
static DEVICE_ATTR(slot_name, S_IRUGO, omap_hsmmc_show_slot_name, NULL);
+/* for hosts with 35xx erratum 2.1.1.128 */
+static ssize_t
+omap_hsmmc_show_unsafe_read(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev);
+ int val = 0;
+
+ if (!(mmc->caps2 & MMC_CAP2_NO_MULTI_READ)) {
+ val = 1;
+ if (mmc->f_max == OMAP_MMC_MAX_CLOCK)
+ val = 2;
+ }
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t
+omap_hsmmc_set_unsafe_read(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev);
+ unsigned long val;
+ int ret;
+
+ ret = strict_strtoul(buf, 0, &val);
+ if (ret)
+ return -EINVAL;
+
+ switch (val) {
+ case 0:
+ mmc->caps2 |= MMC_CAP2_NO_MULTI_READ;
+ mmc->f_max = OMAP_MMC_MAX_CLOCK;
+ break;
+ case 1:
+ mmc->caps2 &= ~MMC_CAP2_NO_MULTI_READ;
+ mmc->f_max = 32000000;
+ break;
+ case 2:
+ mmc->caps2 &= ~MMC_CAP2_NO_MULTI_READ;
+ mmc->f_max = OMAP_MMC_MAX_CLOCK;
+ break;
+ }
+
+ return count;
+}
+static DEVICE_ATTR(unsafe_read, S_IWUSR | S_IRUGO,
+ omap_hsmmc_show_unsafe_read, omap_hsmmc_set_unsafe_read);
+
/*
* Configure the response type and send the cmd.
*/
}
/*
- * Work Item to notify the core about card insertion/removal
+ * irq handler to notify the core about card insertion/removal
*/
-static void omap_hsmmc_detect(struct work_struct *work)
+static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id)
{
- struct omap_hsmmc_host *host =
- container_of(work, struct omap_hsmmc_host, mmc_carddetect_work);
+ struct omap_hsmmc_host *host = dev_id;
struct omap_mmc_slot_data *slot = &mmc_slot(host);
int carddetect;
if (host->suspended)
- return;
+ return IRQ_HANDLED;
sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
mmc_detect_change(host->mmc, (HZ * 200) / 1000);
else
mmc_detect_change(host->mmc, (HZ * 50) / 1000);
-}
-
-/*
- * ISR for handling card insertion and removal
- */
-static irqreturn_t omap_hsmmc_cd_handler(int irq, void *dev_id)
-{
- struct omap_hsmmc_host *host = (struct omap_hsmmc_host *)dev_id;
-
- if (host->suspended)
- return IRQ_HANDLED;
- schedule_work(&host->mmc_carddetect_work);
-
return IRQ_HANDLED;
}
return 0;
}
-static void set_data_timeout(struct omap_hsmmc_host *host,
- unsigned int timeout_ns,
- unsigned int timeout_clks)
+static void set_data_timeout(struct omap_hsmmc_host *host)
{
- unsigned int timeout, cycle_ns;
uint32_t reg, clkd, dto = 0;
reg = OMAP_HSMMC_READ(host->base, SYSCTL);
if (clkd == 0)
clkd = 1;
- cycle_ns = 1000000000 / (clk_get_rate(host->fclk) / clkd);
- timeout = timeout_ns / cycle_ns;
- timeout += timeout_clks;
- if (timeout) {
- while ((timeout & 0x80000000) == 0) {
- dto += 1;
- timeout <<= 1;
- }
- dto = 31 - dto;
- timeout <<= 1;
- if (timeout && dto)
- dto += 1;
- if (dto >= 13)
- dto -= 13;
- else
- dto = 0;
- if (dto > 14)
- dto = 14;
- }
+ /* Use the maximum timeout value allowed in the standard of 14 or 0xE */
+ dto = 14;
reg &= ~DTO_MASK;
reg |= dto << DTO_SHIFT;
* busy signal.
*/
if (req->cmd->flags & MMC_RSP_BUSY)
- set_data_timeout(host, 100000000U, 0);
+ set_data_timeout(host);
return 0;
}
OMAP_HSMMC_WRITE(host->base, BLK, (req->data->blksz)
| (req->data->blocks << 16));
- set_data_timeout(host, req->data->timeout_ns, req->data->timeout_clks);
+ set_data_timeout(host);
if (host->use_dma) {
ret = omap_hsmmc_start_dma_transfer(host, req);
host->next_data.cookie = 1;
platform_set_drvdata(pdev, host);
- INIT_WORK(&host->mmc_carddetect_work, omap_hsmmc_detect);
mmc->ops = &omap_hsmmc_ops;
omap_hsmmc_context_save(host);
mmc->caps |= MMC_CAP_DISABLE;
+
if (host->pdata->controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ) {
dev_info(&pdev->dev, "multiblock reads disabled due to 35xx erratum 2.1.1.128; MMC read performance may suffer\n");
mmc->caps2 |= MMC_CAP2_NO_MULTI_READ;
/* Request IRQ for card detect */
if ((mmc_slot(host).card_detect_irq)) {
- ret = request_irq(mmc_slot(host).card_detect_irq,
- omap_hsmmc_cd_handler,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- mmc_hostname(mmc), host);
+ ret = request_threaded_irq(mmc_slot(host).card_detect_irq,
+ NULL,
+ omap_hsmmc_detect,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ mmc_hostname(mmc), host);
if (ret) {
dev_dbg(mmc_dev(host->mmc),
"Unable to grab MMC CD IRQ\n");
goto err_slot_name;
}
+ if (host->pdata->controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ) {
+ ret = device_create_file(&mmc->class_dev, &dev_attr_unsafe_read);
+
+ /* MMC_CAP2_NO_MULTI_READ makes it crawl, try a different workaround */
+ mmc->caps2 &= ~MMC_CAP2_NO_MULTI_READ;
+ mmc->max_segs = 1;
+ mmc->f_max = 32000000;
+ }
+
omap_hsmmc_debugfs(mmc);
pm_runtime_mark_last_busy(host->dev);
pm_runtime_put_autosuspend(host->dev);
free_irq(host->irq, host);
if (mmc_slot(host).card_detect_irq)
free_irq(mmc_slot(host).card_detect_irq, host);
- flush_work_sync(&host->mmc_carddetect_work);
pm_runtime_put_sync(host->dev);
pm_runtime_disable(host->dev);
return ret;
}
}
- cancel_work_sync(&host->mmc_carddetect_work);
ret = mmc_suspend_host(host->mmc);
if (ret == 0) {