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        spin_lock_irqsave(&mcp->lock, flags);
 141        if (mcp->use_count++ == 0)
 142                mcp->ops->enable(mcp);
 143        spin_unlock_irqrestore(&mcp->lock, flags);
 144}
 145EXPORT_SYMBOL(mcp_enable);
 146
 147/**
 148 *      mcp_disable - disable the MCP interface
 149 *      @mcp: MCP interface to disable
 150 *
 151 *      Disable the MCP interface.  The MCP interface will only be
 152 *      disabled once the number of calls to mcp_enable matches the
 153 *      number of calls to mcp_disable.
 154 */
 155void mcp_disable(struct mcp *mcp)
 156{
 157        unsigned long flags;
 158
 159        spin_lock_irqsave(&mcp->lock, flags);
 160        if (--mcp->use_count == 0)
 161                mcp->ops->disable(mcp);
 162        spin_unlock_irqrestore(&mcp->lock, flags);
 163}
 164EXPORT_SYMBOL(mcp_disable);
 165
 166static void mcp_release(struct device *dev)
 167{
 168        struct mcp *mcp = container_of(dev, struct mcp, attached_device);
 169
 170        kfree(mcp);
 171}
 172
 173struct mcp *mcp_host_alloc(struct device *parent, size_t size)
 174{
 175        struct mcp *mcp;
 176
 177        mcp = kzalloc(sizeof(struct mcp) + size, GFP_KERNEL);
 178        if (mcp) {
 179                spin_lock_init(&mcp->lock);
 180                device_initialize(&mcp->attached_device);
 181                mcp->attached_device.parent = parent;
 182                mcp->attached_device.bus = &mcp_bus_type;
 183                mcp->attached_device.dma_mask = parent->dma_mask;
 184                mcp->attached_device.release = mcp_release;
 185        }
 186        return mcp;
 187}
 188EXPORT_SYMBOL(mcp_host_alloc);
 189
 190int mcp_host_add(struct mcp *mcp, void *pdata)
 191{
 192        mcp->attached_device.platform_data = pdata;
 193        dev_set_name(&mcp->attached_device, "mcp0");
 194        return device_add(&mcp->attached_device);
 195}
 196EXPORT_SYMBOL(mcp_host_add);
 197
 198void mcp_host_del(struct mcp *mcp)
 199{
 200        device_del(&mcp->attached_device);
 201}
 202EXPORT_SYMBOL(mcp_host_del);
 203
 204void mcp_host_free(struct mcp *mcp)
 205{
 206        put_device(&mcp->attached_device);
 207}
 208EXPORT_SYMBOL(mcp_host_free);
 209
 210int mcp_driver_register(struct mcp_driver *mcpdrv)
 211{
 212        mcpdrv->drv.bus = &mcp_bus_type;
 213        return driver_register(&mcpdrv->drv);
 214}
 215EXPORT_SYMBOL(mcp_driver_register);
 216
 217void mcp_driver_unregister(struct mcp_driver *mcpdrv)
 218{
 219        driver_unregister(&mcpdrv->drv);
 220}
 221EXPORT_SYMBOL(mcp_driver_unregister);
 222
 223static int __init mcp_init(void)
 224{
 225        return bus_register(&mcp_bus_type);
 226}
 227
 228static void __exit mcp_exit(void)
 229{
 230        bus_unregister(&mcp_bus_type);
 231}
 232
 233module_init(mcp_init);
 234module_exit(mcp_exit);
 235
 236MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
 237MODULE_DESCRIPTION("Core multimedia communications port driver");
 238MODULE_LICENSE("GPL");
 239
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.