Merge HEAD from master.kernel.org:/home/rmk/linux-2.6-ucb.git
This commit is contained in:
commit
2321fbd2b8
|
@ -752,6 +752,8 @@ source "drivers/hwmon/Kconfig"
|
|||
|
||||
source "drivers/misc/Kconfig"
|
||||
|
||||
source "drivers/mfd/Kconfig"
|
||||
|
||||
source "drivers/media/Kconfig"
|
||||
|
||||
source "drivers/video/Kconfig"
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <asm/mach/map.h>
|
||||
#include <asm/mach/serial_sa1100.h>
|
||||
#include <asm/arch/assabet.h>
|
||||
#include <asm/arch/mcp.h>
|
||||
|
||||
#include "generic.h"
|
||||
|
||||
|
@ -198,6 +199,11 @@ static struct irda_platform_data assabet_irda_data = {
|
|||
.set_speed = assabet_irda_set_speed,
|
||||
};
|
||||
|
||||
static struct mcp_plat_data assabet_mcp_data = {
|
||||
.mccr0 = MCCR0_ADM,
|
||||
.sclk_rate = 11981000,
|
||||
};
|
||||
|
||||
static void __init assabet_init(void)
|
||||
{
|
||||
/*
|
||||
|
@ -246,6 +252,7 @@ static void __init assabet_init(void)
|
|||
sa11x0_set_flash_data(&assabet_flash_data, assabet_flash_resources,
|
||||
ARRAY_SIZE(assabet_flash_resources));
|
||||
sa11x0_set_irda_data(&assabet_irda_data);
|
||||
sa11x0_set_mcp_data(&assabet_mcp_data);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <asm/mach/serial_sa1100.h>
|
||||
|
||||
#include <asm/arch/cerf.h>
|
||||
#include <asm/arch/mcp.h>
|
||||
#include "generic.h"
|
||||
|
||||
static struct resource cerfuart2_resources[] = {
|
||||
|
@ -116,10 +117,16 @@ static void __init cerf_map_io(void)
|
|||
GPDR |= CERF_GPIO_CF_RESET;
|
||||
}
|
||||
|
||||
static struct mcp_plat_data cerf_mcp_data = {
|
||||
.mccr0 = MCCR0_ADM,
|
||||
.sclk_rate = 11981000,
|
||||
};
|
||||
|
||||
static void __init cerf_init(void)
|
||||
{
|
||||
platform_add_devices(cerf_devices, ARRAY_SIZE(cerf_devices));
|
||||
sa11x0_set_flash_data(&cerf_flash_data, &cerf_flash_resource, 1);
|
||||
sa11x0_set_mcp_data(&cerf_mcp_data);
|
||||
}
|
||||
|
||||
MACHINE_START(CERF, "Intrinsyc CerfBoard/CerfCube")
|
||||
|
|
|
@ -221,6 +221,11 @@ static struct platform_device sa11x0mcp_device = {
|
|||
.resource = sa11x0mcp_resources,
|
||||
};
|
||||
|
||||
void sa11x0_set_mcp_data(struct mcp_plat_data *data)
|
||||
{
|
||||
sa11x0mcp_device.dev.platform_data = data;
|
||||
}
|
||||
|
||||
static struct resource sa11x0ssp_resources[] = {
|
||||
[0] = {
|
||||
.start = 0x80070000,
|
||||
|
|
|
@ -34,5 +34,8 @@ struct resource;
|
|||
extern void sa11x0_set_flash_data(struct flash_platform_data *flash,
|
||||
struct resource *res, int nr);
|
||||
|
||||
struct sa11x0_ssp_plat_ops;
|
||||
extern void sa11x0_set_ssp_data(struct sa11x0_ssp_plat_ops *ops);
|
||||
|
||||
struct irda_platform_data;
|
||||
void sa11x0_set_irda_data(struct irda_platform_data *irda);
|
||||
|
|
|
@ -13,12 +13,23 @@
|
|||
#include <asm/mach/arch.h>
|
||||
#include <asm/mach/map.h>
|
||||
#include <asm/mach/serial_sa1100.h>
|
||||
#include <asm/arch/mcp.h>
|
||||
|
||||
#include "generic.h"
|
||||
|
||||
|
||||
#warning "include/asm/arch-sa1100/ide.h needs fixing for lart"
|
||||
|
||||
static struct mcp_plat_data lart_mcp_data = {
|
||||
.mccr0 = MCCR0_ADM,
|
||||
.sclk_rate = 11981000,
|
||||
};
|
||||
|
||||
static void __init lart_init(void)
|
||||
{
|
||||
sa11x0_set_mcp_data(&lart_mcp_data);
|
||||
}
|
||||
|
||||
static struct map_desc lart_io_desc[] __initdata = {
|
||||
/* virtual physical length type */
|
||||
{ 0xe8000000, 0x00000000, 0x00400000, MT_DEVICE }, /* main flash memory */
|
||||
|
@ -47,5 +58,6 @@ MACHINE_START(LART, "LART")
|
|||
.boot_params = 0xc0000100,
|
||||
.map_io = lart_map_io,
|
||||
.init_irq = sa1100_init_irq,
|
||||
.init_machine = lart_init,
|
||||
.timer = &sa1100_timer,
|
||||
MACHINE_END
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <asm/mach/flash.h>
|
||||
#include <asm/mach/map.h>
|
||||
#include <asm/mach/serial_sa1100.h>
|
||||
#include <asm/arch/mcp.h>
|
||||
#include <asm/arch/shannon.h>
|
||||
|
||||
#include "generic.h"
|
||||
|
@ -52,9 +53,15 @@ static struct resource shannon_flash_resource = {
|
|||
.flags = IORESOURCE_MEM,
|
||||
};
|
||||
|
||||
static struct mcp_plat_data shannon_mcp_data = {
|
||||
.mccr0 = MCCR0_ADM,
|
||||
.sclk_rate = 11981000,
|
||||
};
|
||||
|
||||
static void __init shannon_init(void)
|
||||
{
|
||||
sa11x0_set_flash_data(&shannon_flash_data, &shannon_flash_resource, 1);
|
||||
sa11x0_set_mcp_data(&shannon_mcp_data);
|
||||
}
|
||||
|
||||
static void __init shannon_map_io(void)
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <asm/mach/flash.h>
|
||||
#include <asm/mach/map.h>
|
||||
#include <asm/mach/serial_sa1100.h>
|
||||
#include <asm/arch/mcp.h>
|
||||
#include <asm/arch/simpad.h>
|
||||
|
||||
#include <linux/serial_core.h>
|
||||
|
@ -123,6 +124,11 @@ static struct resource simpad_flash_resources [] = {
|
|||
}
|
||||
};
|
||||
|
||||
static struct mcp_plat_data simpad_mcp_data = {
|
||||
.mccr0 = MCCR0_ADM,
|
||||
.sclk_rate = 11981000,
|
||||
};
|
||||
|
||||
|
||||
|
||||
static void __init simpad_map_io(void)
|
||||
|
@ -157,6 +163,7 @@ static void __init simpad_map_io(void)
|
|||
|
||||
sa11x0_set_flash_data(&simpad_flash_data, simpad_flash_resources,
|
||||
ARRAY_SIZE(simpad_flash_resources));
|
||||
sa11x0_set_mcp_data(&simpad_mcp_data);
|
||||
}
|
||||
|
||||
static void simpad_power_off(void)
|
||||
|
|
|
@ -48,6 +48,8 @@ source "drivers/hwmon/Kconfig"
|
|||
|
||||
source "drivers/misc/Kconfig"
|
||||
|
||||
source "drivers/mfd/Kconfig"
|
||||
|
||||
source "drivers/media/Kconfig"
|
||||
|
||||
source "drivers/video/Kconfig"
|
||||
|
|
|
@ -26,7 +26,7 @@ obj-$(CONFIG_FB_INTEL) += video/intelfb/
|
|||
obj-$(CONFIG_SERIO) += input/serio/
|
||||
obj-y += serial/
|
||||
obj-$(CONFIG_PARPORT) += parport/
|
||||
obj-y += base/ block/ misc/ net/ media/
|
||||
obj-y += base/ block/ misc/ mfd/ net/ media/
|
||||
obj-$(CONFIG_NUBUS) += nubus/
|
||||
obj-$(CONFIG_ATM) += atm/
|
||||
obj-$(CONFIG_PPC_PMAC) += macintosh/
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
#
|
||||
# Multifunction miscellaneous devices
|
||||
#
|
||||
|
||||
menu "Multimedia Capabilities Port drivers"
|
||||
|
||||
config MCP
|
||||
tristate
|
||||
|
||||
# Interface drivers
|
||||
config MCP_SA11X0
|
||||
tristate "Support SA11x0 MCP interface"
|
||||
depends on ARCH_SA1100
|
||||
select MCP
|
||||
|
||||
endmenu
|
|
@ -0,0 +1,6 @@
|
|||
#
|
||||
# Makefile for multifunction miscellaneous devices
|
||||
#
|
||||
|
||||
obj-$(CONFIG_MCP) += mcp-core.o
|
||||
obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o
|
|
@ -0,0 +1,255 @@
|
|||
/*
|
||||
* linux/drivers/mfd/mcp-core.c
|
||||
*
|
||||
* Copyright (C) 2001 Russell King
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License.
|
||||
*
|
||||
* Generic MCP (Multimedia Communications Port) layer. All MCP locking
|
||||
* is solely held within this file.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <asm/dma.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
#include "mcp.h"
|
||||
|
||||
#define to_mcp(d) container_of(d, struct mcp, attached_device)
|
||||
#define to_mcp_driver(d) container_of(d, struct mcp_driver, drv)
|
||||
|
||||
static int mcp_bus_match(struct device *dev, struct device_driver *drv)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int mcp_bus_probe(struct device *dev)
|
||||
{
|
||||
struct mcp *mcp = to_mcp(dev);
|
||||
struct mcp_driver *drv = to_mcp_driver(dev->driver);
|
||||
|
||||
return drv->probe(mcp);
|
||||
}
|
||||
|
||||
static int mcp_bus_remove(struct device *dev)
|
||||
{
|
||||
struct mcp *mcp = to_mcp(dev);
|
||||
struct mcp_driver *drv = to_mcp_driver(dev->driver);
|
||||
|
||||
drv->remove(mcp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcp_bus_suspend(struct device *dev, pm_message_t state)
|
||||
{
|
||||
struct mcp *mcp = to_mcp(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (dev->driver) {
|
||||
struct mcp_driver *drv = to_mcp_driver(dev->driver);
|
||||
|
||||
ret = drv->suspend(mcp, state);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp_bus_resume(struct device *dev)
|
||||
{
|
||||
struct mcp *mcp = to_mcp(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (dev->driver) {
|
||||
struct mcp_driver *drv = to_mcp_driver(dev->driver);
|
||||
|
||||
ret = drv->resume(mcp);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct bus_type mcp_bus_type = {
|
||||
.name = "mcp",
|
||||
.match = mcp_bus_match,
|
||||
.suspend = mcp_bus_suspend,
|
||||
.resume = mcp_bus_resume,
|
||||
};
|
||||
|
||||
/**
|
||||
* mcp_set_telecom_divisor - set the telecom divisor
|
||||
* @mcp: MCP interface structure
|
||||
* @div: SIB clock divisor
|
||||
*
|
||||
* Set the telecom divisor on the MCP interface. The resulting
|
||||
* sample rate is SIBCLOCK/div.
|
||||
*/
|
||||
void mcp_set_telecom_divisor(struct mcp *mcp, unsigned int div)
|
||||
{
|
||||
spin_lock_irq(&mcp->lock);
|
||||
mcp->ops->set_telecom_divisor(mcp, div);
|
||||
spin_unlock_irq(&mcp->lock);
|
||||
}
|
||||
EXPORT_SYMBOL(mcp_set_telecom_divisor);
|
||||
|
||||
/**
|
||||
* mcp_set_audio_divisor - set the audio divisor
|
||||
* @mcp: MCP interface structure
|
||||
* @div: SIB clock divisor
|
||||
*
|
||||
* Set the audio divisor on the MCP interface.
|
||||
*/
|
||||
void mcp_set_audio_divisor(struct mcp *mcp, unsigned int div)
|
||||
{
|
||||
spin_lock_irq(&mcp->lock);
|
||||
mcp->ops->set_audio_divisor(mcp, div);
|
||||
spin_unlock_irq(&mcp->lock);
|
||||
}
|
||||
EXPORT_SYMBOL(mcp_set_audio_divisor);
|
||||
|
||||
/**
|
||||
* mcp_reg_write - write a device register
|
||||
* @mcp: MCP interface structure
|
||||
* @reg: 4-bit register index
|
||||
* @val: 16-bit data value
|
||||
*
|
||||
* Write a device register. The MCP interface must be enabled
|
||||
* to prevent this function hanging.
|
||||
*/
|
||||
void mcp_reg_write(struct mcp *mcp, unsigned int reg, unsigned int val)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&mcp->lock, flags);
|
||||
mcp->ops->reg_write(mcp, reg, val);
|
||||
spin_unlock_irqrestore(&mcp->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(mcp_reg_write);
|
||||
|
||||
/**
|
||||
* mcp_reg_read - read a device register
|
||||
* @mcp: MCP interface structure
|
||||
* @reg: 4-bit register index
|
||||
*
|
||||
* Read a device register and return its value. The MCP interface
|
||||
* must be enabled to prevent this function hanging.
|
||||
*/
|
||||
unsigned int mcp_reg_read(struct mcp *mcp, unsigned int reg)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int val;
|
||||
|
||||
spin_lock_irqsave(&mcp->lock, flags);
|
||||
val = mcp->ops->reg_read(mcp, reg);
|
||||
spin_unlock_irqrestore(&mcp->lock, flags);
|
||||
|
||||
return val;
|
||||
}
|
||||
EXPORT_SYMBOL(mcp_reg_read);
|
||||
|
||||
/**
|
||||
* mcp_enable - enable the MCP interface
|
||||
* @mcp: MCP interface to enable
|
||||
*
|
||||
* Enable the MCP interface. Each call to mcp_enable will need
|
||||
* a corresponding call to mcp_disable to disable the interface.
|
||||
*/
|
||||
void mcp_enable(struct mcp *mcp)
|
||||
{
|
||||
spin_lock_irq(&mcp->lock);
|
||||
if (mcp->use_count++ == 0)
|
||||
mcp->ops->enable(mcp);
|
||||
spin_unlock_irq(&mcp->lock);
|
||||
}
|
||||
EXPORT_SYMBOL(mcp_enable);
|
||||
|
||||
/**
|
||||
* mcp_disable - disable the MCP interface
|
||||
* @mcp: MCP interface to disable
|
||||
*
|
||||
* Disable the MCP interface. The MCP interface will only be
|
||||
* disabled once the number of calls to mcp_enable matches the
|
||||
* number of calls to mcp_disable.
|
||||
*/
|
||||
void mcp_disable(struct mcp *mcp)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&mcp->lock, flags);
|
||||
if (--mcp->use_count == 0)
|
||||
mcp->ops->disable(mcp);
|
||||
spin_unlock_irqrestore(&mcp->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(mcp_disable);
|
||||
|
||||
static void mcp_release(struct device *dev)
|
||||
{
|
||||
struct mcp *mcp = container_of(dev, struct mcp, attached_device);
|
||||
|
||||
kfree(mcp);
|
||||
}
|
||||
|
||||
struct mcp *mcp_host_alloc(struct device *parent, size_t size)
|
||||
{
|
||||
struct mcp *mcp;
|
||||
|
||||
mcp = kmalloc(sizeof(struct mcp) + size, GFP_KERNEL);
|
||||
if (mcp) {
|
||||
memset(mcp, 0, sizeof(struct mcp) + size);
|
||||
spin_lock_init(&mcp->lock);
|
||||
mcp->attached_device.parent = parent;
|
||||
mcp->attached_device.bus = &mcp_bus_type;
|
||||
mcp->attached_device.dma_mask = parent->dma_mask;
|
||||
mcp->attached_device.release = mcp_release;
|
||||
}
|
||||
return mcp;
|
||||
}
|
||||
EXPORT_SYMBOL(mcp_host_alloc);
|
||||
|
||||
int mcp_host_register(struct mcp *mcp)
|
||||
{
|
||||
strcpy(mcp->attached_device.bus_id, "mcp0");
|
||||
return device_register(&mcp->attached_device);
|
||||
}
|
||||
EXPORT_SYMBOL(mcp_host_register);
|
||||
|
||||
void mcp_host_unregister(struct mcp *mcp)
|
||||
{
|
||||
device_unregister(&mcp->attached_device);
|
||||
}
|
||||
EXPORT_SYMBOL(mcp_host_unregister);
|
||||
|
||||
int mcp_driver_register(struct mcp_driver *mcpdrv)
|
||||
{
|
||||
mcpdrv->drv.bus = &mcp_bus_type;
|
||||
mcpdrv->drv.probe = mcp_bus_probe;
|
||||
mcpdrv->drv.remove = mcp_bus_remove;
|
||||
return driver_register(&mcpdrv->drv);
|
||||
}
|
||||
EXPORT_SYMBOL(mcp_driver_register);
|
||||
|
||||
void mcp_driver_unregister(struct mcp_driver *mcpdrv)
|
||||
{
|
||||
driver_unregister(&mcpdrv->drv);
|
||||
}
|
||||
EXPORT_SYMBOL(mcp_driver_unregister);
|
||||
|
||||
static int __init mcp_init(void)
|
||||
{
|
||||
return bus_register(&mcp_bus_type);
|
||||
}
|
||||
|
||||
static void __exit mcp_exit(void)
|
||||
{
|
||||
bus_unregister(&mcp_bus_type);
|
||||
}
|
||||
|
||||
module_init(mcp_init);
|
||||
module_exit(mcp_exit);
|
||||
|
||||
MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
|
||||
MODULE_DESCRIPTION("Core multimedia communications port driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,275 @@
|
|||
/*
|
||||
* linux/drivers/mfd/mcp-sa11x0.c
|
||||
*
|
||||
* Copyright (C) 2001-2005 Russell King
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License.
|
||||
*
|
||||
* SA11x0 MCP (Multimedia Communications Port) driver.
|
||||
*
|
||||
* MCP read/write timeouts from Jordi Colomer, rehacked by rmk.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <asm/dma.h>
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/arch/mcp.h>
|
||||
|
||||
#include <asm/arch/assabet.h>
|
||||
|
||||
#include "mcp.h"
|
||||
|
||||
struct mcp_sa11x0 {
|
||||
u32 mccr0;
|
||||
u32 mccr1;
|
||||
};
|
||||
|
||||
#define priv(mcp) ((struct mcp_sa11x0 *)mcp_priv(mcp))
|
||||
|
||||
static void
|
||||
mcp_sa11x0_set_telecom_divisor(struct mcp *mcp, unsigned int divisor)
|
||||
{
|
||||
unsigned int mccr0;
|
||||
|
||||
divisor /= 32;
|
||||
|
||||
mccr0 = Ser4MCCR0 & ~0x00007f00;
|
||||
mccr0 |= divisor << 8;
|
||||
Ser4MCCR0 = mccr0;
|
||||
}
|
||||
|
||||
static void
|
||||
mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor)
|
||||
{
|
||||
unsigned int mccr0;
|
||||
|
||||
divisor /= 32;
|
||||
|
||||
mccr0 = Ser4MCCR0 & ~0x0000007f;
|
||||
mccr0 |= divisor;
|
||||
Ser4MCCR0 = mccr0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write data to the device. The bit should be set after 3 subframe
|
||||
* times (each frame is 64 clocks). We wait a maximum of 6 subframes.
|
||||
* We really should try doing something more productive while we
|
||||
* wait.
|
||||
*/
|
||||
static void
|
||||
mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val)
|
||||
{
|
||||
int ret = -ETIME;
|
||||
int i;
|
||||
|
||||
Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
udelay(mcp->rw_timeout);
|
||||
if (Ser4MCSR & MCSR_CWC) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
printk(KERN_WARNING "mcp: write timed out\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Read data from the device. The bit should be set after 3 subframe
|
||||
* times (each frame is 64 clocks). We wait a maximum of 6 subframes.
|
||||
* We really should try doing something more productive while we
|
||||
* wait.
|
||||
*/
|
||||
static unsigned int
|
||||
mcp_sa11x0_read(struct mcp *mcp, unsigned int reg)
|
||||
{
|
||||
int ret = -ETIME;
|
||||
int i;
|
||||
|
||||
Ser4MCDR2 = reg << 17 | MCDR2_Rd;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
udelay(mcp->rw_timeout);
|
||||
if (Ser4MCSR & MCSR_CRC) {
|
||||
ret = Ser4MCDR2 & 0xffff;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
printk(KERN_WARNING "mcp: read timed out\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mcp_sa11x0_enable(struct mcp *mcp)
|
||||
{
|
||||
Ser4MCSR = -1;
|
||||
Ser4MCCR0 |= MCCR0_MCE;
|
||||
}
|
||||
|
||||
static void mcp_sa11x0_disable(struct mcp *mcp)
|
||||
{
|
||||
Ser4MCCR0 &= ~MCCR0_MCE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Our methods.
|
||||
*/
|
||||
static struct mcp_ops mcp_sa11x0 = {
|
||||
.set_telecom_divisor = mcp_sa11x0_set_telecom_divisor,
|
||||
.set_audio_divisor = mcp_sa11x0_set_audio_divisor,
|
||||
.reg_write = mcp_sa11x0_write,
|
||||
.reg_read = mcp_sa11x0_read,
|
||||
.enable = mcp_sa11x0_enable,
|
||||
.disable = mcp_sa11x0_disable,
|
||||
};
|
||||
|
||||
static int mcp_sa11x0_probe(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct mcp_plat_data *data = pdev->dev.platform_data;
|
||||
struct mcp *mcp;
|
||||
int ret;
|
||||
|
||||
if (!data)
|
||||
return -ENODEV;
|
||||
|
||||
if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp"))
|
||||
return -EBUSY;
|
||||
|
||||
mcp = mcp_host_alloc(&pdev->dev, sizeof(struct mcp_sa11x0));
|
||||
if (!mcp) {
|
||||
ret = -ENOMEM;
|
||||
goto release;
|
||||
}
|
||||
|
||||
mcp->owner = THIS_MODULE;
|
||||
mcp->ops = &mcp_sa11x0;
|
||||
mcp->sclk_rate = data->sclk_rate;
|
||||
mcp->dma_audio_rd = DMA_Ser4MCP0Rd;
|
||||
mcp->dma_audio_wr = DMA_Ser4MCP0Wr;
|
||||
mcp->dma_telco_rd = DMA_Ser4MCP1Rd;
|
||||
mcp->dma_telco_wr = DMA_Ser4MCP1Wr;
|
||||
|
||||
dev_set_drvdata(dev, mcp);
|
||||
|
||||
if (machine_is_assabet()) {
|
||||
ASSABET_BCR_set(ASSABET_BCR_CODEC_RST);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup the PPC unit correctly.
|
||||
*/
|
||||
PPDR &= ~PPC_RXD4;
|
||||
PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM;
|
||||
PSDR |= PPC_RXD4;
|
||||
PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
|
||||
PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
|
||||
|
||||
/*
|
||||
* Initialise device. Note that we initially
|
||||
* set the sampling rate to minimum.
|
||||
*/
|
||||
Ser4MCSR = -1;
|
||||
Ser4MCCR1 = data->mccr1;
|
||||
Ser4MCCR0 = data->mccr0 | 0x7f7f;
|
||||
|
||||
/*
|
||||
* Calculate the read/write timeout (us) from the bit clock
|
||||
* rate. This is the period for 3 64-bit frames. Always
|
||||
* round this time up.
|
||||
*/
|
||||
mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) /
|
||||
mcp->sclk_rate;
|
||||
|
||||
ret = mcp_host_register(mcp);
|
||||
if (ret == 0)
|
||||
goto out;
|
||||
|
||||
release:
|
||||
release_mem_region(0x80060000, 0x60);
|
||||
dev_set_drvdata(dev, NULL);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp_sa11x0_remove(struct device *dev)
|
||||
{
|
||||
struct mcp *mcp = dev_get_drvdata(dev);
|
||||
|
||||
dev_set_drvdata(dev, NULL);
|
||||
mcp_host_unregister(mcp);
|
||||
release_mem_region(0x80060000, 0x60);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcp_sa11x0_suspend(struct device *dev, pm_message_t state, u32 level)
|
||||
{
|
||||
struct mcp *mcp = dev_get_drvdata(dev);
|
||||
|
||||
if (level == SUSPEND_DISABLE) {
|
||||
priv(mcp)->mccr0 = Ser4MCCR0;
|
||||
priv(mcp)->mccr1 = Ser4MCCR1;
|
||||
Ser4MCCR0 &= ~MCCR0_MCE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcp_sa11x0_resume(struct device *dev, u32 level)
|
||||
{
|
||||
struct mcp *mcp = dev_get_drvdata(dev);
|
||||
|
||||
if (level == RESUME_RESTORE_STATE) {
|
||||
Ser4MCCR1 = priv(mcp)->mccr1;
|
||||
Ser4MCCR0 = priv(mcp)->mccr0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The driver for the SA11x0 MCP port.
|
||||
*/
|
||||
static struct device_driver mcp_sa11x0_driver = {
|
||||
.name = "sa11x0-mcp",
|
||||
.bus = &platform_bus_type,
|
||||
.probe = mcp_sa11x0_probe,
|
||||
.remove = mcp_sa11x0_remove,
|
||||
.suspend = mcp_sa11x0_suspend,
|
||||
.resume = mcp_sa11x0_resume,
|
||||
};
|
||||
|
||||
/*
|
||||
* This needs re-working
|
||||
*/
|
||||
static int __init mcp_sa11x0_init(void)
|
||||
{
|
||||
return driver_register(&mcp_sa11x0_driver);
|
||||
}
|
||||
|
||||
static void __exit mcp_sa11x0_exit(void)
|
||||
{
|
||||
driver_unregister(&mcp_sa11x0_driver);
|
||||
}
|
||||
|
||||
module_init(mcp_sa11x0_init);
|
||||
module_exit(mcp_sa11x0_exit);
|
||||
|
||||
MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
|
||||
MODULE_DESCRIPTION("SA11x0 multimedia communications port driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* linux/drivers/mfd/mcp.h
|
||||
*
|
||||
* Copyright (C) 2001 Russell King, All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License.
|
||||
*/
|
||||
#ifndef MCP_H
|
||||
#define MCP_H
|
||||
|
||||
struct mcp_ops;
|
||||
|
||||
struct mcp {
|
||||
struct module *owner;
|
||||
struct mcp_ops *ops;
|
||||
spinlock_t lock;
|
||||
int use_count;
|
||||
unsigned int sclk_rate;
|
||||
unsigned int rw_timeout;
|
||||
dma_device_t dma_audio_rd;
|
||||
dma_device_t dma_audio_wr;
|
||||
dma_device_t dma_telco_rd;
|
||||
dma_device_t dma_telco_wr;
|
||||
struct device attached_device;
|
||||
};
|
||||
|
||||
struct mcp_ops {
|
||||
void (*set_telecom_divisor)(struct mcp *, unsigned int);
|
||||
void (*set_audio_divisor)(struct mcp *, unsigned int);
|
||||
void (*reg_write)(struct mcp *, unsigned int, unsigned int);
|
||||
unsigned int (*reg_read)(struct mcp *, unsigned int);
|
||||
void (*enable)(struct mcp *);
|
||||
void (*disable)(struct mcp *);
|
||||
};
|
||||
|
||||
void mcp_set_telecom_divisor(struct mcp *, unsigned int);
|
||||
void mcp_set_audio_divisor(struct mcp *, unsigned int);
|
||||
void mcp_reg_write(struct mcp *, unsigned int, unsigned int);
|
||||
unsigned int mcp_reg_read(struct mcp *, unsigned int);
|
||||
void mcp_enable(struct mcp *);
|
||||
void mcp_disable(struct mcp *);
|
||||
#define mcp_get_sclk_rate(mcp) ((mcp)->sclk_rate)
|
||||
|
||||
struct mcp *mcp_host_alloc(struct device *, size_t);
|
||||
int mcp_host_register(struct mcp *);
|
||||
void mcp_host_unregister(struct mcp *);
|
||||
|
||||
struct mcp_driver {
|
||||
struct device_driver drv;
|
||||
int (*probe)(struct mcp *);
|
||||
void (*remove)(struct mcp *);
|
||||
int (*suspend)(struct mcp *, pm_message_t);
|
||||
int (*resume)(struct mcp *);
|
||||
};
|
||||
|
||||
int mcp_driver_register(struct mcp_driver *);
|
||||
void mcp_driver_unregister(struct mcp_driver *);
|
||||
|
||||
#define mcp_get_drvdata(mcp) dev_get_drvdata(&(mcp)->attached_device)
|
||||
#define mcp_set_drvdata(mcp,d) dev_set_drvdata(&(mcp)->attached_device, d)
|
||||
|
||||
#define mcp_priv(mcp) ((void *)((mcp)+1))
|
||||
|
||||
#endif
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* linux/include/asm-arm/arch-sa1100/mcp.h
|
||||
*
|
||||
* Copyright (C) 2005 Russell King.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef __ASM_ARM_ARCH_MCP_H
|
||||
#define __ASM_ARM_ARCH_MCP_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct mcp_plat_data {
|
||||
u32 mccr0;
|
||||
u32 mccr1;
|
||||
unsigned int sclk_rate;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue