e6ee7182c3
Add runtime and system-wide power management to the TMIO MMC driver in PIO and DMA modes, allowing it to properly save and restore its state during system suspend. Runtime PM is very crude ATM, because the controller has to be powered on all the time to detect card hotplug events. Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> Signed-off-by: Chris Ball <cjb@laptop.org>
145 lines
3.1 KiB
C
145 lines
3.1 KiB
C
/*
|
|
* linux/drivers/mmc/host/tmio_mmc.c
|
|
*
|
|
* Copyright (C) 2007 Ian Molton
|
|
* Copyright (C) 2004 Ian Molton
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* Driver for the MMC / SD / SDIO cell found in:
|
|
*
|
|
* TC6393XB TC6391XB TC6387XB T7L66XB ASIC3
|
|
*/
|
|
|
|
#include <linux/device.h>
|
|
#include <linux/mfd/core.h>
|
|
#include <linux/mfd/tmio.h>
|
|
#include <linux/mmc/host.h>
|
|
#include <linux/module.h>
|
|
#include <linux/pagemap.h>
|
|
#include <linux/scatterlist.h>
|
|
|
|
#include "tmio_mmc.h"
|
|
|
|
#ifdef CONFIG_PM
|
|
static int tmio_mmc_suspend(struct platform_device *dev, pm_message_t state)
|
|
{
|
|
const struct mfd_cell *cell = mfd_get_cell(dev);
|
|
struct mmc_host *mmc = platform_get_drvdata(dev);
|
|
int ret;
|
|
|
|
ret = tmio_mmc_host_suspend(&dev->dev);
|
|
|
|
/* Tell MFD core it can disable us now.*/
|
|
if (!ret && cell->disable)
|
|
cell->disable(dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tmio_mmc_resume(struct platform_device *dev)
|
|
{
|
|
const struct mfd_cell *cell = mfd_get_cell(dev);
|
|
struct mmc_host *mmc = platform_get_drvdata(dev);
|
|
int ret = 0;
|
|
|
|
/* Tell the MFD core we are ready to be enabled */
|
|
if (cell->resume)
|
|
ret = cell->resume(dev);
|
|
|
|
if (!ret)
|
|
ret = tmio_mmc_host_resume(&dev->dev);
|
|
|
|
return ret;
|
|
}
|
|
#else
|
|
#define tmio_mmc_suspend NULL
|
|
#define tmio_mmc_resume NULL
|
|
#endif
|
|
|
|
static int __devinit tmio_mmc_probe(struct platform_device *pdev)
|
|
{
|
|
const struct mfd_cell *cell = mfd_get_cell(pdev);
|
|
struct tmio_mmc_data *pdata;
|
|
struct tmio_mmc_host *host;
|
|
int ret = -EINVAL;
|
|
|
|
if (pdev->num_resources != 2)
|
|
goto out;
|
|
|
|
pdata = mfd_get_data(pdev);
|
|
if (!pdata || !pdata->hclk)
|
|
goto out;
|
|
|
|
/* Tell the MFD core we are ready to be enabled */
|
|
if (cell->enable) {
|
|
ret = cell->enable(pdev);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
ret = tmio_mmc_host_probe(&host, pdev, pdata);
|
|
if (ret)
|
|
goto cell_disable;
|
|
|
|
pr_info("%s at 0x%08lx irq %d\n", mmc_hostname(host->mmc),
|
|
(unsigned long)host->ctl, host->irq);
|
|
|
|
return 0;
|
|
|
|
cell_disable:
|
|
if (cell->disable)
|
|
cell->disable(pdev);
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int __devexit tmio_mmc_remove(struct platform_device *pdev)
|
|
{
|
|
const struct mfd_cell *cell = mfd_get_cell(pdev);
|
|
struct mmc_host *mmc = platform_get_drvdata(pdev);
|
|
|
|
platform_set_drvdata(pdev, NULL);
|
|
|
|
if (mmc) {
|
|
tmio_mmc_host_remove(mmc_priv(mmc));
|
|
if (cell->disable)
|
|
cell->disable(pdev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ------------------- device registration ----------------------- */
|
|
|
|
static struct platform_driver tmio_mmc_driver = {
|
|
.driver = {
|
|
.name = "tmio-mmc",
|
|
.owner = THIS_MODULE,
|
|
},
|
|
.probe = tmio_mmc_probe,
|
|
.remove = __devexit_p(tmio_mmc_remove),
|
|
.suspend = tmio_mmc_suspend,
|
|
.resume = tmio_mmc_resume,
|
|
};
|
|
|
|
|
|
static int __init tmio_mmc_init(void)
|
|
{
|
|
return platform_driver_register(&tmio_mmc_driver);
|
|
}
|
|
|
|
static void __exit tmio_mmc_exit(void)
|
|
{
|
|
platform_driver_unregister(&tmio_mmc_driver);
|
|
}
|
|
|
|
module_init(tmio_mmc_init);
|
|
module_exit(tmio_mmc_exit);
|
|
|
|
MODULE_DESCRIPTION("Toshiba TMIO SD/MMC driver");
|
|
MODULE_AUTHOR("Ian Molton <spyro@f2s.com>");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_ALIAS("platform:tmio-mmc");
|