65f2e753f1
This reverts commit 5dd7bf59e0
.
Conflicts:
scripts/mod/file2alias.c
This change is wrong on many levels. First and foremost, it causes a
regression. On boot on Assabet, which this patch gives a codec id of
'ucb1x00', it gives:
ucb1x00 ID not found: 1005
0x1005 is a valid ID for the UCB1300 device.
Secondly, this patch is way over the top in terms of complexity. The
only device which has been seen to be connected with this MCP code is
the UCB1x00 (UCB1200, UCB1300 etc) devices, and they all use the same
driver. Adding a match table, requiring the codec string to match the
hardware ID read out of the ID register, etc is completely over the top
when we can just read the hardware ID register.
265 lines
5.5 KiB
C
265 lines
5.5 KiB
C
/*
|
|
* 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/platform_device.h>
|
|
#include <linux/mfd/mcp.h>
|
|
|
|
#include <mach/dma.h>
|
|
#include <mach/hardware.h>
|
|
#include <asm/mach-types.h>
|
|
#include <asm/system.h>
|
|
#include <mach/mcp.h>
|
|
|
|
#include <mach/assabet.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 platform_device *pdev)
|
|
{
|
|
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;
|
|
mcp->gpio_base = data->gpio_base;
|
|
|
|
platform_set_drvdata(pdev, 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);
|
|
platform_set_drvdata(pdev, NULL);
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int mcp_sa11x0_remove(struct platform_device *dev)
|
|
{
|
|
struct mcp *mcp = platform_get_drvdata(dev);
|
|
|
|
platform_set_drvdata(dev, NULL);
|
|
mcp_host_unregister(mcp);
|
|
release_mem_region(0x80060000, 0x60);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mcp_sa11x0_suspend(struct platform_device *dev, pm_message_t state)
|
|
{
|
|
struct mcp *mcp = platform_get_drvdata(dev);
|
|
|
|
priv(mcp)->mccr0 = Ser4MCCR0;
|
|
priv(mcp)->mccr1 = Ser4MCCR1;
|
|
Ser4MCCR0 &= ~MCCR0_MCE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mcp_sa11x0_resume(struct platform_device *dev)
|
|
{
|
|
struct mcp *mcp = platform_get_drvdata(dev);
|
|
|
|
Ser4MCCR1 = priv(mcp)->mccr1;
|
|
Ser4MCCR0 = priv(mcp)->mccr0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* The driver for the SA11x0 MCP port.
|
|
*/
|
|
MODULE_ALIAS("platform:sa11x0-mcp");
|
|
|
|
static struct platform_driver mcp_sa11x0_driver = {
|
|
.probe = mcp_sa11x0_probe,
|
|
.remove = mcp_sa11x0_remove,
|
|
.suspend = mcp_sa11x0_suspend,
|
|
.resume = mcp_sa11x0_resume,
|
|
.driver = {
|
|
.name = "sa11x0-mcp",
|
|
},
|
|
};
|
|
|
|
/*
|
|
* This needs re-working
|
|
*/
|
|
module_platform_driver(mcp_sa11x0_driver);
|
|
|
|
MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
|
|
MODULE_DESCRIPTION("SA11x0 multimedia communications port driver");
|
|
MODULE_LICENSE("GPL");
|