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