linux/drivers/mfd/mcp-sa11x0.c
<<
>>
Prefs
   1/*
   2 *  linux/drivers/mfd/mcp-sa11x0.c
   3 *
   4 *  Copyright (C) 2001-2005 Russell King
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License.
   9 *
  10 *  SA11x0 MCP (Multimedia Communications Port) driver.
  11 *
  12 *  MCP read/write timeouts from Jordi Colomer, rehacked by rmk.
  13 */
  14#include <linux/module.h>
  15#include <linux/init.h>
  16#include <linux/io.h>
  17#include <linux/errno.h>
  18#include <linux/kernel.h>
  19#include <linux/delay.h>
  20#include <linux/spinlock.h>
  21#include <linux/platform_device.h>
  22#include <linux/pm.h>
  23#include <linux/mfd/mcp.h>
  24
  25#include <mach/hardware.h>
  26#include <asm/mach-types.h>
  27#include <mach/mcp.h>
  28
  29#define DRIVER_NAME "sa11x0-mcp"
  30
  31struct mcp_sa11x0 {
  32        void __iomem    *base0;
  33        void __iomem    *base1;
  34        u32             mccr0;
  35        u32             mccr1;
  36};
  37
  38/* Register offsets */
  39#define MCCR0(m)        ((m)->base0 + 0x00)
  40#define MCDR0(m)        ((m)->base0 + 0x08)
  41#define MCDR1(m)        ((m)->base0 + 0x0c)
  42#define MCDR2(m)        ((m)->base0 + 0x10)
  43#define MCSR(m)         ((m)->base0 + 0x18)
  44#define MCCR1(m)        ((m)->base1 + 0x00)
  45
  46#define priv(mcp)       ((struct mcp_sa11x0 *)mcp_priv(mcp))
  47
  48static void
  49mcp_sa11x0_set_telecom_divisor(struct mcp *mcp, unsigned int divisor)
  50{
  51        struct mcp_sa11x0 *m = priv(mcp);
  52
  53        divisor /= 32;
  54
  55        m->mccr0 &= ~0x00007f00;
  56        m->mccr0 |= divisor << 8;
  57        writel_relaxed(m->mccr0, MCCR0(m));
  58}
  59
  60static void
  61mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor)
  62{
  63        struct mcp_sa11x0 *m = priv(mcp);
  64
  65        divisor /= 32;
  66
  67        m->mccr0 &= ~0x0000007f;
  68        m->mccr0 |= divisor;
  69        writel_relaxed(m->mccr0, MCCR0(m));
  70}
  71
  72/*
  73 * Write data to the device.  The bit should be set after 3 subframe
  74 * times (each frame is 64 clocks).  We wait a maximum of 6 subframes.
  75 * We really should try doing something more productive while we
  76 * wait.
  77 */
  78static void
  79mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val)
  80{
  81        struct mcp_sa11x0 *m = priv(mcp);
  82        int ret = -ETIME;
  83        int i;
  84
  85        writel_relaxed(reg << 17 | MCDR2_Wr | (val & 0xffff), MCDR2(m));
  86
  87        for (i = 0; i < 2; i++) {
  88                udelay(mcp->rw_timeout);
  89                if (readl_relaxed(MCSR(m)) & MCSR_CWC) {
  90                        ret = 0;
  91                        break;
  92                }
  93        }
  94
  95        if (ret < 0)
  96                printk(KERN_WARNING "mcp: write timed out\n");
  97}
  98
  99/*
 100 * Read data from the device.  The bit should be set after 3 subframe
 101 * times (each frame is 64 clocks).  We wait a maximum of 6 subframes.
 102 * We really should try doing something more productive while we
 103 * wait.
 104 */
 105static unsigned int
 106mcp_sa11x0_read(struct mcp *mcp, unsigned int reg)
 107{
 108        struct mcp_sa11x0 *m = priv(mcp);
 109        int ret = -ETIME;
 110        int i;
 111
 112        writel_relaxed(reg << 17 | MCDR2_Rd, MCDR2(m));
 113
 114        for (i = 0; i < 2; i++) {
 115                udelay(mcp->rw_timeout);
 116                if (readl_relaxed(MCSR(m)) & MCSR_CRC) {
 117                        ret = readl_relaxed(MCDR2(m)) & 0xffff;
 118                        break;
 119                }
 120        }
 121
 122        if (ret < 0)
 123                printk(KERN_WARNING "mcp: read timed out\n");
 124
 125        return ret;
 126}
 127
 128static void mcp_sa11x0_enable(struct mcp *mcp)
 129{
 130        struct mcp_sa11x0 *m = priv(mcp);
 131
 132        writel(-1, MCSR(m));
 133        m->mccr0 |= MCCR0_MCE;
 134        writel_relaxed(m->mccr0, MCCR0(m));
 135}
 136
 137static void mcp_sa11x0_disable(struct mcp *mcp)
 138{
 139        struct mcp_sa11x0 *m = priv(mcp);
 140
 141        m->mccr0 &= ~MCCR0_MCE;
 142        writel_relaxed(m->mccr0, MCCR0(m));
 143}
 144
 145/*
 146 * Our methods.
 147 */
 148static struct mcp_ops mcp_sa11x0 = {
 149        .set_telecom_divisor    = mcp_sa11x0_set_telecom_divisor,
 150        .set_audio_divisor      = mcp_sa11x0_set_audio_divisor,
 151        .reg_write              = mcp_sa11x0_write,
 152        .reg_read               = mcp_sa11x0_read,
 153        .enable                 = mcp_sa11x0_enable,
 154        .disable                = mcp_sa11x0_disable,
 155};
 156
 157static int mcp_sa11x0_probe(struct platform_device *dev)
 158{
 159        struct mcp_plat_data *data = dev->dev.platform_data;
 160        struct resource *mem0, *mem1;
 161        struct mcp_sa11x0 *m;
 162        struct mcp *mcp;
 163        int ret;
 164
 165        if (!data)
 166                return -ENODEV;
 167
 168        mem0 = platform_get_resource(dev, IORESOURCE_MEM, 0);
 169        mem1 = platform_get_resource(dev, IORESOURCE_MEM, 1);
 170        if (!mem0 || !mem1)
 171                return -ENXIO;
 172
 173        if (!request_mem_region(mem0->start, resource_size(mem0),
 174                                DRIVER_NAME)) {
 175                ret = -EBUSY;
 176                goto err_mem0;
 177        }
 178
 179        if (!request_mem_region(mem1->start, resource_size(mem1),
 180                                DRIVER_NAME)) {
 181                ret = -EBUSY;
 182                goto err_mem1;
 183        }
 184
 185        mcp = mcp_host_alloc(&dev->dev, sizeof(struct mcp_sa11x0));
 186        if (!mcp) {
 187                ret = -ENOMEM;
 188                goto err_alloc;
 189        }
 190
 191        mcp->owner              = THIS_MODULE;
 192        mcp->ops                = &mcp_sa11x0;
 193        mcp->sclk_rate          = data->sclk_rate;
 194
 195        m = priv(mcp);
 196        m->mccr0 = data->mccr0 | 0x7f7f;
 197        m->mccr1 = data->mccr1;
 198
 199        m->base0 = ioremap(mem0->start, resource_size(mem0));
 200        m->base1 = ioremap(mem1->start, resource_size(mem1));
 201        if (!m->base0 || !m->base1) {
 202                ret = -ENOMEM;
 203                goto err_ioremap;
 204        }
 205
 206        platform_set_drvdata(dev, mcp);
 207
 208        /*
 209         * Initialise device.  Note that we initially
 210         * set the sampling rate to minimum.
 211         */
 212        writel_relaxed(-1, MCSR(m));
 213        writel_relaxed(m->mccr1, MCCR1(m));
 214        writel_relaxed(m->mccr0, MCCR0(m));
 215
 216        /*
 217         * Calculate the read/write timeout (us) from the bit clock
 218         * rate.  This is the period for 3 64-bit frames.  Always
 219         * round this time up.
 220         */
 221        mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) /
 222                          mcp->sclk_rate;
 223
 224        ret = mcp_host_add(mcp, data->codec_pdata);
 225        if (ret == 0)
 226                return 0;
 227
 228        platform_set_drvdata(dev, NULL);
 229
 230 err_ioremap:
 231        iounmap(m->base1);
 232        iounmap(m->base0);
 233        mcp_host_free(mcp);
 234 err_alloc:
 235        release_mem_region(mem1->start, resource_size(mem1));
 236 err_mem1:
 237        release_mem_region(mem0->start, resource_size(mem0));
 238 err_mem0:
 239        return ret;
 240}
 241
 242static int mcp_sa11x0_remove(struct platform_device *dev)
 243{
 244        struct mcp *mcp = platform_get_drvdata(dev);
 245        struct mcp_sa11x0 *m = priv(mcp);
 246        struct resource *mem0, *mem1;
 247
 248        if (m->mccr0 & MCCR0_MCE)
 249                dev_warn(&dev->dev,
 250                         "device left active (missing disable call?)\n");
 251
 252        mem0 = platform_get_resource(dev, IORESOURCE_MEM, 0);
 253        mem1 = platform_get_resource(dev, IORESOURCE_MEM, 1);
 254
 255        platform_set_drvdata(dev, NULL);
 256        mcp_host_del(mcp);
 257        iounmap(m->base1);
 258        iounmap(m->base0);
 259        mcp_host_free(mcp);
 260        release_mem_region(mem1->start, resource_size(mem1));
 261        release_mem_region(mem0->start, resource_size(mem0));
 262
 263        return 0;
 264}
 265
 266#ifdef CONFIG_PM_SLEEP
 267static int mcp_sa11x0_suspend(struct device *dev)
 268{
 269        struct mcp_sa11x0 *m = priv(dev_get_drvdata(dev));
 270
 271        if (m->mccr0 & MCCR0_MCE)
 272                dev_warn(dev, "device left active (missing disable call?)\n");
 273
 274        writel(m->mccr0 & ~MCCR0_MCE, MCCR0(m));
 275
 276        return 0;
 277}
 278
 279static int mcp_sa11x0_resume(struct device *dev)
 280{
 281        struct mcp_sa11x0 *m = priv(dev_get_drvdata(dev));
 282
 283        writel_relaxed(m->mccr1, MCCR1(m));
 284        writel_relaxed(m->mccr0, MCCR0(m));
 285
 286        return 0;
 287}
 288#endif
 289
 290static const struct dev_pm_ops mcp_sa11x0_pm_ops = {
 291#ifdef CONFIG_PM_SLEEP
 292        .suspend = mcp_sa11x0_suspend,
 293        .freeze = mcp_sa11x0_suspend,
 294        .poweroff = mcp_sa11x0_suspend,
 295        .resume_noirq = mcp_sa11x0_resume,
 296        .thaw_noirq = mcp_sa11x0_resume,
 297        .restore_noirq = mcp_sa11x0_resume,
 298#endif
 299};
 300
 301static struct platform_driver mcp_sa11x0_driver = {
 302        .probe          = mcp_sa11x0_probe,
 303        .remove         = mcp_sa11x0_remove,
 304        .driver         = {
 305                .name   = DRIVER_NAME,
 306                .owner  = THIS_MODULE,
 307                .pm     = &mcp_sa11x0_pm_ops,
 308        },
 309};
 310
 311/*
 312 * This needs re-working
 313 */
 314module_platform_driver(mcp_sa11x0_driver);
 315
 316MODULE_ALIAS("platform:" DRIVER_NAME);
 317MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
 318MODULE_DESCRIPTION("SA11x0 multimedia communications port driver");
 319MODULE_LICENSE("GPL");
 320
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.