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