Merge branch 'sa11x0-mcp' into sa11x0
Conflicts: arch/arm/mach-sa1100/assabet.c arch/arm/mach-sa1100/collie.c arch/arm/mach-sa1100/generic.c arch/arm/mach-sa1100/lart.c arch/arm/mach-sa1100/shannon.c
This commit is contained in:
commit
7256ecc2b7
|
@ -15,6 +15,7 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/mfd/ucb1x00.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/delay.h>
|
||||
|
@ -71,6 +72,12 @@ void ASSABET_BCR_frob(unsigned int mask, unsigned int val)
|
|||
|
||||
EXPORT_SYMBOL(ASSABET_BCR_frob);
|
||||
|
||||
static void assabet_ucb1x00_reset(enum ucb1x00_reset state)
|
||||
{
|
||||
if (state == UCB_RST_PROBE)
|
||||
ASSABET_BCR_set(ASSABET_BCR_CODEC_RST);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Assabet flash support code.
|
||||
|
@ -167,9 +174,15 @@ static struct irda_platform_data assabet_irda_data = {
|
|||
.set_speed = assabet_irda_set_speed,
|
||||
};
|
||||
|
||||
static struct ucb1x00_plat_data assabet_ucb1x00_data = {
|
||||
.reset = assabet_ucb1x00_reset,
|
||||
.gpio_base = -1,
|
||||
};
|
||||
|
||||
static struct mcp_plat_data assabet_mcp_data = {
|
||||
.mccr0 = MCCR0_ADM,
|
||||
.sclk_rate = 11981000,
|
||||
.codec_pdata = &assabet_ucb1x00_data,
|
||||
};
|
||||
|
||||
static void assabet_lcd_set_visual(u32 visual)
|
||||
|
@ -309,6 +322,8 @@ static void __init assabet_init(void)
|
|||
PPDR |= PPC_TXD3 | PPC_TXD1;
|
||||
PPSR |= PPC_TXD3 | PPC_TXD1;
|
||||
|
||||
sa11x0_ppc_configure_mcp();
|
||||
|
||||
if (machine_has_neponset()) {
|
||||
/*
|
||||
* Angel sets this, but other bootloaders may not.
|
||||
|
|
|
@ -121,6 +121,7 @@ static struct mcp_plat_data cerf_mcp_data = {
|
|||
|
||||
static void __init cerf_init(void)
|
||||
{
|
||||
sa11x0_ppc_configure_mcp();
|
||||
platform_add_devices(cerf_devices, ARRAY_SIZE(cerf_devices));
|
||||
sa11x0_register_mtd(&cerf_flash_data, &cerf_flash_resource, 1);
|
||||
sa11x0_register_mcp(&cerf_mcp_data);
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <linux/tty.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/ucb1x00.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/timer.h>
|
||||
|
@ -83,10 +84,14 @@ static struct scoop_pcmcia_config collie_pcmcia_config = {
|
|||
.num_devs = 1,
|
||||
};
|
||||
|
||||
static struct ucb1x00_plat_data collie_ucb1x00_data = {
|
||||
.gpio_base = COLLIE_TC35143_GPIO_BASE,
|
||||
};
|
||||
|
||||
static struct mcp_plat_data collie_mcp_data = {
|
||||
.mccr0 = MCCR0_ADM | MCCR0_ExtClk,
|
||||
.sclk_rate = 9216000,
|
||||
.gpio_base = COLLIE_TC35143_GPIO_BASE,
|
||||
.codec_pdata = &collie_ucb1x00_data,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -341,6 +346,10 @@ static void __init collie_init(void)
|
|||
|
||||
collie_power_resource[0].start = gpio_to_irq(COLLIE_GPIO_AC_IN);
|
||||
collie_power_resource[0].end = gpio_to_irq(COLLIE_GPIO_AC_IN);
|
||||
|
||||
sa11x0_ppc_configure_mcp();
|
||||
|
||||
|
||||
platform_scoop_config = &collie_pcmcia_config;
|
||||
|
||||
ret = platform_add_devices(devices, ARRAY_SIZE(devices));
|
||||
|
|
|
@ -195,7 +195,8 @@ static struct platform_device sa11x0uart3_device = {
|
|||
|
||||
static struct resource sa11x0mcp_resources[] = {
|
||||
[0] = DEFINE_RES_MEM(__PREG(Ser4MCCR0), SZ_64K),
|
||||
[1] = DEFINE_RES_IRQ(IRQ_Ser4MCP),
|
||||
[1] = DEFINE_RES_MEM(__PREG(Ser4MCCR1), 4),
|
||||
[2] = DEFINE_RES_IRQ(IRQ_Ser4MCP),
|
||||
};
|
||||
|
||||
static u64 sa11x0mcp_dma_mask = 0xffffffffUL;
|
||||
|
@ -211,6 +212,16 @@ static struct platform_device sa11x0mcp_device = {
|
|||
.resource = sa11x0mcp_resources,
|
||||
};
|
||||
|
||||
void __init sa11x0_ppc_configure_mcp(void)
|
||||
{
|
||||
/* Setup the PPC unit for the MCP */
|
||||
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);
|
||||
}
|
||||
|
||||
void sa11x0_register_mcp(struct mcp_plat_data *data)
|
||||
{
|
||||
sa11x0_register_device(&sa11x0mcp_device, data);
|
||||
|
|
|
@ -36,6 +36,7 @@ struct irda_platform_data;
|
|||
void sa11x0_register_irda(struct irda_platform_data *irda);
|
||||
|
||||
struct mcp_plat_data;
|
||||
void sa11x0_ppc_configure_mcp(void);
|
||||
void sa11x0_register_mcp(struct mcp_plat_data *data);
|
||||
|
||||
struct sa1100fb_mach_info;
|
||||
|
|
|
@ -16,7 +16,7 @@ struct mcp_plat_data {
|
|||
u32 mccr0;
|
||||
u32 mccr1;
|
||||
unsigned int sclk_rate;
|
||||
int gpio_base;
|
||||
void *codec_pdata;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -107,6 +107,7 @@ static void __init lart_init(void)
|
|||
if (inf)
|
||||
sa11x0_register_lcd(inf);
|
||||
|
||||
sa11x0_ppc_configure_mcp();
|
||||
sa11x0_register_mcp(&lart_mcp_data);
|
||||
}
|
||||
|
||||
|
|
|
@ -72,6 +72,7 @@ static struct sa1100fb_mach_info shannon_lcd_info = {
|
|||
|
||||
static void __init shannon_init(void)
|
||||
{
|
||||
sa11x0_ppc_configure_mcp();
|
||||
sa11x0_register_lcd(&shannon_lcd_info);
|
||||
sa11x0_register_mtd(&shannon_flash_data, &shannon_flash_resource, 1);
|
||||
sa11x0_register_mcp(&shannon_mcp_data);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/string.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/ucb1x00.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/io.h>
|
||||
|
@ -180,10 +181,14 @@ static struct resource simpad_flash_resources [] = {
|
|||
DEFINE_RES_MEM(SA1100_CS1_PHYS, SZ_16M),
|
||||
};
|
||||
|
||||
static struct ucb1x00_plat_data simpad_ucb1x00_data = {
|
||||
.gpio_base = SIMPAD_UCB1X00_GPIO_BASE,
|
||||
};
|
||||
|
||||
static struct mcp_plat_data simpad_mcp_data = {
|
||||
.mccr0 = MCCR0_ADM,
|
||||
.sclk_rate = 11981000,
|
||||
.gpio_base = SIMPAD_UCB1X00_GPIO_BASE,
|
||||
.codec_pdata = &simpad_ucb1x00_data,
|
||||
};
|
||||
|
||||
|
||||
|
@ -369,6 +374,7 @@ static int __init simpad_init(void)
|
|||
|
||||
pm_power_off = simpad_power_off;
|
||||
|
||||
sa11x0_ppc_configure_mcp();
|
||||
sa11x0_register_mtd(&simpad_flash_data, simpad_flash_resources,
|
||||
ARRAY_SIZE(simpad_flash_resources));
|
||||
sa11x0_register_mcp(&simpad_mcp_data);
|
||||
|
|
|
@ -847,8 +847,9 @@ config MCP_SA11X0
|
|||
|
||||
# Chip drivers
|
||||
config MCP_UCB1200
|
||||
tristate "Support for UCB1200 / UCB1300"
|
||||
depends on MCP
|
||||
bool "Support for UCB1200 / UCB1300"
|
||||
depends on MCP_SA11X0
|
||||
select MCP
|
||||
|
||||
config MCP_UCB1200_TS
|
||||
tristate "Touchscreen interface support"
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <linux/string.h>
|
||||
#include <linux/mfd/mcp.h>
|
||||
|
||||
#include <mach/dma.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
|
||||
|
@ -48,39 +47,11 @@ static int mcp_bus_remove(struct device *dev)
|
|||
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,
|
||||
.probe = mcp_bus_probe,
|
||||
.remove = mcp_bus_remove,
|
||||
.suspend = mcp_bus_suspend,
|
||||
.resume = mcp_bus_resume,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -208,6 +179,7 @@ struct mcp *mcp_host_alloc(struct device *parent, size_t size)
|
|||
mcp = kzalloc(sizeof(struct mcp) + size, GFP_KERNEL);
|
||||
if (mcp) {
|
||||
spin_lock_init(&mcp->lock);
|
||||
device_initialize(&mcp->attached_device);
|
||||
mcp->attached_device.parent = parent;
|
||||
mcp->attached_device.bus = &mcp_bus_type;
|
||||
mcp->attached_device.dma_mask = parent->dma_mask;
|
||||
|
@ -217,18 +189,25 @@ struct mcp *mcp_host_alloc(struct device *parent, size_t size)
|
|||
}
|
||||
EXPORT_SYMBOL(mcp_host_alloc);
|
||||
|
||||
int mcp_host_register(struct mcp *mcp)
|
||||
int mcp_host_add(struct mcp *mcp, void *pdata)
|
||||
{
|
||||
mcp->attached_device.platform_data = pdata;
|
||||
dev_set_name(&mcp->attached_device, "mcp0");
|
||||
return device_register(&mcp->attached_device);
|
||||
return device_add(&mcp->attached_device);
|
||||
}
|
||||
EXPORT_SYMBOL(mcp_host_register);
|
||||
EXPORT_SYMBOL(mcp_host_add);
|
||||
|
||||
void mcp_host_unregister(struct mcp *mcp)
|
||||
void mcp_host_del(struct mcp *mcp)
|
||||
{
|
||||
device_unregister(&mcp->attached_device);
|
||||
device_del(&mcp->attached_device);
|
||||
}
|
||||
EXPORT_SYMBOL(mcp_host_unregister);
|
||||
EXPORT_SYMBOL(mcp_host_del);
|
||||
|
||||
void mcp_host_free(struct mcp *mcp)
|
||||
{
|
||||
put_device(&mcp->attached_device);
|
||||
}
|
||||
EXPORT_SYMBOL(mcp_host_free);
|
||||
|
||||
int mcp_driver_register(struct mcp_driver *mcpdrv)
|
||||
{
|
||||
|
|
|
@ -13,51 +13,61 @@
|
|||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.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>
|
||||
|
||||
#define DRIVER_NAME "sa11x0-mcp"
|
||||
|
||||
struct mcp_sa11x0 {
|
||||
u32 mccr0;
|
||||
u32 mccr1;
|
||||
void __iomem *base0;
|
||||
void __iomem *base1;
|
||||
u32 mccr0;
|
||||
u32 mccr1;
|
||||
};
|
||||
|
||||
/* Register offsets */
|
||||
#define MCCR0(m) ((m)->base0 + 0x00)
|
||||
#define MCDR0(m) ((m)->base0 + 0x08)
|
||||
#define MCDR1(m) ((m)->base0 + 0x0c)
|
||||
#define MCDR2(m) ((m)->base0 + 0x10)
|
||||
#define MCSR(m) ((m)->base0 + 0x18)
|
||||
#define MCCR1(m) ((m)->base1 + 0x00)
|
||||
|
||||
#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;
|
||||
struct mcp_sa11x0 *m = priv(mcp);
|
||||
|
||||
divisor /= 32;
|
||||
|
||||
mccr0 = Ser4MCCR0 & ~0x00007f00;
|
||||
mccr0 |= divisor << 8;
|
||||
Ser4MCCR0 = mccr0;
|
||||
m->mccr0 &= ~0x00007f00;
|
||||
m->mccr0 |= divisor << 8;
|
||||
writel_relaxed(m->mccr0, MCCR0(m));
|
||||
}
|
||||
|
||||
static void
|
||||
mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor)
|
||||
{
|
||||
unsigned int mccr0;
|
||||
struct mcp_sa11x0 *m = priv(mcp);
|
||||
|
||||
divisor /= 32;
|
||||
|
||||
mccr0 = Ser4MCCR0 & ~0x0000007f;
|
||||
mccr0 |= divisor;
|
||||
Ser4MCCR0 = mccr0;
|
||||
m->mccr0 &= ~0x0000007f;
|
||||
m->mccr0 |= divisor;
|
||||
writel_relaxed(m->mccr0, MCCR0(m));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -69,14 +79,15 @@ mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor)
|
|||
static void
|
||||
mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val)
|
||||
{
|
||||
struct mcp_sa11x0 *m = priv(mcp);
|
||||
int ret = -ETIME;
|
||||
int i;
|
||||
|
||||
Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff);
|
||||
writel_relaxed(reg << 17 | MCDR2_Wr | (val & 0xffff), MCDR2(m));
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
udelay(mcp->rw_timeout);
|
||||
if (Ser4MCSR & MCSR_CWC) {
|
||||
if (readl_relaxed(MCSR(m)) & MCSR_CWC) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
@ -95,15 +106,16 @@ mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val)
|
|||
static unsigned int
|
||||
mcp_sa11x0_read(struct mcp *mcp, unsigned int reg)
|
||||
{
|
||||
struct mcp_sa11x0 *m = priv(mcp);
|
||||
int ret = -ETIME;
|
||||
int i;
|
||||
|
||||
Ser4MCDR2 = reg << 17 | MCDR2_Rd;
|
||||
writel_relaxed(reg << 17 | MCDR2_Rd, MCDR2(m));
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
udelay(mcp->rw_timeout);
|
||||
if (Ser4MCSR & MCSR_CRC) {
|
||||
ret = Ser4MCDR2 & 0xffff;
|
||||
if (readl_relaxed(MCSR(m)) & MCSR_CRC) {
|
||||
ret = readl_relaxed(MCDR2(m)) & 0xffff;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -116,13 +128,19 @@ mcp_sa11x0_read(struct mcp *mcp, unsigned int reg)
|
|||
|
||||
static void mcp_sa11x0_enable(struct mcp *mcp)
|
||||
{
|
||||
Ser4MCSR = -1;
|
||||
Ser4MCCR0 |= MCCR0_MCE;
|
||||
struct mcp_sa11x0 *m = priv(mcp);
|
||||
|
||||
writel(-1, MCSR(m));
|
||||
m->mccr0 |= MCCR0_MCE;
|
||||
writel_relaxed(m->mccr0, MCCR0(m));
|
||||
}
|
||||
|
||||
static void mcp_sa11x0_disable(struct mcp *mcp)
|
||||
{
|
||||
Ser4MCCR0 &= ~MCCR0_MCE;
|
||||
struct mcp_sa11x0 *m = priv(mcp);
|
||||
|
||||
m->mccr0 &= ~MCCR0_MCE;
|
||||
writel_relaxed(m->mccr0, MCCR0(m));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -137,55 +155,64 @@ static struct mcp_ops mcp_sa11x0 = {
|
|||
.disable = mcp_sa11x0_disable,
|
||||
};
|
||||
|
||||
static int mcp_sa11x0_probe(struct platform_device *pdev)
|
||||
static int mcp_sa11x0_probe(struct platform_device *dev)
|
||||
{
|
||||
struct mcp_plat_data *data = pdev->dev.platform_data;
|
||||
struct mcp_plat_data *data = dev->dev.platform_data;
|
||||
struct resource *mem0, *mem1;
|
||||
struct mcp_sa11x0 *m;
|
||||
struct mcp *mcp;
|
||||
int ret;
|
||||
|
||||
if (!data)
|
||||
return -ENODEV;
|
||||
|
||||
if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp"))
|
||||
return -EBUSY;
|
||||
mem0 = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
||||
mem1 = platform_get_resource(dev, IORESOURCE_MEM, 1);
|
||||
if (!mem0 || !mem1)
|
||||
return -ENXIO;
|
||||
|
||||
mcp = mcp_host_alloc(&pdev->dev, sizeof(struct mcp_sa11x0));
|
||||
if (!request_mem_region(mem0->start, resource_size(mem0),
|
||||
DRIVER_NAME)) {
|
||||
ret = -EBUSY;
|
||||
goto err_mem0;
|
||||
}
|
||||
|
||||
if (!request_mem_region(mem1->start, resource_size(mem1),
|
||||
DRIVER_NAME)) {
|
||||
ret = -EBUSY;
|
||||
goto err_mem1;
|
||||
}
|
||||
|
||||
mcp = mcp_host_alloc(&dev->dev, sizeof(struct mcp_sa11x0));
|
||||
if (!mcp) {
|
||||
ret = -ENOMEM;
|
||||
goto release;
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
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);
|
||||
m = priv(mcp);
|
||||
m->mccr0 = data->mccr0 | 0x7f7f;
|
||||
m->mccr1 = data->mccr1;
|
||||
|
||||
if (machine_is_assabet()) {
|
||||
ASSABET_BCR_set(ASSABET_BCR_CODEC_RST);
|
||||
m->base0 = ioremap(mem0->start, resource_size(mem0));
|
||||
m->base1 = ioremap(mem1->start, resource_size(mem1));
|
||||
if (!m->base0 || !m->base1) {
|
||||
ret = -ENOMEM;
|
||||
goto err_ioremap;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
platform_set_drvdata(dev, mcp);
|
||||
|
||||
/*
|
||||
* Initialise device. Note that we initially
|
||||
* set the sampling rate to minimum.
|
||||
*/
|
||||
Ser4MCSR = -1;
|
||||
Ser4MCCR1 = data->mccr1;
|
||||
Ser4MCCR0 = data->mccr0 | 0x7f7f;
|
||||
writel_relaxed(-1, MCSR(m));
|
||||
writel_relaxed(m->mccr1, MCCR1(m));
|
||||
writel_relaxed(m->mccr0, MCCR0(m));
|
||||
|
||||
/*
|
||||
* Calculate the read/write timeout (us) from the bit clock
|
||||
|
@ -195,62 +222,90 @@ static int mcp_sa11x0_probe(struct platform_device *pdev)
|
|||
mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) /
|
||||
mcp->sclk_rate;
|
||||
|
||||
ret = mcp_host_register(mcp);
|
||||
ret = mcp_host_add(mcp, data->codec_pdata);
|
||||
if (ret == 0)
|
||||
goto out;
|
||||
return 0;
|
||||
|
||||
release:
|
||||
release_mem_region(0x80060000, 0x60);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
platform_set_drvdata(dev, NULL);
|
||||
|
||||
out:
|
||||
err_ioremap:
|
||||
iounmap(m->base1);
|
||||
iounmap(m->base0);
|
||||
mcp_host_free(mcp);
|
||||
err_alloc:
|
||||
release_mem_region(mem1->start, resource_size(mem1));
|
||||
err_mem1:
|
||||
release_mem_region(mem0->start, resource_size(mem0));
|
||||
err_mem0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp_sa11x0_remove(struct platform_device *dev)
|
||||
{
|
||||
struct mcp *mcp = platform_get_drvdata(dev);
|
||||
struct mcp_sa11x0 *m = priv(mcp);
|
||||
struct resource *mem0, *mem1;
|
||||
|
||||
if (m->mccr0 & MCCR0_MCE)
|
||||
dev_warn(&dev->dev,
|
||||
"device left active (missing disable call?)\n");
|
||||
|
||||
mem0 = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
||||
mem1 = platform_get_resource(dev, IORESOURCE_MEM, 1);
|
||||
|
||||
platform_set_drvdata(dev, NULL);
|
||||
mcp_host_unregister(mcp);
|
||||
release_mem_region(0x80060000, 0x60);
|
||||
mcp_host_del(mcp);
|
||||
iounmap(m->base1);
|
||||
iounmap(m->base0);
|
||||
mcp_host_free(mcp);
|
||||
release_mem_region(mem1->start, resource_size(mem1));
|
||||
release_mem_region(mem0->start, resource_size(mem0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcp_sa11x0_suspend(struct platform_device *dev, pm_message_t state)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mcp_sa11x0_suspend(struct device *dev)
|
||||
{
|
||||
struct mcp *mcp = platform_get_drvdata(dev);
|
||||
struct mcp_sa11x0 *m = priv(dev_get_drvdata(dev));
|
||||
|
||||
priv(mcp)->mccr0 = Ser4MCCR0;
|
||||
priv(mcp)->mccr1 = Ser4MCCR1;
|
||||
Ser4MCCR0 &= ~MCCR0_MCE;
|
||||
if (m->mccr0 & MCCR0_MCE)
|
||||
dev_warn(dev, "device left active (missing disable call?)\n");
|
||||
|
||||
writel(m->mccr0 & ~MCCR0_MCE, MCCR0(m));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcp_sa11x0_resume(struct platform_device *dev)
|
||||
static int mcp_sa11x0_resume(struct device *dev)
|
||||
{
|
||||
struct mcp *mcp = platform_get_drvdata(dev);
|
||||
struct mcp_sa11x0 *m = priv(dev_get_drvdata(dev));
|
||||
|
||||
Ser4MCCR1 = priv(mcp)->mccr1;
|
||||
Ser4MCCR0 = priv(mcp)->mccr0;
|
||||
writel_relaxed(m->mccr1, MCCR1(m));
|
||||
writel_relaxed(m->mccr0, MCCR0(m));
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The driver for the SA11x0 MCP port.
|
||||
*/
|
||||
MODULE_ALIAS("platform:sa11x0-mcp");
|
||||
static const struct dev_pm_ops mcp_sa11x0_pm_ops = {
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.suspend = mcp_sa11x0_suspend,
|
||||
.freeze = mcp_sa11x0_suspend,
|
||||
.poweroff = mcp_sa11x0_suspend,
|
||||
.resume_noirq = mcp_sa11x0_resume,
|
||||
.thaw_noirq = mcp_sa11x0_resume,
|
||||
.restore_noirq = mcp_sa11x0_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
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",
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &mcp_sa11x0_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -259,6 +314,7 @@ static struct platform_driver mcp_sa11x0_driver = {
|
|||
*/
|
||||
module_platform_driver(mcp_sa11x0_driver);
|
||||
|
||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
||||
MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
|
||||
MODULE_DESCRIPTION("SA11x0 multimedia communications port driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -11,14 +11,15 @@
|
|||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/gpio_keys.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/mfd/ucb1x00.h>
|
||||
|
||||
#include <mach/dma.h>
|
||||
|
||||
|
||||
#define UCB1X00_ATTR(name,input)\
|
||||
static ssize_t name##_show(struct device *dev, struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
|
@ -38,14 +39,45 @@ UCB1X00_ATTR(batt_temp, UCB_ADC_INP_AD2);
|
|||
|
||||
static int ucb1x00_assabet_add(struct ucb1x00_dev *dev)
|
||||
{
|
||||
device_create_file(&dev->ucb->dev, &dev_attr_vbatt);
|
||||
device_create_file(&dev->ucb->dev, &dev_attr_vcharger);
|
||||
device_create_file(&dev->ucb->dev, &dev_attr_batt_temp);
|
||||
struct ucb1x00 *ucb = dev->ucb;
|
||||
struct platform_device *pdev;
|
||||
struct gpio_keys_platform_data keys;
|
||||
static struct gpio_keys_button buttons[6];
|
||||
unsigned i;
|
||||
|
||||
memset(buttons, 0, sizeof(buttons));
|
||||
memset(&keys, 0, sizeof(keys));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(buttons); i++) {
|
||||
buttons[i].code = BTN_0 + i;
|
||||
buttons[i].gpio = ucb->gpio.base + i;
|
||||
buttons[i].type = EV_KEY;
|
||||
buttons[i].can_disable = true;
|
||||
}
|
||||
|
||||
keys.buttons = buttons;
|
||||
keys.nbuttons = ARRAY_SIZE(buttons);
|
||||
keys.poll_interval = 50;
|
||||
keys.name = "ucb1x00";
|
||||
|
||||
pdev = platform_device_register_data(&ucb->dev, "gpio-keys", -1,
|
||||
&keys, sizeof(keys));
|
||||
|
||||
device_create_file(&ucb->dev, &dev_attr_vbatt);
|
||||
device_create_file(&ucb->dev, &dev_attr_vcharger);
|
||||
device_create_file(&ucb->dev, &dev_attr_batt_temp);
|
||||
|
||||
dev->priv = pdev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ucb1x00_assabet_remove(struct ucb1x00_dev *dev)
|
||||
{
|
||||
struct platform_device *pdev = dev->priv;
|
||||
|
||||
if (!IS_ERR(pdev))
|
||||
platform_device_unregister(pdev);
|
||||
|
||||
device_remove_file(&dev->ucb->dev, &dev_attr_batt_temp);
|
||||
device_remove_file(&dev->ucb->dev, &dev_attr_vcharger);
|
||||
device_remove_file(&dev->ucb->dev, &dev_attr_vbatt);
|
||||
|
|
|
@ -23,14 +23,12 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/mfd/ucb1x00.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/semaphore.h>
|
||||
|
||||
#include <mach/dma.h>
|
||||
#include <mach/hardware.h>
|
||||
|
||||
static DEFINE_MUTEX(ucb1x00_mutex);
|
||||
static LIST_HEAD(ucb1x00_drivers);
|
||||
|
@ -102,7 +100,7 @@ void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int set, unsigned int clear)
|
|||
* ucb1x00_enable must have been called to enable the comms
|
||||
* before using this function.
|
||||
*
|
||||
* This function does not take any semaphores or spinlocks.
|
||||
* This function does not take any mutexes or spinlocks.
|
||||
*/
|
||||
unsigned int ucb1x00_io_read(struct ucb1x00 *ucb)
|
||||
{
|
||||
|
@ -120,14 +118,22 @@ static void ucb1x00_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
|||
else
|
||||
ucb->io_out &= ~(1 << offset);
|
||||
|
||||
ucb1x00_enable(ucb);
|
||||
ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out);
|
||||
ucb1x00_disable(ucb);
|
||||
spin_unlock_irqrestore(&ucb->io_lock, flags);
|
||||
}
|
||||
|
||||
static int ucb1x00_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio);
|
||||
return ucb1x00_reg_read(ucb, UCB_IO_DATA) & (1 << offset);
|
||||
unsigned val;
|
||||
|
||||
ucb1x00_enable(ucb);
|
||||
val = ucb1x00_reg_read(ucb, UCB_IO_DATA);
|
||||
ucb1x00_disable(ucb);
|
||||
|
||||
return val & (1 << offset);
|
||||
}
|
||||
|
||||
static int ucb1x00_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
|
||||
|
@ -137,7 +143,9 @@ static int ucb1x00_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
|
|||
|
||||
spin_lock_irqsave(&ucb->io_lock, flags);
|
||||
ucb->io_dir &= ~(1 << offset);
|
||||
ucb1x00_enable(ucb);
|
||||
ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir);
|
||||
ucb1x00_disable(ucb);
|
||||
spin_unlock_irqrestore(&ucb->io_lock, flags);
|
||||
|
||||
return 0;
|
||||
|
@ -157,6 +165,7 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset
|
|||
else
|
||||
ucb->io_out &= ~mask;
|
||||
|
||||
ucb1x00_enable(ucb);
|
||||
if (old != ucb->io_out)
|
||||
ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out);
|
||||
|
||||
|
@ -164,11 +173,19 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset
|
|||
ucb->io_dir |= mask;
|
||||
ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir);
|
||||
}
|
||||
ucb1x00_disable(ucb);
|
||||
spin_unlock_irqrestore(&ucb->io_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ucb1x00_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio);
|
||||
|
||||
return ucb->irq_base > 0 ? ucb->irq_base + offset : -ENXIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* UCB1300 data sheet says we must:
|
||||
* 1. enable ADC => 5us (including reference startup time)
|
||||
|
@ -186,7 +203,7 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset
|
|||
* Any code wishing to use the ADC converter must call this
|
||||
* function prior to using it.
|
||||
*
|
||||
* This function takes the ADC semaphore to prevent two or more
|
||||
* This function takes the ADC mutex to prevent two or more
|
||||
* concurrent uses, and therefore may sleep. As a result, it
|
||||
* can only be called from process context, not interrupt
|
||||
* context.
|
||||
|
@ -196,7 +213,7 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset
|
|||
*/
|
||||
void ucb1x00_adc_enable(struct ucb1x00 *ucb)
|
||||
{
|
||||
down(&ucb->adc_sem);
|
||||
mutex_lock(&ucb->adc_mutex);
|
||||
|
||||
ucb->adc_cr |= UCB_ADC_ENA;
|
||||
|
||||
|
@ -218,7 +235,7 @@ void ucb1x00_adc_enable(struct ucb1x00 *ucb)
|
|||
* complete (2 frames max without sync).
|
||||
*
|
||||
* If called for a synchronised ADC conversion, it may sleep
|
||||
* with the ADC semaphore held.
|
||||
* with the ADC mutex held.
|
||||
*/
|
||||
unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync)
|
||||
{
|
||||
|
@ -246,7 +263,7 @@ unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync)
|
|||
* ucb1x00_adc_disable - disable the ADC converter
|
||||
* @ucb: UCB1x00 structure describing chip
|
||||
*
|
||||
* Disable the ADC converter and release the ADC semaphore.
|
||||
* Disable the ADC converter and release the ADC mutex.
|
||||
*/
|
||||
void ucb1x00_adc_disable(struct ucb1x00 *ucb)
|
||||
{
|
||||
|
@ -254,7 +271,7 @@ void ucb1x00_adc_disable(struct ucb1x00 *ucb)
|
|||
ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr);
|
||||
ucb1x00_disable(ucb);
|
||||
|
||||
up(&ucb->adc_sem);
|
||||
mutex_unlock(&ucb->adc_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -265,10 +282,9 @@ void ucb1x00_adc_disable(struct ucb1x00 *ucb)
|
|||
* SIBCLK to talk to the chip. We leave the clock running until
|
||||
* we have finished processing all interrupts from the chip.
|
||||
*/
|
||||
static irqreturn_t ucb1x00_irq(int irqnr, void *devid)
|
||||
static void ucb1x00_irq(unsigned int irq, struct irq_desc *desc)
|
||||
{
|
||||
struct ucb1x00 *ucb = devid;
|
||||
struct ucb1x00_irq *irq;
|
||||
struct ucb1x00 *ucb = irq_desc_get_handler_data(desc);
|
||||
unsigned int isr, i;
|
||||
|
||||
ucb1x00_enable(ucb);
|
||||
|
@ -276,157 +292,104 @@ static irqreturn_t ucb1x00_irq(int irqnr, void *devid)
|
|||
ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr);
|
||||
ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0);
|
||||
|
||||
for (i = 0, irq = ucb->irq_handler; i < 16 && isr; i++, isr >>= 1, irq++)
|
||||
if (isr & 1 && irq->fn)
|
||||
irq->fn(i, irq->devid);
|
||||
for (i = 0; i < 16 && isr; i++, isr >>= 1, irq++)
|
||||
if (isr & 1)
|
||||
generic_handle_irq(ucb->irq_base + i);
|
||||
ucb1x00_disable(ucb);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* ucb1x00_hook_irq - hook a UCB1x00 interrupt
|
||||
* @ucb: UCB1x00 structure describing chip
|
||||
* @idx: interrupt index
|
||||
* @fn: function to call when interrupt is triggered
|
||||
* @devid: device id to pass to interrupt handler
|
||||
*
|
||||
* Hook the specified interrupt. You can only register one handler
|
||||
* for each interrupt source. The interrupt source is not enabled
|
||||
* by this function; use ucb1x00_enable_irq instead.
|
||||
*
|
||||
* Interrupt handlers will be called with other interrupts enabled.
|
||||
*
|
||||
* Returns zero on success, or one of the following errors:
|
||||
* -EINVAL if the interrupt index is invalid
|
||||
* -EBUSY if the interrupt has already been hooked
|
||||
*/
|
||||
int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid)
|
||||
static void ucb1x00_irq_update(struct ucb1x00 *ucb, unsigned mask)
|
||||
{
|
||||
struct ucb1x00_irq *irq;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (idx < 16) {
|
||||
irq = ucb->irq_handler + idx;
|
||||
ret = -EBUSY;
|
||||
|
||||
spin_lock_irq(&ucb->lock);
|
||||
if (irq->fn == NULL) {
|
||||
irq->devid = devid;
|
||||
irq->fn = fn;
|
||||
ret = 0;
|
||||
}
|
||||
spin_unlock_irq(&ucb->lock);
|
||||
}
|
||||
return ret;
|
||||
ucb1x00_enable(ucb);
|
||||
if (ucb->irq_ris_enbl & mask)
|
||||
ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl &
|
||||
ucb->irq_mask);
|
||||
if (ucb->irq_fal_enbl & mask)
|
||||
ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl &
|
||||
ucb->irq_mask);
|
||||
ucb1x00_disable(ucb);
|
||||
}
|
||||
|
||||
/**
|
||||
* ucb1x00_enable_irq - enable an UCB1x00 interrupt source
|
||||
* @ucb: UCB1x00 structure describing chip
|
||||
* @idx: interrupt index
|
||||
* @edges: interrupt edges to enable
|
||||
*
|
||||
* Enable the specified interrupt to trigger on %UCB_RISING,
|
||||
* %UCB_FALLING or both edges. The interrupt should have been
|
||||
* hooked by ucb1x00_hook_irq.
|
||||
*/
|
||||
void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges)
|
||||
static void ucb1x00_irq_noop(struct irq_data *data)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (idx < 16) {
|
||||
spin_lock_irqsave(&ucb->lock, flags);
|
||||
|
||||
ucb1x00_enable(ucb);
|
||||
if (edges & UCB_RISING) {
|
||||
ucb->irq_ris_enbl |= 1 << idx;
|
||||
ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
|
||||
}
|
||||
if (edges & UCB_FALLING) {
|
||||
ucb->irq_fal_enbl |= 1 << idx;
|
||||
ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
|
||||
}
|
||||
ucb1x00_disable(ucb);
|
||||
spin_unlock_irqrestore(&ucb->lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ucb1x00_disable_irq - disable an UCB1x00 interrupt source
|
||||
* @ucb: UCB1x00 structure describing chip
|
||||
* @edges: interrupt edges to disable
|
||||
*
|
||||
* Disable the specified interrupt triggering on the specified
|
||||
* (%UCB_RISING, %UCB_FALLING or both) edges.
|
||||
*/
|
||||
void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges)
|
||||
static void ucb1x00_irq_mask(struct irq_data *data)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data);
|
||||
unsigned mask = 1 << (data->irq - ucb->irq_base);
|
||||
|
||||
if (idx < 16) {
|
||||
spin_lock_irqsave(&ucb->lock, flags);
|
||||
|
||||
ucb1x00_enable(ucb);
|
||||
if (edges & UCB_RISING) {
|
||||
ucb->irq_ris_enbl &= ~(1 << idx);
|
||||
ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
|
||||
}
|
||||
if (edges & UCB_FALLING) {
|
||||
ucb->irq_fal_enbl &= ~(1 << idx);
|
||||
ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
|
||||
}
|
||||
ucb1x00_disable(ucb);
|
||||
spin_unlock_irqrestore(&ucb->lock, flags);
|
||||
}
|
||||
raw_spin_lock(&ucb->irq_lock);
|
||||
ucb->irq_mask &= ~mask;
|
||||
ucb1x00_irq_update(ucb, mask);
|
||||
raw_spin_unlock(&ucb->irq_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* ucb1x00_free_irq - disable and free the specified UCB1x00 interrupt
|
||||
* @ucb: UCB1x00 structure describing chip
|
||||
* @idx: interrupt index
|
||||
* @devid: device id.
|
||||
*
|
||||
* Disable the interrupt source and remove the handler. devid must
|
||||
* match the devid passed when hooking the interrupt.
|
||||
*
|
||||
* Returns zero on success, or one of the following errors:
|
||||
* -EINVAL if the interrupt index is invalid
|
||||
* -ENOENT if devid does not match
|
||||
*/
|
||||
int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid)
|
||||
static void ucb1x00_irq_unmask(struct irq_data *data)
|
||||
{
|
||||
struct ucb1x00_irq *irq;
|
||||
int ret;
|
||||
struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data);
|
||||
unsigned mask = 1 << (data->irq - ucb->irq_base);
|
||||
|
||||
if (idx >= 16)
|
||||
goto bad;
|
||||
|
||||
irq = ucb->irq_handler + idx;
|
||||
ret = -ENOENT;
|
||||
|
||||
spin_lock_irq(&ucb->lock);
|
||||
if (irq->devid == devid) {
|
||||
ucb->irq_ris_enbl &= ~(1 << idx);
|
||||
ucb->irq_fal_enbl &= ~(1 << idx);
|
||||
|
||||
ucb1x00_enable(ucb);
|
||||
ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
|
||||
ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
|
||||
ucb1x00_disable(ucb);
|
||||
|
||||
irq->fn = NULL;
|
||||
irq->devid = NULL;
|
||||
ret = 0;
|
||||
}
|
||||
spin_unlock_irq(&ucb->lock);
|
||||
return ret;
|
||||
|
||||
bad:
|
||||
printk(KERN_ERR "Freeing bad UCB1x00 irq %d\n", idx);
|
||||
return -EINVAL;
|
||||
raw_spin_lock(&ucb->irq_lock);
|
||||
ucb->irq_mask |= mask;
|
||||
ucb1x00_irq_update(ucb, mask);
|
||||
raw_spin_unlock(&ucb->irq_lock);
|
||||
}
|
||||
|
||||
static int ucb1x00_irq_set_type(struct irq_data *data, unsigned int type)
|
||||
{
|
||||
struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data);
|
||||
unsigned mask = 1 << (data->irq - ucb->irq_base);
|
||||
|
||||
raw_spin_lock(&ucb->irq_lock);
|
||||
if (type & IRQ_TYPE_EDGE_RISING)
|
||||
ucb->irq_ris_enbl |= mask;
|
||||
else
|
||||
ucb->irq_ris_enbl &= ~mask;
|
||||
|
||||
if (type & IRQ_TYPE_EDGE_FALLING)
|
||||
ucb->irq_fal_enbl |= mask;
|
||||
else
|
||||
ucb->irq_fal_enbl &= ~mask;
|
||||
if (ucb->irq_mask & mask) {
|
||||
ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl &
|
||||
ucb->irq_mask);
|
||||
ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl &
|
||||
ucb->irq_mask);
|
||||
}
|
||||
raw_spin_unlock(&ucb->irq_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ucb1x00_irq_set_wake(struct irq_data *data, unsigned int on)
|
||||
{
|
||||
struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data);
|
||||
struct ucb1x00_plat_data *pdata = ucb->mcp->attached_device.platform_data;
|
||||
unsigned mask = 1 << (data->irq - ucb->irq_base);
|
||||
|
||||
if (!pdata || !pdata->can_wakeup)
|
||||
return -EINVAL;
|
||||
|
||||
raw_spin_lock(&ucb->irq_lock);
|
||||
if (on)
|
||||
ucb->irq_wake |= mask;
|
||||
else
|
||||
ucb->irq_wake &= ~mask;
|
||||
raw_spin_unlock(&ucb->irq_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_chip ucb1x00_irqchip = {
|
||||
.name = "ucb1x00",
|
||||
.irq_ack = ucb1x00_irq_noop,
|
||||
.irq_mask = ucb1x00_irq_mask,
|
||||
.irq_unmask = ucb1x00_irq_unmask,
|
||||
.irq_set_type = ucb1x00_irq_set_type,
|
||||
.irq_set_wake = ucb1x00_irq_set_wake,
|
||||
};
|
||||
|
||||
static int ucb1x00_add_dev(struct ucb1x00 *ucb, struct ucb1x00_driver *drv)
|
||||
{
|
||||
struct ucb1x00_dev *dev;
|
||||
|
@ -440,8 +403,8 @@ static int ucb1x00_add_dev(struct ucb1x00 *ucb, struct ucb1x00_driver *drv)
|
|||
ret = drv->add(dev);
|
||||
|
||||
if (ret == 0) {
|
||||
list_add(&dev->dev_node, &ucb->devs);
|
||||
list_add(&dev->drv_node, &drv->devs);
|
||||
list_add_tail(&dev->dev_node, &ucb->devs);
|
||||
list_add_tail(&dev->drv_node, &drv->devs);
|
||||
} else {
|
||||
kfree(dev);
|
||||
}
|
||||
|
@ -533,98 +496,126 @@ static struct class ucb1x00_class = {
|
|||
|
||||
static int ucb1x00_probe(struct mcp *mcp)
|
||||
{
|
||||
struct ucb1x00 *ucb;
|
||||
struct ucb1x00_plat_data *pdata = mcp->attached_device.platform_data;
|
||||
struct ucb1x00_driver *drv;
|
||||
unsigned int id;
|
||||
struct ucb1x00 *ucb;
|
||||
unsigned id, i, irq_base;
|
||||
int ret = -ENODEV;
|
||||
int temp;
|
||||
|
||||
/* Tell the platform to deassert the UCB1x00 reset */
|
||||
if (pdata && pdata->reset)
|
||||
pdata->reset(UCB_RST_PROBE);
|
||||
|
||||
mcp_enable(mcp);
|
||||
id = mcp_reg_read(mcp, UCB_ID);
|
||||
mcp_disable(mcp);
|
||||
|
||||
if (id != UCB_ID_1200 && id != UCB_ID_1300 && id != UCB_ID_TC35143) {
|
||||
printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id);
|
||||
goto err_disable;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ucb = kzalloc(sizeof(struct ucb1x00), GFP_KERNEL);
|
||||
ret = -ENOMEM;
|
||||
if (!ucb)
|
||||
goto err_disable;
|
||||
|
||||
goto out;
|
||||
|
||||
device_initialize(&ucb->dev);
|
||||
ucb->dev.class = &ucb1x00_class;
|
||||
ucb->dev.parent = &mcp->attached_device;
|
||||
dev_set_name(&ucb->dev, "ucb1x00");
|
||||
|
||||
spin_lock_init(&ucb->lock);
|
||||
raw_spin_lock_init(&ucb->irq_lock);
|
||||
spin_lock_init(&ucb->io_lock);
|
||||
sema_init(&ucb->adc_sem, 1);
|
||||
mutex_init(&ucb->adc_mutex);
|
||||
|
||||
ucb->id = id;
|
||||
ucb->mcp = mcp;
|
||||
|
||||
ret = device_add(&ucb->dev);
|
||||
if (ret)
|
||||
goto err_dev_add;
|
||||
|
||||
ucb1x00_enable(ucb);
|
||||
ucb->irq = ucb1x00_detect_irq(ucb);
|
||||
ucb1x00_disable(ucb);
|
||||
if (ucb->irq == NO_IRQ) {
|
||||
printk(KERN_ERR "UCB1x00: IRQ probe failed\n");
|
||||
dev_err(&ucb->dev, "IRQ probe failed\n");
|
||||
ret = -ENODEV;
|
||||
goto err_free;
|
||||
goto err_no_irq;
|
||||
}
|
||||
|
||||
ucb->gpio.base = -1;
|
||||
if (mcp->gpio_base != 0) {
|
||||
irq_base = pdata ? pdata->irq_base : 0;
|
||||
ucb->irq_base = irq_alloc_descs(-1, irq_base, 16, -1);
|
||||
if (ucb->irq_base < 0) {
|
||||
dev_err(&ucb->dev, "unable to allocate 16 irqs: %d\n",
|
||||
ucb->irq_base);
|
||||
goto err_irq_alloc;
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
unsigned irq = ucb->irq_base + i;
|
||||
|
||||
irq_set_chip_and_handler(irq, &ucb1x00_irqchip, handle_edge_irq);
|
||||
irq_set_chip_data(irq, ucb);
|
||||
set_irq_flags(irq, IRQF_VALID | IRQ_NOREQUEST);
|
||||
}
|
||||
|
||||
irq_set_irq_type(ucb->irq, IRQ_TYPE_EDGE_RISING);
|
||||
irq_set_handler_data(ucb->irq, ucb);
|
||||
irq_set_chained_handler(ucb->irq, ucb1x00_irq);
|
||||
|
||||
if (pdata && pdata->gpio_base) {
|
||||
ucb->gpio.label = dev_name(&ucb->dev);
|
||||
ucb->gpio.base = mcp->gpio_base;
|
||||
ucb->gpio.dev = &ucb->dev;
|
||||
ucb->gpio.owner = THIS_MODULE;
|
||||
ucb->gpio.base = pdata->gpio_base;
|
||||
ucb->gpio.ngpio = 10;
|
||||
ucb->gpio.set = ucb1x00_gpio_set;
|
||||
ucb->gpio.get = ucb1x00_gpio_get;
|
||||
ucb->gpio.direction_input = ucb1x00_gpio_direction_input;
|
||||
ucb->gpio.direction_output = ucb1x00_gpio_direction_output;
|
||||
ucb->gpio.to_irq = ucb1x00_to_irq;
|
||||
ret = gpiochip_add(&ucb->gpio);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
goto err_gpio_add;
|
||||
} else
|
||||
dev_info(&ucb->dev, "gpio_base not set so no gpiolib support");
|
||||
|
||||
ret = request_irq(ucb->irq, ucb1x00_irq, IRQF_TRIGGER_RISING,
|
||||
"UCB1x00", ucb);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "ucb1x00: unable to grab irq%d: %d\n",
|
||||
ucb->irq, ret);
|
||||
goto err_gpio;
|
||||
}
|
||||
|
||||
mcp_set_drvdata(mcp, ucb);
|
||||
|
||||
ret = device_register(&ucb->dev);
|
||||
if (ret)
|
||||
goto err_irq;
|
||||
|
||||
if (pdata)
|
||||
device_set_wakeup_capable(&ucb->dev, pdata->can_wakeup);
|
||||
|
||||
INIT_LIST_HEAD(&ucb->devs);
|
||||
mutex_lock(&ucb1x00_mutex);
|
||||
list_add(&ucb->node, &ucb1x00_devices);
|
||||
list_add_tail(&ucb->node, &ucb1x00_devices);
|
||||
list_for_each_entry(drv, &ucb1x00_drivers, node) {
|
||||
ucb1x00_add_dev(ucb, drv);
|
||||
}
|
||||
mutex_unlock(&ucb1x00_mutex);
|
||||
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
err_irq:
|
||||
free_irq(ucb->irq, ucb);
|
||||
err_gpio:
|
||||
if (ucb->gpio.base != -1)
|
||||
temp = gpiochip_remove(&ucb->gpio);
|
||||
err_free:
|
||||
kfree(ucb);
|
||||
err_disable:
|
||||
mcp_disable(mcp);
|
||||
err_gpio_add:
|
||||
irq_set_chained_handler(ucb->irq, NULL);
|
||||
err_irq_alloc:
|
||||
if (ucb->irq_base > 0)
|
||||
irq_free_descs(ucb->irq_base, 16);
|
||||
err_no_irq:
|
||||
device_del(&ucb->dev);
|
||||
err_dev_add:
|
||||
put_device(&ucb->dev);
|
||||
out:
|
||||
if (pdata && pdata->reset)
|
||||
pdata->reset(UCB_RST_PROBE_FAIL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ucb1x00_remove(struct mcp *mcp)
|
||||
{
|
||||
struct ucb1x00_plat_data *pdata = mcp->attached_device.platform_data;
|
||||
struct ucb1x00 *ucb = mcp_get_drvdata(mcp);
|
||||
struct list_head *l, *n;
|
||||
int ret;
|
||||
|
@ -643,8 +634,12 @@ static void ucb1x00_remove(struct mcp *mcp)
|
|||
dev_err(&ucb->dev, "Can't remove gpio chip: %d\n", ret);
|
||||
}
|
||||
|
||||
free_irq(ucb->irq, ucb);
|
||||
irq_set_chained_handler(ucb->irq, NULL);
|
||||
irq_free_descs(ucb->irq_base, 16);
|
||||
device_unregister(&ucb->dev);
|
||||
|
||||
if (pdata && pdata->reset)
|
||||
pdata->reset(UCB_RST_REMOVE);
|
||||
}
|
||||
|
||||
int ucb1x00_register_driver(struct ucb1x00_driver *drv)
|
||||
|
@ -653,7 +648,7 @@ int ucb1x00_register_driver(struct ucb1x00_driver *drv)
|
|||
|
||||
INIT_LIST_HEAD(&drv->devs);
|
||||
mutex_lock(&ucb1x00_mutex);
|
||||
list_add(&drv->node, &ucb1x00_drivers);
|
||||
list_add_tail(&drv->node, &ucb1x00_drivers);
|
||||
list_for_each_entry(ucb, &ucb1x00_devices, node) {
|
||||
ucb1x00_add_dev(ucb, drv);
|
||||
}
|
||||
|
@ -674,44 +669,86 @@ void ucb1x00_unregister_driver(struct ucb1x00_driver *drv)
|
|||
mutex_unlock(&ucb1x00_mutex);
|
||||
}
|
||||
|
||||
static int ucb1x00_suspend(struct mcp *mcp, pm_message_t state)
|
||||
static int ucb1x00_suspend(struct device *dev)
|
||||
{
|
||||
struct ucb1x00 *ucb = mcp_get_drvdata(mcp);
|
||||
struct ucb1x00_dev *dev;
|
||||
struct ucb1x00_plat_data *pdata = dev->platform_data;
|
||||
struct ucb1x00 *ucb = dev_get_drvdata(dev);
|
||||
struct ucb1x00_dev *udev;
|
||||
|
||||
mutex_lock(&ucb1x00_mutex);
|
||||
list_for_each_entry(dev, &ucb->devs, dev_node) {
|
||||
if (dev->drv->suspend)
|
||||
dev->drv->suspend(dev, state);
|
||||
list_for_each_entry(udev, &ucb->devs, dev_node) {
|
||||
if (udev->drv->suspend)
|
||||
udev->drv->suspend(udev);
|
||||
}
|
||||
mutex_unlock(&ucb1x00_mutex);
|
||||
|
||||
if (ucb->irq_wake) {
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&ucb->irq_lock, flags);
|
||||
ucb1x00_enable(ucb);
|
||||
ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl &
|
||||
ucb->irq_wake);
|
||||
ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl &
|
||||
ucb->irq_wake);
|
||||
ucb1x00_disable(ucb);
|
||||
raw_spin_unlock_irqrestore(&ucb->irq_lock, flags);
|
||||
|
||||
enable_irq_wake(ucb->irq);
|
||||
} else if (pdata && pdata->reset)
|
||||
pdata->reset(UCB_RST_SUSPEND);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ucb1x00_resume(struct mcp *mcp)
|
||||
static int ucb1x00_resume(struct device *dev)
|
||||
{
|
||||
struct ucb1x00 *ucb = mcp_get_drvdata(mcp);
|
||||
struct ucb1x00_dev *dev;
|
||||
struct ucb1x00_plat_data *pdata = dev->platform_data;
|
||||
struct ucb1x00 *ucb = dev_get_drvdata(dev);
|
||||
struct ucb1x00_dev *udev;
|
||||
|
||||
if (!ucb->irq_wake && pdata && pdata->reset)
|
||||
pdata->reset(UCB_RST_RESUME);
|
||||
|
||||
ucb1x00_enable(ucb);
|
||||
ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out);
|
||||
ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir);
|
||||
|
||||
if (ucb->irq_wake) {
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&ucb->irq_lock, flags);
|
||||
ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl &
|
||||
ucb->irq_mask);
|
||||
ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl &
|
||||
ucb->irq_mask);
|
||||
raw_spin_unlock_irqrestore(&ucb->irq_lock, flags);
|
||||
|
||||
disable_irq_wake(ucb->irq);
|
||||
}
|
||||
ucb1x00_disable(ucb);
|
||||
|
||||
mutex_lock(&ucb1x00_mutex);
|
||||
list_for_each_entry(dev, &ucb->devs, dev_node) {
|
||||
if (dev->drv->resume)
|
||||
dev->drv->resume(dev);
|
||||
list_for_each_entry(udev, &ucb->devs, dev_node) {
|
||||
if (udev->drv->resume)
|
||||
udev->drv->resume(udev);
|
||||
}
|
||||
mutex_unlock(&ucb1x00_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops ucb1x00_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(ucb1x00_suspend, ucb1x00_resume)
|
||||
};
|
||||
|
||||
static struct mcp_driver ucb1x00_driver = {
|
||||
.drv = {
|
||||
.name = "ucb1x00",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &ucb1x00_pm_ops,
|
||||
},
|
||||
.probe = ucb1x00_probe,
|
||||
.remove = ucb1x00_remove,
|
||||
.suspend = ucb1x00_suspend,
|
||||
.resume = ucb1x00_resume,
|
||||
};
|
||||
|
||||
static int __init ucb1x00_init(void)
|
||||
|
@ -742,14 +779,10 @@ EXPORT_SYMBOL(ucb1x00_adc_enable);
|
|||
EXPORT_SYMBOL(ucb1x00_adc_read);
|
||||
EXPORT_SYMBOL(ucb1x00_adc_disable);
|
||||
|
||||
EXPORT_SYMBOL(ucb1x00_hook_irq);
|
||||
EXPORT_SYMBOL(ucb1x00_free_irq);
|
||||
EXPORT_SYMBOL(ucb1x00_enable_irq);
|
||||
EXPORT_SYMBOL(ucb1x00_disable_irq);
|
||||
|
||||
EXPORT_SYMBOL(ucb1x00_register_driver);
|
||||
EXPORT_SYMBOL(ucb1x00_unregister_driver);
|
||||
|
||||
MODULE_ALIAS("mcp:ucb1x00");
|
||||
MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
|
||||
MODULE_DESCRIPTION("UCB1x00 core driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -20,8 +20,9 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/string.h>
|
||||
|
@ -32,7 +33,6 @@
|
|||
#include <linux/kthread.h>
|
||||
#include <linux/mfd/ucb1x00.h>
|
||||
|
||||
#include <mach/dma.h>
|
||||
#include <mach/collie.h>
|
||||
#include <asm/mach-types.h>
|
||||
|
||||
|
@ -42,6 +42,8 @@ struct ucb1x00_ts {
|
|||
struct input_dev *idev;
|
||||
struct ucb1x00 *ucb;
|
||||
|
||||
spinlock_t irq_lock;
|
||||
unsigned irq_disabled;
|
||||
wait_queue_head_t irq_wait;
|
||||
struct task_struct *rtask;
|
||||
u16 x_res;
|
||||
|
@ -238,7 +240,12 @@ static int ucb1x00_thread(void *_ts)
|
|||
if (ucb1x00_ts_pen_down(ts)) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, machine_is_collie() ? UCB_RISING : UCB_FALLING);
|
||||
spin_lock_irq(&ts->irq_lock);
|
||||
if (ts->irq_disabled) {
|
||||
ts->irq_disabled = 0;
|
||||
enable_irq(ts->ucb->irq_base + UCB_IRQ_TSPX);
|
||||
}
|
||||
spin_unlock_irq(&ts->irq_lock);
|
||||
ucb1x00_disable(ts->ucb);
|
||||
|
||||
/*
|
||||
|
@ -281,23 +288,37 @@ static int ucb1x00_thread(void *_ts)
|
|||
* We only detect touch screen _touches_ with this interrupt
|
||||
* handler, and even then we just schedule our task.
|
||||
*/
|
||||
static void ucb1x00_ts_irq(int idx, void *id)
|
||||
static irqreturn_t ucb1x00_ts_irq(int irq, void *id)
|
||||
{
|
||||
struct ucb1x00_ts *ts = id;
|
||||
|
||||
ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING);
|
||||
spin_lock(&ts->irq_lock);
|
||||
ts->irq_disabled = 1;
|
||||
disable_irq_nosync(ts->ucb->irq_base + UCB_IRQ_TSPX);
|
||||
spin_unlock(&ts->irq_lock);
|
||||
wake_up(&ts->irq_wait);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ucb1x00_ts_open(struct input_dev *idev)
|
||||
{
|
||||
struct ucb1x00_ts *ts = input_get_drvdata(idev);
|
||||
unsigned long flags = 0;
|
||||
int ret = 0;
|
||||
|
||||
BUG_ON(ts->rtask);
|
||||
|
||||
if (machine_is_collie())
|
||||
flags = IRQF_TRIGGER_RISING;
|
||||
else
|
||||
flags = IRQF_TRIGGER_FALLING;
|
||||
|
||||
ts->irq_disabled = 0;
|
||||
|
||||
init_waitqueue_head(&ts->irq_wait);
|
||||
ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts);
|
||||
ret = request_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ucb1x00_ts_irq,
|
||||
flags, "ucb1x00-ts", ts);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
|
@ -314,7 +335,7 @@ static int ucb1x00_ts_open(struct input_dev *idev)
|
|||
if (!IS_ERR(ts->rtask)) {
|
||||
ret = 0;
|
||||
} else {
|
||||
ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
|
||||
free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts);
|
||||
ts->rtask = NULL;
|
||||
ret = -EFAULT;
|
||||
}
|
||||
|
@ -334,7 +355,7 @@ static void ucb1x00_ts_close(struct input_dev *idev)
|
|||
kthread_stop(ts->rtask);
|
||||
|
||||
ucb1x00_enable(ts->ucb);
|
||||
ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
|
||||
free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts);
|
||||
ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0);
|
||||
ucb1x00_disable(ts->ucb);
|
||||
}
|
||||
|
@ -359,11 +380,13 @@ static int ucb1x00_ts_add(struct ucb1x00_dev *dev)
|
|||
ts->ucb = dev->ucb;
|
||||
ts->idev = idev;
|
||||
ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC;
|
||||
spin_lock_init(&ts->irq_lock);
|
||||
|
||||
idev->name = "Touchscreen panel";
|
||||
idev->id.product = ts->ucb->id;
|
||||
idev->open = ucb1x00_ts_open;
|
||||
idev->close = ucb1x00_ts_close;
|
||||
idev->dev.parent = &ts->ucb->dev;
|
||||
|
||||
idev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
|
||||
idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
#ifndef MCP_H
|
||||
#define MCP_H
|
||||
|
||||
#include <mach/dma.h>
|
||||
|
||||
struct mcp_ops;
|
||||
|
||||
struct mcp {
|
||||
|
@ -21,12 +19,7 @@ struct mcp {
|
|||
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;
|
||||
int gpio_base;
|
||||
};
|
||||
|
||||
struct mcp_ops {
|
||||
|
@ -47,15 +40,14 @@ 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 *);
|
||||
int mcp_host_add(struct mcp *, void *);
|
||||
void mcp_host_del(struct mcp *);
|
||||
void mcp_host_free(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 *);
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
#include <linux/mfd/mcp.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#define UCB_IO_DATA 0x00
|
||||
#define UCB_IO_DIR 0x01
|
||||
|
@ -104,17 +104,27 @@
|
|||
#define UCB_MODE_DYN_VFLAG_ENA (1 << 12)
|
||||
#define UCB_MODE_AUD_OFF_CAN (1 << 13)
|
||||
|
||||
enum ucb1x00_reset {
|
||||
UCB_RST_PROBE,
|
||||
UCB_RST_RESUME,
|
||||
UCB_RST_SUSPEND,
|
||||
UCB_RST_REMOVE,
|
||||
UCB_RST_PROBE_FAIL,
|
||||
};
|
||||
|
||||
struct ucb1x00_irq {
|
||||
void *devid;
|
||||
void (*fn)(int, void *);
|
||||
struct ucb1x00_plat_data {
|
||||
void (*reset)(enum ucb1x00_reset);
|
||||
unsigned irq_base;
|
||||
int gpio_base;
|
||||
unsigned can_wakeup;
|
||||
};
|
||||
|
||||
struct ucb1x00 {
|
||||
spinlock_t lock;
|
||||
raw_spinlock_t irq_lock;
|
||||
struct mcp *mcp;
|
||||
unsigned int irq;
|
||||
struct semaphore adc_sem;
|
||||
int irq_base;
|
||||
struct mutex adc_mutex;
|
||||
spinlock_t io_lock;
|
||||
u16 id;
|
||||
u16 io_dir;
|
||||
|
@ -122,7 +132,8 @@ struct ucb1x00 {
|
|||
u16 adc_cr;
|
||||
u16 irq_fal_enbl;
|
||||
u16 irq_ris_enbl;
|
||||
struct ucb1x00_irq irq_handler[16];
|
||||
u16 irq_mask;
|
||||
u16 irq_wake;
|
||||
struct device dev;
|
||||
struct list_head node;
|
||||
struct list_head devs;
|
||||
|
@ -144,7 +155,7 @@ struct ucb1x00_driver {
|
|||
struct list_head devs;
|
||||
int (*add)(struct ucb1x00_dev *dev);
|
||||
void (*remove)(struct ucb1x00_dev *dev);
|
||||
int (*suspend)(struct ucb1x00_dev *dev, pm_message_t state);
|
||||
int (*suspend)(struct ucb1x00_dev *dev);
|
||||
int (*resume)(struct ucb1x00_dev *dev);
|
||||
};
|
||||
|
||||
|
@ -245,15 +256,4 @@ unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync);
|
|||
void ucb1x00_adc_enable(struct ucb1x00 *ucb);
|
||||
void ucb1x00_adc_disable(struct ucb1x00 *ucb);
|
||||
|
||||
/*
|
||||
* Which edges of the IRQ do you want to control today?
|
||||
*/
|
||||
#define UCB_RISING (1 << 0)
|
||||
#define UCB_FALLING (1 << 1)
|
||||
|
||||
int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid);
|
||||
void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges);
|
||||
void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges);
|
||||
int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue