linux/drivers/mfd/tc6387xb.c
<<
>>
Prefs
   1/*
   2 * Toshiba TC6387XB support
   3 * Copyright (c) 2005 Ian Molton
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License version 2 as
   7 * published by the Free Software Foundation.
   8 *
   9 * This file contains TC6387XB base support.
  10 *
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/platform_device.h>
  15#include <linux/clk.h>
  16#include <linux/err.h>
  17#include <linux/mfd/core.h>
  18#include <linux/mfd/tmio.h>
  19#include <linux/mfd/tc6387xb.h>
  20#include <linux/slab.h>
  21
  22enum {
  23        TC6387XB_CELL_MMC,
  24};
  25
  26struct tc6387xb {
  27        void __iomem *scr;
  28        struct clk *clk32k;
  29        struct resource rscr;
  30};
  31
  32static struct resource tc6387xb_mmc_resources[] = {
  33        {
  34                .start = 0x800,
  35                .end   = 0x9ff,
  36                .flags = IORESOURCE_MEM,
  37        },
  38        {
  39                .start = 0,
  40                .end   = 0,
  41                .flags = IORESOURCE_IRQ,
  42        },
  43};
  44
  45/*--------------------------------------------------------------------------*/
  46
  47#ifdef CONFIG_PM
  48static int tc6387xb_suspend(struct platform_device *dev, pm_message_t state)
  49{
  50        struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
  51        struct tc6387xb_platform_data *pdata = dev->dev.platform_data;
  52
  53        if (pdata && pdata->suspend)
  54                pdata->suspend(dev);
  55        clk_disable(tc6387xb->clk32k);
  56
  57        return 0;
  58}
  59
  60static int tc6387xb_resume(struct platform_device *dev)
  61{
  62        struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
  63        struct tc6387xb_platform_data *pdata = dev->dev.platform_data;
  64
  65        clk_enable(tc6387xb->clk32k);
  66        if (pdata && pdata->resume)
  67                pdata->resume(dev);
  68
  69        tmio_core_mmc_resume(tc6387xb->scr + 0x200, 0,
  70                tc6387xb_mmc_resources[0].start & 0xfffe);
  71
  72        return 0;
  73}
  74#else
  75#define tc6387xb_suspend  NULL
  76#define tc6387xb_resume   NULL
  77#endif
  78
  79/*--------------------------------------------------------------------------*/
  80
  81static void tc6387xb_mmc_pwr(struct platform_device *mmc, int state)
  82{
  83        struct platform_device *dev = to_platform_device(mmc->dev.parent);
  84        struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
  85
  86        tmio_core_mmc_pwr(tc6387xb->scr + 0x200, 0, state);
  87}
  88
  89static void tc6387xb_mmc_clk_div(struct platform_device *mmc, int state)
  90{
  91        struct platform_device *dev = to_platform_device(mmc->dev.parent);
  92        struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
  93
  94        tmio_core_mmc_clk_div(tc6387xb->scr + 0x200, 0, state);
  95}
  96
  97
  98static int tc6387xb_mmc_enable(struct platform_device *mmc)
  99{
 100        struct platform_device *dev      = to_platform_device(mmc->dev.parent);
 101        struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
 102
 103        clk_enable(tc6387xb->clk32k);
 104
 105        tmio_core_mmc_enable(tc6387xb->scr + 0x200, 0,
 106                tc6387xb_mmc_resources[0].start & 0xfffe);
 107
 108        return 0;
 109}
 110
 111static int tc6387xb_mmc_disable(struct platform_device *mmc)
 112{
 113        struct platform_device *dev      = to_platform_device(mmc->dev.parent);
 114        struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
 115
 116        clk_disable(tc6387xb->clk32k);
 117
 118        return 0;
 119}
 120
 121static struct tmio_mmc_data tc6387xb_mmc_data = {
 122        .hclk = 24000000,
 123        .set_pwr = tc6387xb_mmc_pwr,
 124        .set_clk_div = tc6387xb_mmc_clk_div,
 125};
 126
 127/*--------------------------------------------------------------------------*/
 128
 129static struct mfd_cell tc6387xb_cells[] = {
 130        [TC6387XB_CELL_MMC] = {
 131                .name = "tmio-mmc",
 132                .enable = tc6387xb_mmc_enable,
 133                .disable = tc6387xb_mmc_disable,
 134                .mfd_data = &tc6387xb_mmc_data,
 135                .num_resources = ARRAY_SIZE(tc6387xb_mmc_resources),
 136                .resources = tc6387xb_mmc_resources,
 137        },
 138};
 139
 140static int __devinit tc6387xb_probe(struct platform_device *dev)
 141{
 142        struct tc6387xb_platform_data *pdata = dev->dev.platform_data;
 143        struct resource *iomem, *rscr;
 144        struct clk *clk32k;
 145        struct tc6387xb *tc6387xb;
 146        int irq, ret;
 147
 148        iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
 149        if (!iomem) {
 150                return -EINVAL;
 151        }
 152
 153        tc6387xb = kzalloc(sizeof *tc6387xb, GFP_KERNEL);
 154        if (!tc6387xb)
 155                return -ENOMEM;
 156
 157        ret  = platform_get_irq(dev, 0);
 158        if (ret >= 0)
 159                irq = ret;
 160        else
 161                goto err_no_irq;
 162
 163        clk32k = clk_get(&dev->dev, "CLK_CK32K");
 164        if (IS_ERR(clk32k)) {
 165                ret = PTR_ERR(clk32k);
 166                goto err_no_clk;
 167        }
 168
 169        rscr = &tc6387xb->rscr;
 170        rscr->name = "tc6387xb-core";
 171        rscr->start = iomem->start;
 172        rscr->end = iomem->start + 0xff;
 173        rscr->flags = IORESOURCE_MEM;
 174
 175        ret = request_resource(iomem, rscr);
 176        if (ret)
 177                goto err_resource;
 178
 179        tc6387xb->scr = ioremap(rscr->start, rscr->end - rscr->start + 1);
 180        if (!tc6387xb->scr) {
 181                ret = -ENOMEM;
 182                goto err_ioremap;
 183        }
 184
 185        tc6387xb->clk32k = clk32k;
 186        platform_set_drvdata(dev, tc6387xb);
 187
 188        if (pdata && pdata->enable)
 189                pdata->enable(dev);
 190
 191        printk(KERN_INFO "Toshiba tc6387xb initialised\n");
 192
 193        ret = mfd_add_devices(&dev->dev, dev->id, tc6387xb_cells,
 194                              ARRAY_SIZE(tc6387xb_cells), iomem, irq);
 195
 196        if (!ret)
 197                return 0;
 198
 199        iounmap(tc6387xb->scr);
 200err_ioremap:
 201        release_resource(&tc6387xb->rscr);
 202err_resource:
 203        clk_put(clk32k);
 204err_no_clk:
 205err_no_irq:
 206        kfree(tc6387xb);
 207        return ret;
 208}
 209
 210static int __devexit tc6387xb_remove(struct platform_device *dev)
 211{
 212        struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
 213
 214        mfd_remove_devices(&dev->dev);
 215        iounmap(tc6387xb->scr);
 216        release_resource(&tc6387xb->rscr);
 217        clk_disable(tc6387xb->clk32k);
 218        clk_put(tc6387xb->clk32k);
 219        platform_set_drvdata(dev, NULL);
 220        kfree(tc6387xb);
 221
 222        return 0;
 223}
 224
 225
 226static struct platform_driver tc6387xb_platform_driver = {
 227        .driver = {
 228                .name           = "tc6387xb",
 229        },
 230        .probe          = tc6387xb_probe,
 231        .remove         = __devexit_p(tc6387xb_remove),
 232        .suspend        = tc6387xb_suspend,
 233        .resume         = tc6387xb_resume,
 234};
 235
 236
 237static int __init tc6387xb_init(void)
 238{
 239        return platform_driver_register(&tc6387xb_platform_driver);
 240}
 241
 242static void __exit tc6387xb_exit(void)
 243{
 244        platform_driver_unregister(&tc6387xb_platform_driver);
 245}
 246
 247module_init(tc6387xb_init);
 248module_exit(tc6387xb_exit);
 249
 250MODULE_DESCRIPTION("Toshiba TC6387XB core driver");
 251MODULE_LICENSE("GPL v2");
 252MODULE_AUTHOR("Ian Molton");
 253MODULE_ALIAS("platform:tc6387xb");
 254
 255