Commit 7256ecc2 authored by Russell King's avatar Russell King

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