linux/drivers/mfd/mcp-core.c
<<
>>
Prefs
   1/*
   2 *  linux/drivers/mfd/mcp-core.c
   3 *
   4 *  Copyright (C) 2001 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 *  Generic MCP (Multimedia Communications Port) layer.  All MCP locking
  11 *  is solely held within this file.
  12 */
  13#include <linux/module.h>
  14#include <linux/init.h>
  15#include <linux/errno.h>
  16#include <linux/smp.h>
  17#include <linux/device.h>
  18#include <linux/slab.h>
  19#include <linux/string.h>
  20#include <linux/mfd/mcp.h>
  21
  22
  23#define to_mcp(d)               container_of(d, struct mcp, attached_device)
  24#define to_mcp_driver(d)        container_of(d, struct mcp_driver, drv)
  25
  26static int mcp_bus_match(struct device *dev, struct device_driver *drv)
  27{
  28        return 1;
  29}
  30
  31static int mcp_bus_probe(struct device *dev)
  32{
  33        struct mcp *mcp = to_mcp(dev);
  34        struct mcp_driver *drv = to_mcp_driver(dev->driver);
  35
  36        return drv->probe(mcp);
  37}
  38
  39static int mcp_bus_remove(struct device *dev)
  40{
  41        struct mcp *mcp = to_mcp(dev);
  42        struct mcp_driver *drv = to_mcp_driver(dev->driver);
  43
  44        drv->remove(mcp);
  45        return 0;
  46}
  47
  48static struct bus_type mcp_bus_type = {
  49        .name           = "mcp",
  50        .match          = mcp_bus_match,
  51        .probe          = mcp_bus_probe,
  52        .remove         = mcp_bus_remove,
  53};
  54
  55/**
  56 *      mcp_set_telecom_divisor - set the telecom divisor
  57 *      @mcp: MCP interface structure
  58 *      @div: SIB clock divisor
  59 *
  60 *      Set the telecom divisor on the MCP interface.  The resulting
  61 *      sample rate is SIBCLOCK/div.
  62 */
  63void mcp_set_telecom_divisor(struct mcp *mcp, unsigned int div)
  64{
  65        unsigned long flags;
  66
  67        spin_lock_irqsave(&mcp->lock, flags);
  68        mcp->ops->set_telecom_divisor(mcp, div);
  69        spin_unlock_irqrestore(&mcp->lock, flags);
  70}
  71EXPORT_SYMBOL(mcp_set_telecom_divisor);
  72
  73/**
  74 *      mcp_set_audio_divisor - set the audio divisor
  75 *      @mcp: MCP interface structure
  76 *      @div: SIB clock divisor
  77 *
  78 *      Set the audio divisor on the MCP interface.
  79 */
  80void mcp_set_audio_divisor(struct mcp *mcp, unsigned int div)
  81{
  82        unsigned long flags;
  83
  84        spin_lock_irqsave(&mcp->lock, flags);
  85        mcp->ops->set_audio_divisor(mcp, div);
  86        spin_unlock_irqrestore(&mcp->lock, flags);
  87}
  88EXPORT_SYMBOL(mcp_set_audio_divisor);
  89
  90/**
  91 *      mcp_reg_write - write a device register
  92 *      @mcp: MCP interface structure
  93 *      @reg: 4-bit register index
  94 *      @val: 16-bit data value
  95 *
  96 *      Write a device register.  The MCP interface must be enabled
  97 *      to prevent this function hanging.
  98 */
  99void mcp_reg_write(struct mcp *mcp, unsigned int reg, unsigned int val)
 100{
 101        unsigned long flags;
 102
 103        spin_lock_irqsave(&mcp->lock, flags);
 104        mcp->ops->reg_write(mcp, reg, val);
 105        spin_unlock_irqrestore(&mcp->lock, flags);
 106}
 107EXPORT_SYMBOL(mcp_reg_write);
 108
 109/**
 110 *      mcp_reg_read - read a device register
 111 *      @mcp: MCP interface structure
 112 *      @reg: 4-bit register index
 113 *
 114 *      Read a device register and return its value.  The MCP interface
 115 *      must be enabled to prevent this function hanging.
 116 */
 117unsigned int mcp_reg_read(struct mcp *mcp, unsigned int reg)
 118{
 119        unsigned long flags;
 120        unsigned int val;
 121
 122        spin_lock_irqsave(&mcp->lock, flags);
 123        val = mcp->ops->reg_read(mcp, reg);
 124        spin_unlock_irqrestore(&mcp->lock, flags);
 125
 126        return val;
 127}
 128EXPORT_SYMBOL(mcp_reg_read);
 129
 130/**
 131 *      mcp_enable - enable the MCP interface
 132 *      @mcp: MCP interface to enable
 133 *
 134 *      Enable the MCP interface.  Each call to mcp_enable will need
 135 *      a corresponding call to mcp_disable to disable the interface.
 136 */
 137void mcp_enable(struct mcp *mcp)
 138{
 139        unsigned long flags;
 140
 141        spin_lock_irqsave(&mcp->lock, flags);
 142        if (mcp->use_count++ == 0)
 143                mcp->ops->enable(mcp);
 144        spin_unlock_irqrestore(&mcp->lock, flags);
 145}
 146EXPORT_SYMBOL(mcp_enable);
 147
 148/**
 149 *      mcp_disable - disable the MCP interface
 150 *      @mcp: MCP interface to disable
 151 *
 152 *      Disable the MCP interface.  The MCP interface will only be
 153 *      disabled once the number of calls to mcp_enable matches the
 154 *      number of calls to mcp_disable.
 155 */
 156void mcp_disable(struct mcp *mcp)
 157{
 158        unsigned long flags;
 159
 160        spin_lock_irqsave(&mcp->lock, flags);
 161        if (--mcp->use_count == 0)
 162                mcp->ops->disable(mcp);
 163        spin_unlock_irqrestore(&mcp->lock, flags);
 164}
 165EXPORT_SYMBOL(mcp_disable);
 166
 167static void mcp_release(struct device *dev)
 168{
 169        struct mcp *mcp = container_of(dev, struct mcp, attached_device);
 170
 171        kfree(mcp);
 172}
 173
 174struct mcp *mcp_host_alloc(struct device *parent, size_t size)
 175{
 176        struct mcp *mcp;
 177
 178        mcp = kzalloc(sizeof(struct mcp) + size, GFP_KERNEL);
 179        if (mcp) {
 180                spin_lock_init(&mcp->lock);
 181                device_initialize(&mcp->attached_device);
 182                mcp->attached_device.parent = parent;
 183                mcp->attached_device.bus = &mcp_bus_type;
 184                mcp->attached_device.dma_mask = parent->dma_mask;
 185                mcp->attached_device.release = mcp_release;
 186        }
 187        return mcp;
 188}
 189EXPORT_SYMBOL(mcp_host_alloc);
 190
 191int mcp_host_add(struct mcp *mcp, void *pdata)
 192{
 193        mcp->attached_device.platform_data = pdata;
 194        dev_set_name(&mcp->attached_device, "mcp0");
 195        return device_add(&mcp->attached_device);
 196}
 197EXPORT_SYMBOL(mcp_host_add);
 198
 199void mcp_host_del(struct mcp *mcp)
 200{
 201        device_del(&mcp->attached_device);
 202}
 203EXPORT_SYMBOL(mcp_host_del);
 204
 205void mcp_host_free(struct mcp *mcp)
 206{
 207        put_device(&mcp->attached_device);
 208}
 209EXPORT_SYMBOL(mcp_host_free);
 210
 211int mcp_driver_register(struct mcp_driver *mcpdrv)
 212{
 213        mcpdrv->drv.bus = &mcp_bus_type;
 214        return driver_register(&mcpdrv->drv);
 215}
 216EXPORT_SYMBOL(mcp_driver_register);
 217
 218void mcp_driver_unregister(struct mcp_driver *mcpdrv)
 219{
 220        driver_unregister(&mcpdrv->drv);
 221}
 222EXPORT_SYMBOL(mcp_driver_unregister);
 223
 224static int __init mcp_init(void)
 225{
 226        return bus_register(&mcp_bus_type);
 227}
 228
 229static void __exit mcp_exit(void)
 230{
 231        bus_unregister(&mcp_bus_type);
 232}
 233
 234module_init(mcp_init);
 235module_exit(mcp_exit);
 236
 237MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
 238MODULE_DESCRIPTION("Core multimedia communications port driver");
 239MODULE_LICENSE("GPL");
 240