linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [Patch 06/10] mfd: PCAP driver for the Motorola EZX GSM mobile phones
       [not found] <20080707184000.411913919@datenfreihafen.org>
@ 2008-07-07 18:40 ` stefan
  2008-07-07 19:21   ` pHilipp Zabel
  0 siblings, 1 reply; 12+ messages in thread
From: stefan @ 2008-07-07 18:40 UTC (permalink / raw)
  To: linux-arm-kernel, sameo; +Cc: philipp.zabel, linux-kernel, Daniel Ribeiro

[-- Attachment #1: ezx-pcap.patch --]
[-- Type: text/plain, Size: 25597 bytes --]

The PCAP Asic as present on EZX phones is a multi function device with voltage
regulators, irq expander, touch screen controller and audio codec.
It is connected to the processor via SPI, this driver provides read/write
functions to its registers and a irq demultiplexer.

Signed-off-by: Daniel Ribeiro <wyrm@openezx.org>

PATCH FOLLOWS
KernelVersion: 2.6-arm-git pxa branch

Index: linux-2.6-arm/drivers/mfd/ezx-pcap.c
===================================================================
--- /dev/null
+++ linux-2.6-arm/drivers/mfd/ezx-pcap.c
@@ -0,0 +1,402 @@
+/* Driver for Motorola PCAP2 as present in EZX phones
+ *
+ * This is both a SPI device driver for PCAP itself, as well as
+ * an IRQ demultiplexer for handling PCAP generated events such as
+ * headphone jack sense by downstream drivers.
+ *
+ * Copyright (C) 2006 Harald Welte <laforge@openezx.org>
+ * Copyright (C) 2007-2008 Daniel Ribeiro <wyrm@openezx.org>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <linux/proc_fs.h>
+#include <linux/mfd/ezx-pcap.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+
+#include <asm/arch/ssp.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/regs-ssp.h>
+#include <asm/arch/mfp-pxa27x.h>
+#include <asm/arch/irqs.h>
+#include <asm/arch/mmc.h>
+#include <asm/mach/irq.h>
+
+#if 0
+#define DEBUGP(x, args...) printk(x, ## args)
+#else
+#define DEBUGP(x, args...)
+#endif
+
+static DEFINE_SPINLOCK(ezx_ssp_lock);
+static struct ssp_dev ezx_ssp_dev;
+static struct ssp_state ezx_ssp_state;
+static struct pcap_platform_data *pcap_data;
+static int pcap_irq;
+
+static u_int32_t ezx_ssp_pcap_putget(u_int32_t data)
+{
+	unsigned long flag;
+	u_int32_t ret = 0;
+
+	spin_lock_irqsave(&ezx_ssp_lock, flag);
+	if (pcap_data->cs >= 0) {
+		if (machine_is_ezx_a780() || machine_is_ezx_e680())
+			gpio_set_value(pcap_data->cs, 0);
+		else
+			gpio_set_value(pcap_data->cs, 1);
+	}
+
+	ssp_write_word(&ezx_ssp_dev, data);
+	ssp_read_word(&ezx_ssp_dev, &ret);
+
+	if (pcap_data->cs >= 0) {
+		if (machine_is_ezx_a780() || machine_is_ezx_e680())
+			gpio_set_value(pcap_data->cs, 1);
+		else
+			gpio_set_value(pcap_data->cs, 0);
+	}
+
+	spin_unlock_irqrestore(&ezx_ssp_lock, flag);
+
+	return ret;
+}
+
+void ezx_pcap_write(u_int8_t reg_num, u_int32_t value)
+{
+	value &= PCAP_REGISTER_VALUE_MASK;
+	value |= PCAP_REGISTER_WRITE_OP_BIT
+		| (reg_num<<PCAP_REGISTER_ADDRESS_SHIFT);
+
+	ezx_ssp_pcap_putget(value);
+
+	DEBUGP("pcap write r%x: 0x%08x\n", reg_num, value);
+}
+EXPORT_SYMBOL_GPL(ezx_pcap_write);
+
+void ezx_pcap_read(u_int8_t reg_num, u_int32_t *value)
+{
+	u_int32_t frame = PCAP_REGISTER_READ_OP_BIT
+		| (reg_num<<PCAP_REGISTER_ADDRESS_SHIFT);
+
+	*value = ezx_ssp_pcap_putget(frame);
+
+	DEBUGP("pcap read r%x:  0x%08x\n", reg_num, *value);
+}
+EXPORT_SYMBOL_GPL(ezx_pcap_read);
+
+void ezx_pcap_set_sw(u_int8_t sw, u_int8_t what, u_int8_t val)
+{
+	u_int32_t tmp;
+
+	ezx_pcap_read(PCAP_REG_LOWPWR, &tmp);
+	tmp &= ~(0xf << (sw + what));
+	tmp |= ((val & 0xf) << (sw + what));
+	ezx_pcap_write(PCAP_REG_LOWPWR, tmp);
+}
+EXPORT_SYMBOL_GPL(ezx_pcap_set_sw);
+
+static u_int8_t vaux_table[][8] = {
+	/*		EN	INDEX	MASK	STBY	LOWPWR	*/
+	[VAUX1]	= {	1,	2,	0x3,	22,	23,	},
+	[VAUX2]	= {	4,	5,	0x3,	0,	1,	},
+	[VAUX3]	= {	7,	8,	0xf,	2,	3,	},
+	[VAUX4]	= {	12,	13,	0x3,	4,	5,	},
+	[VSIM]	= {	17,	18,	0x1,	0xff,	6,	},
+	[VSIM2]	= {	16,	0xff,	0x0,	0xff,	7,	},
+	[VVIB]	= {	19,	20,	0x3,	0xff,	0xff,	},
+	[VC]	= {	0xff,	0xff,	0x0,	24,	0xff,	},
+};
+
+int ezx_pcap_set_vaux(u_int8_t vaux, u_int8_t what, u_int8_t val)
+{
+	u_int8_t reg, shift, mask;
+	u_int32_t tmp;
+
+	switch (what) {
+	case VAUX_EN:
+		reg = PCAP_REG_AUXVREG;
+		shift = vaux_table[vaux][VAUX_EN];
+		mask = 0x1;
+		break;
+	case VAUX_VAL:
+		reg = PCAP_REG_AUXVREG;
+		shift = vaux_table[vaux][VAUX_VAL];
+		mask = vaux_table[vaux][VAUX_MASK];
+		break;
+	case VAUX_STBY:
+		if (vaux == VAUX1) /* exception */
+			reg = PCAP_REG_AUXVREG;
+		else
+			reg = PCAP_REG_LOWPWR;
+		shift = vaux_table[vaux][VAUX_STBY];
+		mask = 0x1;
+		break;
+	case VAUX_LOWPWR:
+		if (vaux == VAUX1)
+			reg = PCAP_REG_AUXVREG;
+		else
+			reg = PCAP_REG_LOWPWR;
+		shift = vaux_table[vaux][VAUX_LOWPWR];
+		mask = 0x1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* invalid setting */
+	if (shift == 0xff || val > mask)
+		return -EINVAL;
+
+	ezx_pcap_read(reg, &tmp);
+	tmp &= ~(mask << shift);
+	tmp |= ((val & mask) << shift);
+	ezx_pcap_write(reg, tmp);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ezx_pcap_set_vaux);
+
+/* IRQ Handling */
+
+/* Array indexed by BIT POSITION of PCAP register, returns IRQ number */
+static unsigned int pcap2irq[] = {
+	[0]     = EZX_IRQ_ADCDONE,
+	[1]     = EZX_IRQ_TS,
+	[2]     = EZX_IRQ_1HZ, /* 1HZ */
+	[3]     = EZX_IRQ_WH, /* WH */
+	[4]     = EZX_IRQ_WL, /* WL */
+	[5]     = EZX_IRQ_TODA, /* TODA */
+	[6]     = EZX_IRQ_USB4V,
+	[7]     = EZX_IRQ_ONOFF, /* ONOFF */
+	[8]     = EZX_IRQ_ONOFF2, /* ONOFF2 */
+	[9]     = EZX_IRQ_USB1V,
+	[10]    = EZX_IRQ_MOBPORT, /* MOBPORT */
+	[11]    = EZX_IRQ_MIC,
+	[12]    = EZX_IRQ_HEADJACK,
+	[13]    = EZX_IRQ_ST, /* ST */
+	[14]    = EZX_IRQ_PC, /* PC */
+	[15]    = EZX_IRQ_WARM, /* WARM */
+	[16]    = EZX_IRQ_EOL, /* EOL */
+	[17]    = EZX_IRQ_CLK, /* CLK */
+	[18]    = EZX_IRQ_SYSRST, /* SYSRST */
+	[19]    = 0,
+	[20]    = EZX_IRQ_ADCDONE2,
+	[21]    = EZX_IRQ_SOFTRESET, /* SOFTRESET */
+	[22]    = EZX_IRQ_MNEXB, /* MNEXB */
+};
+
+/* Array indexed by IRQ NUMBER, returns PCAP absolute value */
+static unsigned int irq2pcap[] = {
+	[EZX_IRQ_MNEXB]		= PCAP_IRQ_MNEXB,
+	[EZX_IRQ_SOFTRESET]	= PCAP_IRQ_SOFTRESET,
+	[EZX_IRQ_SYSRST]	= PCAP_IRQ_SYSRST,
+	[EZX_IRQ_CLK]		= PCAP_IRQ_CLK,
+	[EZX_IRQ_EOL]		= PCAP_IRQ_EOL,
+	[EZX_IRQ_WARM]		= PCAP_IRQ_WARM,
+	[EZX_IRQ_PC]		= PCAP_IRQ_PC,
+	[EZX_IRQ_ST]		= PCAP_IRQ_ST,
+	[EZX_IRQ_MOBPORT]	= PCAP_IRQ_MOBPORT,
+	[EZX_IRQ_ONOFF2]	= PCAP_IRQ_ONOFF2,
+	[EZX_IRQ_ONOFF]		= PCAP_IRQ_ONOFF,
+	[EZX_IRQ_TODA]		= PCAP_IRQ_TODA,
+	[EZX_IRQ_WL]		= PCAP_IRQ_WL,
+	[EZX_IRQ_WH]		= PCAP_IRQ_WH,
+	[EZX_IRQ_1HZ]		= PCAP_IRQ_1HZ,
+	[EZX_IRQ_USB4V]		= PCAP_IRQ_USB4V,
+	[EZX_IRQ_USB1V]		= PCAP_IRQ_USB1V,
+	[EZX_IRQ_HEADJACK]	= PCAP_IRQ_A1,
+	[EZX_IRQ_MIC]		= PCAP_IRQ_MB2,
+	[EZX_IRQ_TS]		= PCAP_IRQ_TS,
+	[EZX_IRQ_ADCDONE]	= PCAP_IRQ_ADCDONE,
+	[EZX_IRQ_ADCDONE2]	= PCAP_IRQ_ADCDONE2,
+};
+
+static void pcap_ack_irq(unsigned int irq)
+{
+	DEBUGP("pcap_ack_irq: %u\n", irq);
+	ezx_pcap_write(PCAP_REG_ISR, irq2pcap[irq]);
+}
+
+static void pcap_mask_irq(unsigned int irq)
+{
+	u_int32_t reg;
+	unsigned long flag;
+
+	spin_lock_irqsave(&ezx_ssp_lock, flag);
+	DEBUGP("pcap_mask_irq: %u\n", irq);
+	ezx_pcap_read(PCAP_REG_MSR, &reg);
+	reg |= irq2pcap[irq];
+	ezx_pcap_write(PCAP_REG_MSR, reg);
+	spin_unlock_irqrestore(&ezx_ssp_lock, flag);
+}
+
+static void pcap_unmask_irq(unsigned int irq)
+{
+	u_int32_t tmp;
+	unsigned long flag;
+
+	spin_lock_irqsave(&ezx_ssp_lock, flag);
+	DEBUGP("pcap_unmask_irq: %u\n", irq);
+	ezx_pcap_read(PCAP_REG_MSR, &tmp);
+	tmp &= ~irq2pcap[irq];
+	ezx_pcap_write(PCAP_REG_MSR, tmp);
+	spin_unlock_irqrestore(&ezx_ssp_lock, flag);
+}
+
+static struct irq_chip pcap_chip = {
+	.name	= "ezx-pcap",
+	.ack    = pcap_ack_irq,
+	.mask   = pcap_mask_irq,
+	.unmask = pcap_unmask_irq,
+};
+
+/* handler for interrupt received from PCAP via GPIO */
+static void pcap_irq_demux_handler(unsigned int irq, struct irq_desc *desc)
+{
+	int i;
+	u_int32_t isr;
+
+	desc->chip->ack(irq);
+	ezx_pcap_read(PCAP_REG_ISR, &isr);
+	for (i = ARRAY_SIZE(pcap2irq)-1; i >= 0; i--) {
+		unsigned int pirq = pcap2irq[i];
+		if (!(isr & irq2pcap[pirq]))
+			continue;
+		desc = &irq_desc[pirq];
+		DEBUGP("found irq %u\n", pirq);
+		desc_handle_irq(pirq, desc);
+	}
+}
+
+static int ezx_pcap_remove(struct platform_device *pdev)
+{
+	int irq;
+	DEBUGP("exz_pcap_remove entered\n");
+
+	set_irq_chained_handler(pcap_irq, NULL);
+
+	for (irq = EZX_IRQ(0); irq <= EZX_IRQ(21); irq++) {
+		set_irq_chip(irq, NULL);
+		set_irq_handler(irq, NULL);
+		set_irq_flags(irq, 0);
+	}
+
+	ssp_exit(&ezx_ssp_dev);
+
+	return 0;
+}
+
+static int __init ezx_pcap_probe(struct platform_device *pdev)
+{
+	unsigned int ret, irq;
+	DEBUGP("ezx_pcap_probe entered\n");
+
+	pcap_data = pdev->dev.platform_data;
+
+	if (pcap_data->cs >= 0) {
+		if (machine_is_ezx_a780() || machine_is_ezx_e680())
+			gpio_direction_output(pcap_data->cs, 1);
+		else
+			gpio_direction_output(pcap_data->cs, 0);
+	}
+	pcap_irq = platform_get_irq(pdev, 0);
+	if (pcap_irq < 0) {
+		printk(KERN_ERR "Unable to get IRQ for pcap!\n");
+		return pcap_irq;
+	}
+
+	ret = ssp_init(&ezx_ssp_dev, pcap_data->port, 0);
+	if (ret) {
+		printk(KERN_ERR "Unable to register SSP handler!\n");
+		return ret;
+	}
+
+	ssp_disable(&ezx_ssp_dev);
+	ssp_config(&ezx_ssp_dev,
+		(SSCR0_Motorola | SSCR0_DataSize(16) | SSCR0_EDSS),
+		(SSCR1_TxTresh(1) | SSCR1_RxTresh(1)),
+		0, SSCR0_SerClkDiv(pcap_data->clk));
+	ssp_enable(&ezx_ssp_dev);
+
+	if (pcap_data->init)
+		pcap_data->init();
+
+	/* set up interrupt demultiplexing code for PCAP2 irqs */
+	for (irq = EZX_IRQ(0); irq <= EZX_IRQ(21); irq++) {
+		set_irq_chip(irq, &pcap_chip);
+		set_irq_handler(irq, handle_level_irq);
+		set_irq_flags(irq, IRQF_VALID);
+	}
+	set_irq_type(pcap_irq, IRQ_TYPE_EDGE_RISING);
+	set_irq_chained_handler(pcap_irq, pcap_irq_demux_handler);
+	set_irq_wake(pcap_irq, 1);
+
+	/* mask/ack all PCAP interrupts */
+	ezx_pcap_write(PCAP_REG_MSR, PCAP_MASK_ALL_INTERRUPT);
+	ezx_pcap_write(PCAP_REG_ISR, PCAP_CLEAR_INTERRUPT_REGISTER);
+
+
+	printk(KERN_INFO "ezx-pcap: ssp driver registered\n");
+	return ret;
+}
+
+#ifdef CONFIG_PM
+static int ezx_pcap_suspend(struct platform_device *dev, pm_message_t state)
+{
+	DEBUGP("pcap suspend!\n");
+	ssp_flush(&ezx_ssp_dev);
+	ssp_save_state(&ezx_ssp_dev, &ezx_ssp_state);
+	ssp_disable(&ezx_ssp_dev);
+	return 0;
+}
+
+static int ezx_pcap_resume(struct platform_device *dev)
+{
+	DEBUGP("pcap resume!\n");
+	ssp_restore_state(&ezx_ssp_dev, &ezx_ssp_state);
+	ssp_enable(&ezx_ssp_dev);
+
+	return 0;
+}
+#endif
+
+static struct platform_driver ezxpcap_driver = {
+	.probe		= ezx_pcap_probe,
+	.remove		= ezx_pcap_remove,
+#ifdef CONFIG_PM
+	.suspend	= ezx_pcap_suspend,
+	.resume		= ezx_pcap_resume,
+#endif
+	.driver		= {
+		.name   = "ezx-pcap",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init ezx_pcap_init(void)
+{
+	DEBUGP("ezx_pcap_init entered\n");
+	return platform_driver_register(&ezxpcap_driver);
+}
+
+static void __exit ezx_pcap_exit(void)
+{
+	return platform_driver_unregister(&ezxpcap_driver);
+}
+
+module_init(ezx_pcap_init);
+module_exit(ezx_pcap_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Harald Welte");
+MODULE_DESCRIPTION("Motorola PCAP2 ASIC Driver");
+
Index: linux-2.6-arm/include/linux/mfd/ezx-pcap.h
===================================================================
--- /dev/null
+++ linux-2.6-arm/include/linux/mfd/ezx-pcap.h
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2007 Daniel Ribeiro <wyrm@openezx.org>
+ *
+ * For further information, please see http://wiki.openezx.org/PCAP2
+ */
+
+#ifndef EZX_PCAP_H
+#define EZX_PCAP_H
+
+struct pcap_platform_data {
+	int port;		/* SSP port */
+	int cs;			/* CS gpio */
+	int clk;
+	int (*init)(void);	/* board specific driver init */
+};
+
+#define PCAP_REGISTER_WRITE_OP_BIT	0x80000000
+#define PCAP_REGISTER_READ_OP_BIT	0x00000000
+
+#define PCAP_REGISTER_VALUE_MASK	0x01ffffff
+#define PCAP_REGISTER_ADDRESS_MASK	0x7c000000
+#define PCAP_REGISTER_ADDRESS_SHIFT	26
+#define PCAP_REGISTER_NUMBER		32
+#define PCAP_CLEAR_INTERRUPT_REGISTER	0x01ffffff
+#define PCAP_MASK_ALL_INTERRUPT		0x01ffffff
+
+
+#define pbit(reg, bit)	((reg << PCAP_REGISTER_ADDRESS_SHIFT) | bit)
+
+/* registers acessible by both pcap ports */
+#define PCAP_REG_ISR		0x0	/* Interrupt Status */
+#define PCAP_REG_MSR		0x1	/* Interrupt Mask */
+#define PCAP_REG_PSTAT		0x2	/* Processor Status */
+#define PCAP_REG_VREG2		0x6	/* Regulator Bank 2 Control */
+#define PCAP_REG_AUXVREG	0x7	/* Auxiliary Regulator Control */
+#define PCAP_REG_BATT		0x8	/* Battery Control */
+#define PCAP_REG_ADC		0x9	/* AD Control */
+#define PCAP_REG_ADR		0xa	/* AD Result */
+#define PCAP_REG_CODEC		0xb	/* Audio Codec Control */
+#define PCAP_REG_RX_AMPS	0xc	/* RX Audio Amplifiers Control */
+#define PCAP_REG_ST_DAC		0xd	/* Stereo DAC Control */
+#define PCAP_REG_BUSCTRL	0x14	/* Connectivity Control */
+#define PCAP_REG_PERIPH		0x15	/* Peripheral Control */
+#define PCAP_REG_LOWPWR		0x18	/* Regulator Low Power Control */
+#define PCAP_REG_TX_AMPS	0x1a	/* TX Audio Amplifiers Control */
+#define PCAP_REG_GP		0x1b	/* General Purpose */
+
+/* registers acessible by pcap port 1 only (a1200, e2 & e6) */
+#define PCAP_REG_INT_SEL	0x3	/* Interrupt Select */
+#define PCAP_REG_SWCTRL		0x4	/* Switching Regulator Control */
+#define PCAP_REG_VREG1		0x5	/* Regulator Bank 1 Control */
+#define PCAP_REG_RTC_TOD	0xe	/* RTC Time of Day */
+#define PCAP_REG_RTC_TODA	0xf	/* RTC Time of Day Alarm */
+#define PCAP_REG_RTC_DAY	0x10	/* RTC Day */
+#define PCAP_REG_RTC_DAYA	0x11	/* RTC Day Alarm */
+#define PCAP_REG_MTRTMR		0x12	/* AD Monitor Timer */
+#define PCAP_REG_PWR		0x13	/* Power Control */
+#define PCAP_REG_AUXVREG_MASK	0x16	/* Auxiliary Regulator Mask */
+#define PCAP_REG_VENDOR_REV	0x17
+#define PCAP_REG_PERIPH_MASK	0x19	/* Peripheral Mask */
+
+/* interrupts - registers 0x0, 0x1, 0x2, 0x3 */
+#define PCAP_IRQ_ADCDONE	(1 << 0)	/* AD Conversion Done Port 1 */
+#define PCAP_IRQ_TS		(1 << 1)	/* Touch Screen */
+#define PCAP_IRQ_1HZ		(1 << 2)	/* 1HZ Timer */
+#define PCAP_IRQ_WH		(1 << 3)	/* "...high"??? */
+#define PCAP_IRQ_WL		(1 << 4)	/* "...low"??? */
+#define PCAP_IRQ_TODA		(1 << 5)	/* RTC Time Of Day?
+						     (see "RTC_TODA") */
+#define PCAP_IRQ_USB4V		(1 << 6)	/* USB above 4volt???
+						called "USBDET_4V" in blob */
+#define PCAP_IRQ_ONOFF		(1 << 7)	/* in blob: "ONOFFSNS" */
+#define PCAP_IRQ_ONOFF2		(1 << 8)	/* in blob: "ONOFFSNS2" */
+#define PCAP_IRQ_USB1V		(1 << 9)	/* USB below 1volt???
+						     in blob: "USBDET_1V" */
+#define PCAP_IRQ_MOBPORT	(1 << 10)	/* GSM-related?? ("mobport",
+				see 958_MotDoc.pdf); in blob: "MOBSENSB" */
+#define PCAP_IRQ_MB2		(1 << 11)	/* Mic; in blob: "MB2SNS" */
+#define PCAP_IRQ_A1		(1 << 12)	/* Audio jack;
+						     in blob: "A1SNS" */
+#define PCAP_IRQ_ST		(1 << 13)	/* called "MSTB" in blob */
+#define PCAP_IRQ_PC		(1 << 14)
+#define PCAP_IRQ_WARM		(1 << 15)
+#define PCAP_IRQ_EOL		(1 << 16)	/* battery End Of Life???
+					(see below); in blob: "EOL_STAT" */
+#define PCAP_IRQ_CLK		(1 << 17)	/* called "CLK_STAT" in blob */
+#define PCAP_IRQ_SYSRST		(1 << 18)
+#define PCAP_IRQ_ADCDONE2	(1 << 20)	/* AD Conversion Done Port 2 */
+#define PCAP_IRQ_SOFTRESET	(1 << 21)
+#define PCAP_IRQ_MNEXB		(1 << 22)
+
+/* register VREG2 (0x6) */
+#define PCAP_VREG2_V1_STBY	(1 << 0)
+#define PCAP_VREG2_V2_STBY	(1 << 1)
+/* V3, SRAM: */
+#define PCAP_VREG2_V3_STBY	(1 << 2)
+#define PCAP_VREG2_V4_STBY	(1 << 3)
+#define PCAP_VREG2_V5_STBY	(1 << 4)
+/* V6, E680 I2C camera?: */
+#define PCAP_VREG2_V6_STBY	(1 << 5)
+#define PCAP_VREG2_V7_STBY	(1 << 6)
+/* V8, PLL: */
+#define PCAP_VREG2_V8_STBY	(1 << 7)
+#define PCAP_VREG2_V9_STBY	(1 << 8)
+#define PCAP_VREG2_V10_STBY	(1 << 9)
+#define PCAP_VREG2_V1_LOWPWR	(1 << 10)
+#define PCAP_VREG2_V2_LOWPWR	(1 << 11)
+/* V3, SRAM: */
+#define PCAP_VREG2_V3_LOWPWR	(1 << 12)
+#define PCAP_VREG2_V4_LOWPWR	(1 << 13)
+#define PCAP_VREG2_V5_LOWPWR	(1 << 14)
+/* V6, E680 I2C camera?: */
+#define PCAP_VREG2_V6_LOWPWR	(1 << 15)
+#define PCAP_VREG2_V7_LOWPWR	(1 << 16)
+/* V8, PLL: */
+#define PCAP_VREG2_V8_LOWPWR	(1 << 17)
+#define PCAP_VREG2_V9_LOWPWR	(1 << 18)
+#define PCAP_VREG2_V10_LOWPWR	(1 << 19)
+
+/* register AUXVREG (0x7) */
+#define VAUX1		0
+#define VAUX2		1
+#define VAUX3		2
+#define VAUX4		3
+#define VSIM		4
+#define VSIM2		5
+#define VVIB		6
+#define VC		7
+
+#define VAUX_EN		0
+#define VAUX_VAL	1
+#define VAUX_MASK	2
+#define VAUX_STBY	3
+#define VAUX_LOWPWR	4
+
+#define PCAP_BATT_DAC_MASK		0x000000ff
+#define PCAP_BATT_DAC_SHIFT		0
+#define PCAP_BATT_B_FDBK		(1 << 8)
+#define PCAP_BATT_EXT_ISENSE		(1 << 9)
+#define PCAP_BATT_V_COIN_MASK		0x00003c00
+#define PCAP_BATT_V_COIN_SHIFT		10
+#define PCAP_BATT_I_COIN		(1 << 14)
+#define PCAP_BATT_COIN_CH_EN		(1 << 15)
+#define PCAP_BATT_EOL_SEL_MASK		0x000e0000
+#define PCAP_BATT_EOL_SEL_SHIFT		17
+#define PCAP_BATT_EOL_CMP_EN		(1 << 20)
+#define PCAP_BATT_BATT_DET_EN		(1 << 21)
+#define PCAP_BATT_THERMBIAS_CTRL	(1 << 22)
+
+#define PCAP_ADC_ADEN			(1 << 0)
+#define PCAP_ADC_RAND			(1 << 1)
+#define PCAP_ADC_AD_SEL1		(1 << 2)
+#define PCAP_ADC_AD_SEL2		(1 << 3)
+#define PCAP_ADC_ADA1_MASK		0x00000070
+#define PCAP_ADC_ADA1_SHIFT		4
+#define PCAP_ADC_ADA2_MASK		0x00000380
+#define PCAP_ADC_ADA2_SHIFT		7
+#define PCAP_ADC_ATO_MASK		0x00003c00
+#define PCAP_ADC_ATO_SHIFT		10
+#define PCAP_ADC_ATOX			(1 << 14)
+#define PCAP_ADC_MTR1			(1 << 15)
+#define PCAP_ADC_MTR2			(1 << 16)
+#define PCAP_ADC_TS_M_MASK		0x000e0000
+#define PCAP_ADC_TS_M_SHIFT		17
+#define PCAP_ADC_TS_REF_LOWPWR		(1 << 20)
+#define PCAP_ADC_TS_REFENB		(1 << 21)
+#define PCAP_ADC_BATT_I_POLARITY	(1 << 22)
+#define PCAP_ADC_BATT_I_ADC		(1 << 23)
+
+#define PCAP_ADR_ADD1_MASK		0x000003ff
+#define PCAP_ADR_ADD1_SHIFT		0
+#define PCAP_ADR_ADD2_MASK		0x000ffc00
+#define PCAP_ADR_ADD2_SHIFT		10
+#define PCAP_ADR_ADINC1			(1 << 20)
+#define PCAP_ADR_ADINC2			(1 << 21)
+#define PCAP_ADR_ASC			(1 << 22)
+#define PCAP_ADR_ONESHOT		(1 << 23)
+
+#define PCAP_BUSCTRL_FSENB		(1 << 0)
+#define PCAP_BUSCTRL_USB_SUSPEND	(1 << 1)
+#define PCAP_BUSCTRL_USB_PU		(1 << 2)
+#define PCAP_BUSCTRL_USB_PD		(1 << 3)
+#define PCAP_BUSCTRL_VUSB_EN		(1 << 4)
+#define PCAP_BUSCTRL_USB_PS		(1 << 5)
+#define PCAP_BUSCTRL_VUSB_MSTR_EN	(1 << 6)
+#define PCAP_BUSCTRL_VBUS_PD_ENB	(1 << 7)
+#define PCAP_BUSCTRL_CURRLIM		(1 << 8)
+#define PCAP_BUSCTRL_RS232ENB		(1 << 9)
+#define PCAP_BUSCTRL_RS232_DIR		(1 << 10)
+#define PCAP_BUSCTRL_SE0_CONN		(1 << 11)
+#define PCAP_BUSCTRL_USB_PDM		(1 << 12)
+#define PCAP_BUSCTRL_BUS_PRI_ADJ	(1 << 24)
+
+#define PCAP_BIT_PERIPH_BL_CTRL0	0x54000001
+#define PCAP_BIT_PERIPH_BL_CTRL1	0x54000002
+#define PCAP_BIT_PERIPH_BL_CTRL2	0x54000004
+#define PCAP_BIT_PERIPH_BL_CTRL3	0x54000008
+#define PCAP_BIT_PERIPH_BL_CTRL4	0x54000010
+#define PCAP_BIT_PERIPH_LEDR_EN		0x54000020
+#define PCAP_BIT_PERIPH_LEDG_EN		0x54000040
+#define PCAP_BIT_PERIPH_LEDR_CTRL0	0x54000080
+#define PCAP_BIT_PERIPH_LEDR_CTRL1	0x54000100
+#define PCAP_BIT_PERIPH_LEDR_CTRL2	0x54000200
+#define PCAP_BIT_PERIPH_LEDR_CTRL3	0x54000400
+#define PCAP_BIT_PERIPH_LEDG_CTRL0	0x54000800
+#define PCAP_BIT_PERIPH_LEDG_CTRL1	0x54001000
+#define PCAP_BIT_PERIPH_LEDG_CTRL2	0x54002000
+#define PCAP_BIT_PERIPH_LEDG_CTRL3	0x54004000
+#define PCAP_BIT_PERIPH_LEDR_I0		0x54008000
+#define PCAP_BIT_PERIPH_LEDR_I1		0x54010000
+#define PCAP_BIT_PERIPH_LEDG_I0		0x54020000
+#define PCAP_BIT_PERIPH_LEDG_I1		0x54040000
+#define PCAP_BIT_PERIPH_SKIP		0x54080000
+#define PCAP_BIT_PERIPH_BL2_CTRL0	0x54100000
+#define PCAP_BIT_PERIPH_BL2_CTRL1	0x54200000
+#define PCAP_BIT_PERIPH_BL2_CTRL2	0x54400000
+#define PCAP_BIT_PERIPH_BL2_CTRL3	0x54800000
+#define PCAP_BIT_PERIPH_BL2_CTRL4	0x55000000
+
+/* LOWPWR */
+#define SW1		8
+#define SW2		16
+
+#define SW_MODE		0
+#define SW_VOLTAGE	4
+
+#define SW_VOLTAGE_900	0x0
+#define SW_VOLTAGE_950	0x1
+#define SW_VOLTAGE_1000	0x2
+#define SW_VOLTAGE_1050	0x3
+#define SW_VOLTAGE_1100	0x4
+#define SW_VOLTAGE_1150	0x5
+#define SW_VOLTAGE_1200	0x6
+#define SW_VOLTAGE_1250	0x7
+#define SW_VOLTAGE_1300	0x8
+#define SW_VOLTAGE_1350	0x9
+#define SW_VOLTAGE_1400	0xa
+#define SW_VOLTAGE_1500	0xb
+#define SW_VOLTAGE_1600	0xc
+#define SW_VOLTAGE_1875	0xd
+#define SW_VOLTAGE_2250	0xe
+#define SW_VOLTAGE_4400	0xf
+
+void ezx_pcap_write(u_int8_t, u_int32_t);
+void ezx_pcap_read(u_int8_t, u_int32_t *);
+void ezx_pcap_set_sw(u_int8_t, u_int8_t, u_int8_t);
+int ezx_pcap_set_vaux(u_int8_t, u_int8_t, u_int8_t);
+#endif
Index: linux-2.6-arm/include/asm-arm/arch-pxa/irqs.h
===================================================================
--- linux-2.6-arm.orig/include/asm-arm/arch-pxa/irqs.h
+++ linux-2.6-arm/include/asm-arm/arch-pxa/irqs.h
@@ -89,7 +89,7 @@
  * within sensible limits.
  */
 #define IRQ_BOARD_START		(PXA_GPIO_IRQ_BASE + PXA_GPIO_IRQ_NUM)
-#define IRQ_BOARD_END		(IRQ_BOARD_START + 16)
+#define IRQ_BOARD_END		(IRQ_BOARD_START + 22)
 
 #define IRQ_SA1111_START	(IRQ_BOARD_END)
 #define IRQ_GPAIN0		(IRQ_BOARD_END + 0)
@@ -183,7 +183,8 @@
       defined(CONFIG_MACH_TOSA) || \
       defined(CONFIG_MACH_MAINSTONE) || \
       defined(CONFIG_MACH_PCM027) || \
-      defined(CONFIG_MACH_MAGICIAN)
+      defined(CONFIG_MACH_MAGICIAN) || \
+      defined(CONFIG_PXA_EZX)
 #define NR_IRQS			(IRQ_BOARD_END)
 #else
 #define NR_IRQS			(IRQ_BOARD_START)
@@ -237,6 +238,31 @@
 #define PCM027_MMCDET_IRQ      PCM027_IRQ(2)
 #define PCM027_PM_5V_IRQ       PCM027_IRQ(3)
 
+/* EZX Interrupts (CONFIG_EZX) */
+#define EZX_IRQ(x)         (IRQ_BOARD_START + (x))
+#define EZX_IRQ_USB4V      EZX_IRQ(0) /* EMU */
+#define EZX_IRQ_USB1V      EZX_IRQ(1) /* EMU */
+#define EZX_IRQ_HEADJACK   EZX_IRQ(2) /* Audio connector */
+#define EZX_IRQ_MIC        EZX_IRQ(3) /* Audio connector */
+#define EZX_IRQ_ADCDONE    EZX_IRQ(4)
+#define EZX_IRQ_TS         EZX_IRQ(5) /* TS touch */
+#define EZX_IRQ_ADCDONE2   EZX_IRQ(6) /* TS x/y ADC ready */
+#define EZX_IRQ_WH         EZX_IRQ(7)
+#define EZX_IRQ_WL         EZX_IRQ(8)
+#define EZX_IRQ_ONOFF      EZX_IRQ(9)
+#define EZX_IRQ_ONOFF2     EZX_IRQ(10)
+#define EZX_IRQ_MOBPORT    EZX_IRQ(11)
+#define EZX_IRQ_TODA       EZX_IRQ(12)
+#define EZX_IRQ_1HZ        EZX_IRQ(13)
+#define EZX_IRQ_MNEXB      EZX_IRQ(14)
+#define EZX_IRQ_ST         EZX_IRQ(15)
+#define EZX_IRQ_PC         EZX_IRQ(16)
+#define EZX_IRQ_SYSRST     EZX_IRQ(17)
+#define EZX_IRQ_SOFTRESET  EZX_IRQ(18)
+#define EZX_IRQ_EOL        EZX_IRQ(19)
+#define EZX_IRQ_CLK        EZX_IRQ(20)
+#define EZX_IRQ_WARM       EZX_IRQ(21)
+
 /* ITE8152 irqs */
 /* add IT8152 IRQs beyond BOARD_END */
 #ifdef CONFIG_PCI_HOST_ITE8152
Index: linux-2.6-arm/drivers/mfd/Kconfig
===================================================================
--- linux-2.6-arm.orig/drivers/mfd/Kconfig
+++ linux-2.6-arm/drivers/mfd/Kconfig
@@ -49,6 +49,13 @@
 	help
 	  Support for Toshiba Mobile IO Controller TC6393XB
 
+config EZX_PCAP
+	bool "PCAP Support"
+	depends on PXA_SSP && PXA_EZX
+	help
+	  This enables the PCAP ASIC present on EZX Phones. This is
+	  needed for MMC, TouchScreen, Sound, USB, etc..
+
 endmenu
 
 menu "Multimedia Capabilities Port drivers"
Index: linux-2.6-arm/drivers/mfd/Makefile
===================================================================
--- linux-2.6-arm.orig/drivers/mfd/Makefile
+++ linux-2.6-arm/drivers/mfd/Makefile
@@ -12,6 +12,8 @@
 
 obj-$(CONFIG_MFD_CORE)		+= mfd-core.o
 
+obj-$(CONFIG_EZX_PCAP)		+= ezx-pcap.o
+
 obj-$(CONFIG_MCP)		+= mcp-core.o
 obj-$(CONFIG_MCP_SA11X0)	+= mcp-sa11x0.o
 obj-$(CONFIG_MCP_UCB1200)	+= ucb1x00-core.o
Index: linux-2.6-arm/arch/arm/mach-pxa/Kconfig
===================================================================
--- linux-2.6-arm.orig/arch/arm/mach-pxa/Kconfig
+++ linux-2.6-arm/arch/arm/mach-pxa/Kconfig
@@ -224,6 +224,8 @@
 	select PXA27x
 	select IWMMXT
 	select HAVE_PWM
+	select PXA_SSP
+	select EZX_PCAP
 
 config MACH_EZX_A780
 	bool "Motorola EZX A780"
Index: linux-2.6-arm/arch/arm/mach-pxa/ezx.c
===================================================================
--- linux-2.6-arm.orig/arch/arm/mach-pxa/ezx.c
+++ linux-2.6-arm/arch/arm/mach-pxa/ezx.c
@@ -15,7 +15,9 @@
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/delay.h>
+#include <linux/gpio.h>
 #include <linux/pwm_backlight.h>
+#include <linux/mfd/ezx-pcap.h>
 
 #include <asm/setup.h>
 #include <asm/arch/pxafb.h>
@@ -87,6 +89,51 @@
 	.lcd_conn	= LCD_COLOR_TFT_18BPP,
 };
 
+/* PCAP */
+static int ezx_pcap_init(void)
+{
+	/* disable all voltage regulators */
+	ezx_pcap_write(PCAP_REG_AUXVREG, 0);
+
+	/* set SW1 sleep to keep SW1 1.3v in sync mode */
+	/*  SW1 active in sync mode */
+	ezx_pcap_set_sw(SW1, SW_MODE, 0x1);
+
+	/*  set core voltage */
+	ezx_pcap_set_sw(SW1, SW_VOLTAGE, SW_VOLTAGE_1250);
+
+	/* redirect all interrupts to AP */
+	if (!(machine_is_ezx_a780() || machine_is_ezx_e680()))
+		ezx_pcap_write(PCAP_REG_INT_SEL, 0);
+
+	return 0;
+}
+
+static struct pcap_platform_data ezx_pcap_platform_data = {
+	.port   = 1,
+	.cs     = 24,
+	.clk    = 1,
+	.init   = ezx_pcap_init,
+};
+
+static struct resource ezx_pcap_resources[] = {
+	[0] = {
+		.start      = IRQ_GPIO1,
+		.end        = IRQ_GPIO1,
+		.flags      = IORESOURCE_IRQ,
+	},
+};
+
+struct platform_device ezx_pcap_device = {
+	.name       = "ezx-pcap",
+	.id     = -1,
+	.num_resources  = ARRAY_SIZE(ezx_pcap_resources),
+	.resource   = ezx_pcap_resources,
+	.dev        = {
+		.platform_data = &ezx_pcap_platform_data,
+	},
+};
+
 static struct platform_device *devices[] __initdata = {
 	&ezx_backlight_device,
 };
@@ -105,6 +152,11 @@
 	GPIO46_STUART_RXD,
 	GPIO47_STUART_TXD,
 
+	/* PCAP SSP */
+	GPIO29_SSP1_SCLK,
+	GPIO25_SSP1_TXD,
+	GPIO26_SSP1_RXD,
+
 	/* For A780 support (connected with Neptune GSM chip) */
 	GPIO30_USB_P3_2,	/* ICL_TXENB */
 	GPIO31_USB_P3_6,	/* ICL_VPOUT */
@@ -122,7 +174,7 @@
 		set_pxa_fb_info(&ezx_fb_info_1);
 	else
 		set_pxa_fb_info(&ezx_fb_info_2);
-
+	platform_device_register(&ezx_pcap_device);
 	platform_add_devices(devices, ARRAY_SIZE(devices));
 }
 

-- 

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Patch 06/10] mfd: PCAP driver for the Motorola EZX GSM mobile phones
  2008-07-07 18:40 ` [Patch 06/10] mfd: PCAP driver for the Motorola EZX GSM mobile phones stefan
@ 2008-07-07 19:21   ` pHilipp Zabel
  2008-07-07 20:02     ` Stefan Schmidt
  0 siblings, 1 reply; 12+ messages in thread
From: pHilipp Zabel @ 2008-07-07 19:21 UTC (permalink / raw)
  To: stefan; +Cc: linux-arm-kernel, sameo, linux-kernel, Daniel Ribeiro

On Mon, Jul 7, 2008 at 8:40 PM,  <stefan@datenfreihafen.org> wrote:
> The PCAP Asic as present on EZX phones is a multi function device with voltage
> regulators, irq expander, touch screen controller and audio codec.
> It is connected to the processor via SPI, this driver provides read/write
> functions to its registers and a irq demultiplexer.

Are the EZX phones expected to be the only linux devices with PCAP?
Otherwise I'd dislike the hardcoded IRQ numbers in the MFD driver.

The machine_is_xyz() calls inside ezx-pcap could be replaced with
configuration via platform_data (have pcap_platform_data->cs_inverted,
for example)

> Signed-off-by: Daniel Ribeiro <wyrm@openezx.org>
>
> PATCH FOLLOWS
> KernelVersion: 2.6-arm-git pxa branch
>
> Index: linux-2.6-arm/drivers/mfd/ezx-pcap.c
> ===================================================================
> --- /dev/null
> +++ linux-2.6-arm/drivers/mfd/ezx-pcap.c
> @@ -0,0 +1,402 @@
> +/* Driver for Motorola PCAP2 as present in EZX phones
> + *
> + * This is both a SPI device driver for PCAP itself, as well as
> + * an IRQ demultiplexer for handling PCAP generated events such as
> + * headphone jack sense by downstream drivers.
> + *
> + * Copyright (C) 2006 Harald Welte <laforge@openezx.org>
> + * Copyright (C) 2007-2008 Daniel Ribeiro <wyrm@openezx.org>
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel_stat.h>
> +#include <linux/proc_fs.h>
> +#include <linux/mfd/ezx-pcap.h>
> +#include <linux/delay.h>
> +#include <linux/gpio.h>
> +
> +#include <asm/hardware.h>
> +#include <asm/mach-types.h>
> +
> +#include <asm/arch/ssp.h>
> +#include <asm/arch/pxa-regs.h>
> +#include <asm/arch/regs-ssp.h>
> +#include <asm/arch/mfp-pxa27x.h>
> +#include <asm/arch/irqs.h>
> +#include <asm/arch/mmc.h>
> +#include <asm/mach/irq.h>
> +
> +#if 0
> +#define DEBUGP(x, args...) printk(x, ## args)
> +#else
> +#define DEBUGP(x, args...)
> +#endif

Could you remove the custom debug macros and use pr_debug (or even
better, dev_dbg) instead?

> +static DEFINE_SPINLOCK(ezx_ssp_lock);
> +static struct ssp_dev ezx_ssp_dev;
> +static struct ssp_state ezx_ssp_state;
> +static struct pcap_platform_data *pcap_data;
> +static int pcap_irq;
> +
> +static u_int32_t ezx_ssp_pcap_putget(u_int32_t data)
> +{
> +       unsigned long flag;
> +       u_int32_t ret = 0;
> +
> +       spin_lock_irqsave(&ezx_ssp_lock, flag);
> +       if (pcap_data->cs >= 0) {
> +               if (machine_is_ezx_a780() || machine_is_ezx_e680())
> +                       gpio_set_value(pcap_data->cs, 0);
> +               else
> +                       gpio_set_value(pcap_data->cs, 1);
> +       }
> +
> +       ssp_write_word(&ezx_ssp_dev, data);
> +       ssp_read_word(&ezx_ssp_dev, &ret);
> +
> +       if (pcap_data->cs >= 0) {
> +               if (machine_is_ezx_a780() || machine_is_ezx_e680())
> +                       gpio_set_value(pcap_data->cs, 1);
> +               else
> +                       gpio_set_value(pcap_data->cs, 0);
> +       }
> +
> +       spin_unlock_irqrestore(&ezx_ssp_lock, flag);
> +
> +       return ret;
> +}
> +
> +void ezx_pcap_write(u_int8_t reg_num, u_int32_t value)
> +{
> +       value &= PCAP_REGISTER_VALUE_MASK;
> +       value |= PCAP_REGISTER_WRITE_OP_BIT
> +               | (reg_num<<PCAP_REGISTER_ADDRESS_SHIFT);
> +
> +       ezx_ssp_pcap_putget(value);
> +
> +       DEBUGP("pcap write r%x: 0x%08x\n", reg_num, value);
> +}
> +EXPORT_SYMBOL_GPL(ezx_pcap_write);
> +
> +void ezx_pcap_read(u_int8_t reg_num, u_int32_t *value)
> +{
> +       u_int32_t frame = PCAP_REGISTER_READ_OP_BIT
> +               | (reg_num<<PCAP_REGISTER_ADDRESS_SHIFT);
> +
> +       *value = ezx_ssp_pcap_putget(frame);
> +
> +       DEBUGP("pcap read r%x:  0x%08x\n", reg_num, *value);
> +}
> +EXPORT_SYMBOL_GPL(ezx_pcap_read);
> +
> +void ezx_pcap_set_sw(u_int8_t sw, u_int8_t what, u_int8_t val)
> +{
> +       u_int32_t tmp;
> +
> +       ezx_pcap_read(PCAP_REG_LOWPWR, &tmp);
> +       tmp &= ~(0xf << (sw + what));
> +       tmp |= ((val & 0xf) << (sw + what));
> +       ezx_pcap_write(PCAP_REG_LOWPWR, tmp);
> +}
> +EXPORT_SYMBOL_GPL(ezx_pcap_set_sw);
> +
> +static u_int8_t vaux_table[][8] = {
> +       /*              EN      INDEX   MASK    STBY    LOWPWR  */
> +       [VAUX1] = {     1,      2,      0x3,    22,     23,     },
> +       [VAUX2] = {     4,      5,      0x3,    0,      1,      },
> +       [VAUX3] = {     7,      8,      0xf,    2,      3,      },
> +       [VAUX4] = {     12,     13,     0x3,    4,      5,      },
> +       [VSIM]  = {     17,     18,     0x1,    0xff,   6,      },
> +       [VSIM2] = {     16,     0xff,   0x0,    0xff,   7,      },
> +       [VVIB]  = {     19,     20,     0x3,    0xff,   0xff,   },
> +       [VC]    = {     0xff,   0xff,   0x0,    24,     0xff,   },
> +};
> +
> +int ezx_pcap_set_vaux(u_int8_t vaux, u_int8_t what, u_int8_t val)
> +{
> +       u_int8_t reg, shift, mask;
> +       u_int32_t tmp;
> +
> +       switch (what) {
> +       case VAUX_EN:
> +               reg = PCAP_REG_AUXVREG;
> +               shift = vaux_table[vaux][VAUX_EN];
> +               mask = 0x1;
> +               break;
> +       case VAUX_VAL:
> +               reg = PCAP_REG_AUXVREG;
> +               shift = vaux_table[vaux][VAUX_VAL];
> +               mask = vaux_table[vaux][VAUX_MASK];
> +               break;
> +       case VAUX_STBY:
> +               if (vaux == VAUX1) /* exception */
> +                       reg = PCAP_REG_AUXVREG;
> +               else
> +                       reg = PCAP_REG_LOWPWR;
> +               shift = vaux_table[vaux][VAUX_STBY];
> +               mask = 0x1;
> +               break;
> +       case VAUX_LOWPWR:
> +               if (vaux == VAUX1)
> +                       reg = PCAP_REG_AUXVREG;
> +               else
> +                       reg = PCAP_REG_LOWPWR;
> +               shift = vaux_table[vaux][VAUX_LOWPWR];
> +               mask = 0x1;
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       /* invalid setting */
> +       if (shift == 0xff || val > mask)
> +               return -EINVAL;
> +
> +       ezx_pcap_read(reg, &tmp);
> +       tmp &= ~(mask << shift);
> +       tmp |= ((val & mask) << shift);
> +       ezx_pcap_write(reg, tmp);
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(ezx_pcap_set_vaux);
> +
> +/* IRQ Handling */
> +
> +/* Array indexed by BIT POSITION of PCAP register, returns IRQ number */
> +static unsigned int pcap2irq[] = {
> +       [0]     = EZX_IRQ_ADCDONE,
> +       [1]     = EZX_IRQ_TS,
> +       [2]     = EZX_IRQ_1HZ, /* 1HZ */
> +       [3]     = EZX_IRQ_WH, /* WH */
> +       [4]     = EZX_IRQ_WL, /* WL */
> +       [5]     = EZX_IRQ_TODA, /* TODA */
> +       [6]     = EZX_IRQ_USB4V,
> +       [7]     = EZX_IRQ_ONOFF, /* ONOFF */
> +       [8]     = EZX_IRQ_ONOFF2, /* ONOFF2 */
> +       [9]     = EZX_IRQ_USB1V,
> +       [10]    = EZX_IRQ_MOBPORT, /* MOBPORT */
> +       [11]    = EZX_IRQ_MIC,
> +       [12]    = EZX_IRQ_HEADJACK,
> +       [13]    = EZX_IRQ_ST, /* ST */
> +       [14]    = EZX_IRQ_PC, /* PC */
> +       [15]    = EZX_IRQ_WARM, /* WARM */
> +       [16]    = EZX_IRQ_EOL, /* EOL */
> +       [17]    = EZX_IRQ_CLK, /* CLK */
> +       [18]    = EZX_IRQ_SYSRST, /* SYSRST */
> +       [19]    = 0,
> +       [20]    = EZX_IRQ_ADCDONE2,
> +       [21]    = EZX_IRQ_SOFTRESET, /* SOFTRESET */
> +       [22]    = EZX_IRQ_MNEXB, /* MNEXB */
> +};
> +
> +/* Array indexed by IRQ NUMBER, returns PCAP absolute value */
> +static unsigned int irq2pcap[] = {
> +       [EZX_IRQ_MNEXB]         = PCAP_IRQ_MNEXB,
> +       [EZX_IRQ_SOFTRESET]     = PCAP_IRQ_SOFTRESET,
> +       [EZX_IRQ_SYSRST]        = PCAP_IRQ_SYSRST,
> +       [EZX_IRQ_CLK]           = PCAP_IRQ_CLK,
> +       [EZX_IRQ_EOL]           = PCAP_IRQ_EOL,
> +       [EZX_IRQ_WARM]          = PCAP_IRQ_WARM,
> +       [EZX_IRQ_PC]            = PCAP_IRQ_PC,
> +       [EZX_IRQ_ST]            = PCAP_IRQ_ST,
> +       [EZX_IRQ_MOBPORT]       = PCAP_IRQ_MOBPORT,
> +       [EZX_IRQ_ONOFF2]        = PCAP_IRQ_ONOFF2,
> +       [EZX_IRQ_ONOFF]         = PCAP_IRQ_ONOFF,
> +       [EZX_IRQ_TODA]          = PCAP_IRQ_TODA,
> +       [EZX_IRQ_WL]            = PCAP_IRQ_WL,
> +       [EZX_IRQ_WH]            = PCAP_IRQ_WH,
> +       [EZX_IRQ_1HZ]           = PCAP_IRQ_1HZ,
> +       [EZX_IRQ_USB4V]         = PCAP_IRQ_USB4V,
> +       [EZX_IRQ_USB1V]         = PCAP_IRQ_USB1V,
> +       [EZX_IRQ_HEADJACK]      = PCAP_IRQ_A1,
> +       [EZX_IRQ_MIC]           = PCAP_IRQ_MB2,
> +       [EZX_IRQ_TS]            = PCAP_IRQ_TS,
> +       [EZX_IRQ_ADCDONE]       = PCAP_IRQ_ADCDONE,
> +       [EZX_IRQ_ADCDONE2]      = PCAP_IRQ_ADCDONE2,
> +};
> +
> +static void pcap_ack_irq(unsigned int irq)
> +{
> +       DEBUGP("pcap_ack_irq: %u\n", irq);
> +       ezx_pcap_write(PCAP_REG_ISR, irq2pcap[irq]);
> +}
> +
> +static void pcap_mask_irq(unsigned int irq)
> +{
> +       u_int32_t reg;
> +       unsigned long flag;
> +
> +       spin_lock_irqsave(&ezx_ssp_lock, flag);
> +       DEBUGP("pcap_mask_irq: %u\n", irq);
> +       ezx_pcap_read(PCAP_REG_MSR, &reg);
> +       reg |= irq2pcap[irq];
> +       ezx_pcap_write(PCAP_REG_MSR, reg);
> +       spin_unlock_irqrestore(&ezx_ssp_lock, flag);
> +}
> +
> +static void pcap_unmask_irq(unsigned int irq)
> +{
> +       u_int32_t tmp;
> +       unsigned long flag;
> +
> +       spin_lock_irqsave(&ezx_ssp_lock, flag);
> +       DEBUGP("pcap_unmask_irq: %u\n", irq);
> +       ezx_pcap_read(PCAP_REG_MSR, &tmp);
> +       tmp &= ~irq2pcap[irq];
> +       ezx_pcap_write(PCAP_REG_MSR, tmp);
> +       spin_unlock_irqrestore(&ezx_ssp_lock, flag);
> +}
> +
> +static struct irq_chip pcap_chip = {
> +       .name   = "ezx-pcap",
> +       .ack    = pcap_ack_irq,
> +       .mask   = pcap_mask_irq,
> +       .unmask = pcap_unmask_irq,
> +};
> +
> +/* handler for interrupt received from PCAP via GPIO */
> +static void pcap_irq_demux_handler(unsigned int irq, struct irq_desc *desc)
> +{
> +       int i;
> +       u_int32_t isr;
> +
> +       desc->chip->ack(irq);
> +       ezx_pcap_read(PCAP_REG_ISR, &isr);
> +       for (i = ARRAY_SIZE(pcap2irq)-1; i >= 0; i--) {
> +               unsigned int pirq = pcap2irq[i];
> +               if (!(isr & irq2pcap[pirq]))
> +                       continue;
> +               desc = &irq_desc[pirq];
> +               DEBUGP("found irq %u\n", pirq);
> +               desc_handle_irq(pirq, desc);
> +       }
> +}
> +
> +static int ezx_pcap_remove(struct platform_device *pdev)
> +{
> +       int irq;
> +       DEBUGP("exz_pcap_remove entered\n");
> +
> +       set_irq_chained_handler(pcap_irq, NULL);
> +
> +       for (irq = EZX_IRQ(0); irq <= EZX_IRQ(21); irq++) {
> +               set_irq_chip(irq, NULL);
> +               set_irq_handler(irq, NULL);
> +               set_irq_flags(irq, 0);
> +       }
> +
> +       ssp_exit(&ezx_ssp_dev);
> +
> +       return 0;
> +}
> +
> +static int __init ezx_pcap_probe(struct platform_device *pdev)
> +{
> +       unsigned int ret, irq;
> +       DEBUGP("ezx_pcap_probe entered\n");
> +
> +       pcap_data = pdev->dev.platform_data;
> +
> +       if (pcap_data->cs >= 0) {
> +               if (machine_is_ezx_a780() || machine_is_ezx_e680())
> +                       gpio_direction_output(pcap_data->cs, 1);
> +               else
> +                       gpio_direction_output(pcap_data->cs, 0);
> +       }
> +       pcap_irq = platform_get_irq(pdev, 0);
> +       if (pcap_irq < 0) {
> +               printk(KERN_ERR "Unable to get IRQ for pcap!\n");
> +               return pcap_irq;
> +       }
> +
> +       ret = ssp_init(&ezx_ssp_dev, pcap_data->port, 0);
> +       if (ret) {
> +               printk(KERN_ERR "Unable to register SSP handler!\n");
> +               return ret;
> +       }
> +
> +       ssp_disable(&ezx_ssp_dev);
> +       ssp_config(&ezx_ssp_dev,
> +               (SSCR0_Motorola | SSCR0_DataSize(16) | SSCR0_EDSS),
> +               (SSCR1_TxTresh(1) | SSCR1_RxTresh(1)),
> +               0, SSCR0_SerClkDiv(pcap_data->clk));
> +       ssp_enable(&ezx_ssp_dev);
> +
> +       if (pcap_data->init)
> +               pcap_data->init();
> +
> +       /* set up interrupt demultiplexing code for PCAP2 irqs */
> +       for (irq = EZX_IRQ(0); irq <= EZX_IRQ(21); irq++) {
> +               set_irq_chip(irq, &pcap_chip);
> +               set_irq_handler(irq, handle_level_irq);
> +               set_irq_flags(irq, IRQF_VALID);
> +       }
> +       set_irq_type(pcap_irq, IRQ_TYPE_EDGE_RISING);
> +       set_irq_chained_handler(pcap_irq, pcap_irq_demux_handler);
> +       set_irq_wake(pcap_irq, 1);
> +
> +       /* mask/ack all PCAP interrupts */
> +       ezx_pcap_write(PCAP_REG_MSR, PCAP_MASK_ALL_INTERRUPT);
> +       ezx_pcap_write(PCAP_REG_ISR, PCAP_CLEAR_INTERRUPT_REGISTER);
> +
> +
> +       printk(KERN_INFO "ezx-pcap: ssp driver registered\n");
> +       return ret;
> +}
> +
> +#ifdef CONFIG_PM
> +static int ezx_pcap_suspend(struct platform_device *dev, pm_message_t state)
> +{
> +       DEBUGP("pcap suspend!\n");
> +       ssp_flush(&ezx_ssp_dev);
> +       ssp_save_state(&ezx_ssp_dev, &ezx_ssp_state);
> +       ssp_disable(&ezx_ssp_dev);
> +       return 0;
> +}
> +
> +static int ezx_pcap_resume(struct platform_device *dev)
> +{
> +       DEBUGP("pcap resume!\n");
> +       ssp_restore_state(&ezx_ssp_dev, &ezx_ssp_state);
> +       ssp_enable(&ezx_ssp_dev);
> +
> +       return 0;
> +}
> +#endif
> +
> +static struct platform_driver ezxpcap_driver = {
> +       .probe          = ezx_pcap_probe,
> +       .remove         = ezx_pcap_remove,
> +#ifdef CONFIG_PM
> +       .suspend        = ezx_pcap_suspend,
> +       .resume         = ezx_pcap_resume,
> +#endif
> +       .driver         = {
> +               .name   = "ezx-pcap",
> +               .owner  = THIS_MODULE,
> +       },
> +};
> +
> +static int __init ezx_pcap_init(void)
> +{
> +       DEBUGP("ezx_pcap_init entered\n");
> +       return platform_driver_register(&ezxpcap_driver);
> +}
> +
> +static void __exit ezx_pcap_exit(void)
> +{
> +       return platform_driver_unregister(&ezxpcap_driver);
> +}
> +
> +module_init(ezx_pcap_init);

Depending on what platform_devices depend on this, maybe use
subsys_initcall here.

> +module_exit(ezx_pcap_exit);

Why bother with module_exit when the Kconfig option is bool?

> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Harald Welte");
> +MODULE_DESCRIPTION("Motorola PCAP2 ASIC Driver");
> +
> Index: linux-2.6-arm/include/linux/mfd/ezx-pcap.h
> ===================================================================
> --- /dev/null
> +++ linux-2.6-arm/include/linux/mfd/ezx-pcap.h
> @@ -0,0 +1,248 @@
> +/*
> + * Copyright 2007 Daniel Ribeiro <wyrm@openezx.org>
> + *
> + * For further information, please see http://wiki.openezx.org/PCAP2
> + */
> +
> +#ifndef EZX_PCAP_H
> +#define EZX_PCAP_H
> +
> +struct pcap_platform_data {
> +       int port;               /* SSP port */
> +       int cs;                 /* CS gpio */
> +       int clk;
> +       int (*init)(void);      /* board specific driver init */
> +};
> +
> +#define PCAP_REGISTER_WRITE_OP_BIT     0x80000000
> +#define PCAP_REGISTER_READ_OP_BIT      0x00000000
> +
> +#define PCAP_REGISTER_VALUE_MASK       0x01ffffff
> +#define PCAP_REGISTER_ADDRESS_MASK     0x7c000000
> +#define PCAP_REGISTER_ADDRESS_SHIFT    26
> +#define PCAP_REGISTER_NUMBER           32
> +#define PCAP_CLEAR_INTERRUPT_REGISTER  0x01ffffff
> +#define PCAP_MASK_ALL_INTERRUPT                0x01ffffff
> +
> +
> +#define pbit(reg, bit) ((reg << PCAP_REGISTER_ADDRESS_SHIFT) | bit)
> +
> +/* registers acessible by both pcap ports */
> +#define PCAP_REG_ISR           0x0     /* Interrupt Status */
> +#define PCAP_REG_MSR           0x1     /* Interrupt Mask */
> +#define PCAP_REG_PSTAT         0x2     /* Processor Status */
> +#define PCAP_REG_VREG2         0x6     /* Regulator Bank 2 Control */
> +#define PCAP_REG_AUXVREG       0x7     /* Auxiliary Regulator Control */
> +#define PCAP_REG_BATT          0x8     /* Battery Control */
> +#define PCAP_REG_ADC           0x9     /* AD Control */
> +#define PCAP_REG_ADR           0xa     /* AD Result */
> +#define PCAP_REG_CODEC         0xb     /* Audio Codec Control */
> +#define PCAP_REG_RX_AMPS       0xc     /* RX Audio Amplifiers Control */
> +#define PCAP_REG_ST_DAC                0xd     /* Stereo DAC Control */
> +#define PCAP_REG_BUSCTRL       0x14    /* Connectivity Control */
> +#define PCAP_REG_PERIPH                0x15    /* Peripheral Control */
> +#define PCAP_REG_LOWPWR                0x18    /* Regulator Low Power Control */
> +#define PCAP_REG_TX_AMPS       0x1a    /* TX Audio Amplifiers Control */
> +#define PCAP_REG_GP            0x1b    /* General Purpose */
> +
> +/* registers acessible by pcap port 1 only (a1200, e2 & e6) */
> +#define PCAP_REG_INT_SEL       0x3     /* Interrupt Select */
> +#define PCAP_REG_SWCTRL                0x4     /* Switching Regulator Control */
> +#define PCAP_REG_VREG1         0x5     /* Regulator Bank 1 Control */
> +#define PCAP_REG_RTC_TOD       0xe     /* RTC Time of Day */
> +#define PCAP_REG_RTC_TODA      0xf     /* RTC Time of Day Alarm */
> +#define PCAP_REG_RTC_DAY       0x10    /* RTC Day */
> +#define PCAP_REG_RTC_DAYA      0x11    /* RTC Day Alarm */
> +#define PCAP_REG_MTRTMR                0x12    /* AD Monitor Timer */
> +#define PCAP_REG_PWR           0x13    /* Power Control */
> +#define PCAP_REG_AUXVREG_MASK  0x16    /* Auxiliary Regulator Mask */
> +#define PCAP_REG_VENDOR_REV    0x17
> +#define PCAP_REG_PERIPH_MASK   0x19    /* Peripheral Mask */
> +
> +/* interrupts - registers 0x0, 0x1, 0x2, 0x3 */
> +#define PCAP_IRQ_ADCDONE       (1 << 0)        /* AD Conversion Done Port 1 */
> +#define PCAP_IRQ_TS            (1 << 1)        /* Touch Screen */
> +#define PCAP_IRQ_1HZ           (1 << 2)        /* 1HZ Timer */
> +#define PCAP_IRQ_WH            (1 << 3)        /* "...high"??? */
> +#define PCAP_IRQ_WL            (1 << 4)        /* "...low"??? */
> +#define PCAP_IRQ_TODA          (1 << 5)        /* RTC Time Of Day?
> +                                                    (see "RTC_TODA") */
> +#define PCAP_IRQ_USB4V         (1 << 6)        /* USB above 4volt???
> +                                               called "USBDET_4V" in blob */

I assume that's for OTG operation. The VBUS voltage is valid from 4.4 V, and the
PXA27x UDC controller has "Vbus valid 4.0 V" and "Vbus valid 4.4 V" interrupts

> +#define PCAP_IRQ_ONOFF         (1 << 7)        /* in blob: "ONOFFSNS" */
> +#define PCAP_IRQ_ONOFF2                (1 << 8)        /* in blob: "ONOFFSNS2" */
> +#define PCAP_IRQ_USB1V         (1 << 9)        /* USB below 1volt???
> +                                                    in blob: "USBDET_1V" */
> +#define PCAP_IRQ_MOBPORT       (1 << 10)       /* GSM-related?? ("mobport",
> +                               see 958_MotDoc.pdf); in blob: "MOBSENSB" */
> +#define PCAP_IRQ_MB2           (1 << 11)       /* Mic; in blob: "MB2SNS" */
> +#define PCAP_IRQ_A1            (1 << 12)       /* Audio jack;
> +                                                    in blob: "A1SNS" */
> +#define PCAP_IRQ_ST            (1 << 13)       /* called "MSTB" in blob */
> +#define PCAP_IRQ_PC            (1 << 14)
> +#define PCAP_IRQ_WARM          (1 << 15)
> +#define PCAP_IRQ_EOL           (1 << 16)       /* battery End Of Life???
> +                                       (see below); in blob: "EOL_STAT" */
> +#define PCAP_IRQ_CLK           (1 << 17)       /* called "CLK_STAT" in blob */
> +#define PCAP_IRQ_SYSRST                (1 << 18)
> +#define PCAP_IRQ_ADCDONE2      (1 << 20)       /* AD Conversion Done Port 2 */
> +#define PCAP_IRQ_SOFTRESET     (1 << 21)
> +#define PCAP_IRQ_MNEXB         (1 << 22)
> +
> +/* register VREG2 (0x6) */
> +#define PCAP_VREG2_V1_STBY     (1 << 0)
> +#define PCAP_VREG2_V2_STBY     (1 << 1)
> +/* V3, SRAM: */
> +#define PCAP_VREG2_V3_STBY     (1 << 2)
> +#define PCAP_VREG2_V4_STBY     (1 << 3)
> +#define PCAP_VREG2_V5_STBY     (1 << 4)
> +/* V6, E680 I2C camera?: */
> +#define PCAP_VREG2_V6_STBY     (1 << 5)
> +#define PCAP_VREG2_V7_STBY     (1 << 6)
> +/* V8, PLL: */
> +#define PCAP_VREG2_V8_STBY     (1 << 7)
> +#define PCAP_VREG2_V9_STBY     (1 << 8)
> +#define PCAP_VREG2_V10_STBY    (1 << 9)
> +#define PCAP_VREG2_V1_LOWPWR   (1 << 10)
> +#define PCAP_VREG2_V2_LOWPWR   (1 << 11)
> +/* V3, SRAM: */
> +#define PCAP_VREG2_V3_LOWPWR   (1 << 12)
> +#define PCAP_VREG2_V4_LOWPWR   (1 << 13)
> +#define PCAP_VREG2_V5_LOWPWR   (1 << 14)
> +/* V6, E680 I2C camera?: */
> +#define PCAP_VREG2_V6_LOWPWR   (1 << 15)
> +#define PCAP_VREG2_V7_LOWPWR   (1 << 16)
> +/* V8, PLL: */
> +#define PCAP_VREG2_V8_LOWPWR   (1 << 17)
> +#define PCAP_VREG2_V9_LOWPWR   (1 << 18)
> +#define PCAP_VREG2_V10_LOWPWR  (1 << 19)
> +
> +/* register AUXVREG (0x7) */
> +#define VAUX1          0
> +#define VAUX2          1
> +#define VAUX3          2
> +#define VAUX4          3
> +#define VSIM           4
> +#define VSIM2          5
> +#define VVIB           6
> +#define VC             7
> +
> +#define VAUX_EN                0
> +#define VAUX_VAL       1
> +#define VAUX_MASK      2
> +#define VAUX_STBY      3
> +#define VAUX_LOWPWR    4
> +
> +#define PCAP_BATT_DAC_MASK             0x000000ff
> +#define PCAP_BATT_DAC_SHIFT            0
> +#define PCAP_BATT_B_FDBK               (1 << 8)
> +#define PCAP_BATT_EXT_ISENSE           (1 << 9)
> +#define PCAP_BATT_V_COIN_MASK          0x00003c00
> +#define PCAP_BATT_V_COIN_SHIFT         10
> +#define PCAP_BATT_I_COIN               (1 << 14)
> +#define PCAP_BATT_COIN_CH_EN           (1 << 15)
> +#define PCAP_BATT_EOL_SEL_MASK         0x000e0000
> +#define PCAP_BATT_EOL_SEL_SHIFT                17
> +#define PCAP_BATT_EOL_CMP_EN           (1 << 20)
> +#define PCAP_BATT_BATT_DET_EN          (1 << 21)
> +#define PCAP_BATT_THERMBIAS_CTRL       (1 << 22)
> +
> +#define PCAP_ADC_ADEN                  (1 << 0)
> +#define PCAP_ADC_RAND                  (1 << 1)
> +#define PCAP_ADC_AD_SEL1               (1 << 2)
> +#define PCAP_ADC_AD_SEL2               (1 << 3)
> +#define PCAP_ADC_ADA1_MASK             0x00000070
> +#define PCAP_ADC_ADA1_SHIFT            4
> +#define PCAP_ADC_ADA2_MASK             0x00000380
> +#define PCAP_ADC_ADA2_SHIFT            7
> +#define PCAP_ADC_ATO_MASK              0x00003c00
> +#define PCAP_ADC_ATO_SHIFT             10
> +#define PCAP_ADC_ATOX                  (1 << 14)
> +#define PCAP_ADC_MTR1                  (1 << 15)
> +#define PCAP_ADC_MTR2                  (1 << 16)
> +#define PCAP_ADC_TS_M_MASK             0x000e0000
> +#define PCAP_ADC_TS_M_SHIFT            17
> +#define PCAP_ADC_TS_REF_LOWPWR         (1 << 20)
> +#define PCAP_ADC_TS_REFENB             (1 << 21)
> +#define PCAP_ADC_BATT_I_POLARITY       (1 << 22)
> +#define PCAP_ADC_BATT_I_ADC            (1 << 23)
> +
> +#define PCAP_ADR_ADD1_MASK             0x000003ff
> +#define PCAP_ADR_ADD1_SHIFT            0
> +#define PCAP_ADR_ADD2_MASK             0x000ffc00
> +#define PCAP_ADR_ADD2_SHIFT            10
> +#define PCAP_ADR_ADINC1                        (1 << 20)
> +#define PCAP_ADR_ADINC2                        (1 << 21)
> +#define PCAP_ADR_ASC                   (1 << 22)
> +#define PCAP_ADR_ONESHOT               (1 << 23)
> +
> +#define PCAP_BUSCTRL_FSENB             (1 << 0)
> +#define PCAP_BUSCTRL_USB_SUSPEND       (1 << 1)
> +#define PCAP_BUSCTRL_USB_PU            (1 << 2)
> +#define PCAP_BUSCTRL_USB_PD            (1 << 3)
> +#define PCAP_BUSCTRL_VUSB_EN           (1 << 4)
> +#define PCAP_BUSCTRL_USB_PS            (1 << 5)
> +#define PCAP_BUSCTRL_VUSB_MSTR_EN      (1 << 6)
> +#define PCAP_BUSCTRL_VBUS_PD_ENB       (1 << 7)
> +#define PCAP_BUSCTRL_CURRLIM           (1 << 8)
> +#define PCAP_BUSCTRL_RS232ENB          (1 << 9)
> +#define PCAP_BUSCTRL_RS232_DIR         (1 << 10)
> +#define PCAP_BUSCTRL_SE0_CONN          (1 << 11)
> +#define PCAP_BUSCTRL_USB_PDM           (1 << 12)
> +#define PCAP_BUSCTRL_BUS_PRI_ADJ       (1 << 24)
> +
> +#define PCAP_BIT_PERIPH_BL_CTRL0       0x54000001
> +#define PCAP_BIT_PERIPH_BL_CTRL1       0x54000002
> +#define PCAP_BIT_PERIPH_BL_CTRL2       0x54000004
> +#define PCAP_BIT_PERIPH_BL_CTRL3       0x54000008
> +#define PCAP_BIT_PERIPH_BL_CTRL4       0x54000010
> +#define PCAP_BIT_PERIPH_LEDR_EN                0x54000020
> +#define PCAP_BIT_PERIPH_LEDG_EN                0x54000040
> +#define PCAP_BIT_PERIPH_LEDR_CTRL0     0x54000080
> +#define PCAP_BIT_PERIPH_LEDR_CTRL1     0x54000100
> +#define PCAP_BIT_PERIPH_LEDR_CTRL2     0x54000200
> +#define PCAP_BIT_PERIPH_LEDR_CTRL3     0x54000400
> +#define PCAP_BIT_PERIPH_LEDG_CTRL0     0x54000800
> +#define PCAP_BIT_PERIPH_LEDG_CTRL1     0x54001000
> +#define PCAP_BIT_PERIPH_LEDG_CTRL2     0x54002000
> +#define PCAP_BIT_PERIPH_LEDG_CTRL3     0x54004000
> +#define PCAP_BIT_PERIPH_LEDR_I0                0x54008000
> +#define PCAP_BIT_PERIPH_LEDR_I1                0x54010000
> +#define PCAP_BIT_PERIPH_LEDG_I0                0x54020000
> +#define PCAP_BIT_PERIPH_LEDG_I1                0x54040000
> +#define PCAP_BIT_PERIPH_SKIP           0x54080000
> +#define PCAP_BIT_PERIPH_BL2_CTRL0      0x54100000
> +#define PCAP_BIT_PERIPH_BL2_CTRL1      0x54200000
> +#define PCAP_BIT_PERIPH_BL2_CTRL2      0x54400000
> +#define PCAP_BIT_PERIPH_BL2_CTRL3      0x54800000
> +#define PCAP_BIT_PERIPH_BL2_CTRL4      0x55000000
> +
> +/* LOWPWR */
> +#define SW1            8
> +#define SW2            16
> +
> +#define SW_MODE                0
> +#define SW_VOLTAGE     4
> +
> +#define SW_VOLTAGE_900 0x0
> +#define SW_VOLTAGE_950 0x1
> +#define SW_VOLTAGE_1000        0x2
> +#define SW_VOLTAGE_1050        0x3
> +#define SW_VOLTAGE_1100        0x4
> +#define SW_VOLTAGE_1150        0x5
> +#define SW_VOLTAGE_1200        0x6
> +#define SW_VOLTAGE_1250        0x7
> +#define SW_VOLTAGE_1300        0x8
> +#define SW_VOLTAGE_1350        0x9
> +#define SW_VOLTAGE_1400        0xa
> +#define SW_VOLTAGE_1500        0xb
> +#define SW_VOLTAGE_1600        0xc
> +#define SW_VOLTAGE_1875        0xd
> +#define SW_VOLTAGE_2250        0xe
> +#define SW_VOLTAGE_4400        0xf
> +
> +void ezx_pcap_write(u_int8_t, u_int32_t);
> +void ezx_pcap_read(u_int8_t, u_int32_t *);
> +void ezx_pcap_set_sw(u_int8_t, u_int8_t, u_int8_t);
> +int ezx_pcap_set_vaux(u_int8_t, u_int8_t, u_int8_t);
> +#endif
> Index: linux-2.6-arm/include/asm-arm/arch-pxa/irqs.h
> ===================================================================
> --- linux-2.6-arm.orig/include/asm-arm/arch-pxa/irqs.h
> +++ linux-2.6-arm/include/asm-arm/arch-pxa/irqs.h
> @@ -89,7 +89,7 @@
>  * within sensible limits.
>  */
>  #define IRQ_BOARD_START                (PXA_GPIO_IRQ_BASE + PXA_GPIO_IRQ_NUM)
> -#define IRQ_BOARD_END          (IRQ_BOARD_START + 16)
> +#define IRQ_BOARD_END          (IRQ_BOARD_START + 22)
>
>  #define IRQ_SA1111_START       (IRQ_BOARD_END)
>  #define IRQ_GPAIN0             (IRQ_BOARD_END + 0)
> @@ -183,7 +183,8 @@
>       defined(CONFIG_MACH_TOSA) || \
>       defined(CONFIG_MACH_MAINSTONE) || \
>       defined(CONFIG_MACH_PCM027) || \
> -      defined(CONFIG_MACH_MAGICIAN)
> +      defined(CONFIG_MACH_MAGICIAN) || \
> +      defined(CONFIG_PXA_EZX)
>  #define NR_IRQS                        (IRQ_BOARD_END)
>  #else
>  #define NR_IRQS                        (IRQ_BOARD_START)
> @@ -237,6 +238,31 @@
>  #define PCM027_MMCDET_IRQ      PCM027_IRQ(2)
>  #define PCM027_PM_5V_IRQ       PCM027_IRQ(3)
>
> +/* EZX Interrupts (CONFIG_EZX) */
> +#define EZX_IRQ(x)         (IRQ_BOARD_START + (x))
> +#define EZX_IRQ_USB4V      EZX_IRQ(0) /* EMU */
> +#define EZX_IRQ_USB1V      EZX_IRQ(1) /* EMU */
> +#define EZX_IRQ_HEADJACK   EZX_IRQ(2) /* Audio connector */
> +#define EZX_IRQ_MIC        EZX_IRQ(3) /* Audio connector */
> +#define EZX_IRQ_ADCDONE    EZX_IRQ(4)
> +#define EZX_IRQ_TS         EZX_IRQ(5) /* TS touch */
> +#define EZX_IRQ_ADCDONE2   EZX_IRQ(6) /* TS x/y ADC ready */
> +#define EZX_IRQ_WH         EZX_IRQ(7)
> +#define EZX_IRQ_WL         EZX_IRQ(8)
> +#define EZX_IRQ_ONOFF      EZX_IRQ(9)
> +#define EZX_IRQ_ONOFF2     EZX_IRQ(10)
> +#define EZX_IRQ_MOBPORT    EZX_IRQ(11)
> +#define EZX_IRQ_TODA       EZX_IRQ(12)
> +#define EZX_IRQ_1HZ        EZX_IRQ(13)
> +#define EZX_IRQ_MNEXB      EZX_IRQ(14)
> +#define EZX_IRQ_ST         EZX_IRQ(15)
> +#define EZX_IRQ_PC         EZX_IRQ(16)
> +#define EZX_IRQ_SYSRST     EZX_IRQ(17)
> +#define EZX_IRQ_SOFTRESET  EZX_IRQ(18)
> +#define EZX_IRQ_EOL        EZX_IRQ(19)
> +#define EZX_IRQ_CLK        EZX_IRQ(20)
> +#define EZX_IRQ_WARM       EZX_IRQ(21)
> +
>  /* ITE8152 irqs */
>  /* add IT8152 IRQs beyond BOARD_END */
>  #ifdef CONFIG_PCI_HOST_ITE8152
> Index: linux-2.6-arm/drivers/mfd/Kconfig
> ===================================================================
> --- linux-2.6-arm.orig/drivers/mfd/Kconfig
> +++ linux-2.6-arm/drivers/mfd/Kconfig
> @@ -49,6 +49,13 @@
>        help
>          Support for Toshiba Mobile IO Controller TC6393XB
>
> +config EZX_PCAP
> +       bool "PCAP Support"
> +       depends on PXA_SSP && PXA_EZX
> +       help
> +         This enables the PCAP ASIC present on EZX Phones. This is
> +         needed for MMC, TouchScreen, Sound, USB, etc..
> +
>  endmenu
>
>  menu "Multimedia Capabilities Port drivers"
> Index: linux-2.6-arm/drivers/mfd/Makefile
> ===================================================================
> --- linux-2.6-arm.orig/drivers/mfd/Makefile
> +++ linux-2.6-arm/drivers/mfd/Makefile
> @@ -12,6 +12,8 @@
>
>  obj-$(CONFIG_MFD_CORE)         += mfd-core.o
>
> +obj-$(CONFIG_EZX_PCAP)         += ezx-pcap.o
> +
>  obj-$(CONFIG_MCP)              += mcp-core.o
>  obj-$(CONFIG_MCP_SA11X0)       += mcp-sa11x0.o
>  obj-$(CONFIG_MCP_UCB1200)      += ucb1x00-core.o
> Index: linux-2.6-arm/arch/arm/mach-pxa/Kconfig
> ===================================================================
> --- linux-2.6-arm.orig/arch/arm/mach-pxa/Kconfig
> +++ linux-2.6-arm/arch/arm/mach-pxa/Kconfig
> @@ -224,6 +224,8 @@
>        select PXA27x
>        select IWMMXT
>        select HAVE_PWM
> +       select PXA_SSP
> +       select EZX_PCAP
>
>  config MACH_EZX_A780
>        bool "Motorola EZX A780"
> Index: linux-2.6-arm/arch/arm/mach-pxa/ezx.c
> ===================================================================
> --- linux-2.6-arm.orig/arch/arm/mach-pxa/ezx.c
> +++ linux-2.6-arm/arch/arm/mach-pxa/ezx.c
> @@ -15,7 +15,9 @@
>  #include <linux/init.h>
>  #include <linux/platform_device.h>
>  #include <linux/delay.h>
> +#include <linux/gpio.h>
>  #include <linux/pwm_backlight.h>
> +#include <linux/mfd/ezx-pcap.h>
>
>  #include <asm/setup.h>
>  #include <asm/arch/pxafb.h>
> @@ -87,6 +89,51 @@
>        .lcd_conn       = LCD_COLOR_TFT_18BPP,
>  };
>
> +/* PCAP */
> +static int ezx_pcap_init(void)
> +{
> +       /* disable all voltage regulators */
> +       ezx_pcap_write(PCAP_REG_AUXVREG, 0);
> +
> +       /* set SW1 sleep to keep SW1 1.3v in sync mode */
> +       /*  SW1 active in sync mode */
> +       ezx_pcap_set_sw(SW1, SW_MODE, 0x1);
> +
> +       /*  set core voltage */
> +       ezx_pcap_set_sw(SW1, SW_VOLTAGE, SW_VOLTAGE_1250);

Btw, did you see the voltage regulator framework that is in linux-next?

> +
> +       /* redirect all interrupts to AP */
> +       if (!(machine_is_ezx_a780() || machine_is_ezx_e680()))
> +               ezx_pcap_write(PCAP_REG_INT_SEL, 0);
> +
> +       return 0;
> +}
> +
> +static struct pcap_platform_data ezx_pcap_platform_data = {
> +       .port   = 1,
> +       .cs     = 24,
> +       .clk    = 1,
> +       .init   = ezx_pcap_init,
> +};
> +
> +static struct resource ezx_pcap_resources[] = {
> +       [0] = {
> +               .start      = IRQ_GPIO1,
> +               .end        = IRQ_GPIO1,
> +               .flags      = IORESOURCE_IRQ,
> +       },
> +};
> +
> +struct platform_device ezx_pcap_device = {
> +       .name       = "ezx-pcap",
> +       .id     = -1,
> +       .num_resources  = ARRAY_SIZE(ezx_pcap_resources),
> +       .resource   = ezx_pcap_resources,
> +       .dev        = {
> +               .platform_data = &ezx_pcap_platform_data,
> +       },
> +};
> +
>  static struct platform_device *devices[] __initdata = {
>        &ezx_backlight_device,
>  };
> @@ -105,6 +152,11 @@
>        GPIO46_STUART_RXD,
>        GPIO47_STUART_TXD,
>
> +       /* PCAP SSP */
> +       GPIO29_SSP1_SCLK,
> +       GPIO25_SSP1_TXD,
> +       GPIO26_SSP1_RXD,
> +
>        /* For A780 support (connected with Neptune GSM chip) */
>        GPIO30_USB_P3_2,        /* ICL_TXENB */
>        GPIO31_USB_P3_6,        /* ICL_VPOUT */
> @@ -122,7 +174,7 @@
>                set_pxa_fb_info(&ezx_fb_info_1);
>        else
>                set_pxa_fb_info(&ezx_fb_info_2);
> -
> +       platform_device_register(&ezx_pcap_device);
>        platform_add_devices(devices, ARRAY_SIZE(devices));

You could put ezx_pcap_device into the beginning of devices[].

>  }
>
>
> --
>

regards
Philipp

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Patch 06/10] mfd: PCAP driver for the Motorola EZX GSM mobile phones
  2008-07-07 19:21   ` pHilipp Zabel
@ 2008-07-07 20:02     ` Stefan Schmidt
  2008-07-08  6:49       ` Eric Miao
  0 siblings, 1 reply; 12+ messages in thread
From: Stefan Schmidt @ 2008-07-07 20:02 UTC (permalink / raw)
  To: pHilipp Zabel; +Cc: linux-arm-kernel, sameo, linux-kernel, Daniel Ribeiro

Hello.

On Mon, 2008-07-07 at 21:21, pHilipp Zabel wrote:
> On Mon, Jul 7, 2008 at 8:40 PM,  <stefan@datenfreihafen.org> wrote:
> > The PCAP Asic as present on EZX phones is a multi function device with voltage
> > regulators, irq expander, touch screen controller and audio codec.
> > It is connected to the processor via SPI, this driver provides read/write
> > functions to its registers and a irq demultiplexer.
> 
> Are the EZX phones expected to be the only linux devices with PCAP?

That is somewhat hard to predict as Motorola was not interested in helping us
with the driver for mainline and we never got datasheets or technical support.
>From what we have researched it seems that they use an antecessor of this chip
in older feature phones. As far as we know none of them runs linux.

> Otherwise I'd dislike the hardcoded IRQ numbers in the MFD driver.

So far it seems to be no problem. If there is a strong feeling to change this we
will of course work on that. Personally I would prefer to do this when new users
of this driver appear.

> The machine_is_xyz() calls inside ezx-pcap could be replaced with
> configuration via platform_data (have pcap_platform_data->cs_inverted,
> for example)

We had this before and it turned out as messy as this solution. We have one
machine file that supports up to 6 devices right now. So we would have to deal
with the same machine_is_xyz macros in the machine file.

We thought it would be better to let the driver handle this. Open for discussion
of course.

> > +#if 0
> > +#define DEBUGP(x, args...) printk(x, ## args)
> > +#else
> > +#define DEBUGP(x, args...)
> > +#endif
> 
> Could you remove the custom debug macros and use pr_debug (or even
> better, dev_dbg) instead?

Will do.

> > +static void __exit ezx_pcap_exit(void)
> > +{
> > +       return platform_driver_unregister(&ezxpcap_driver);
> > +}
> > +
> > +module_init(ezx_pcap_init);
> 
> Depending on what platform_devices depend on this, maybe use
> subsys_initcall here.

Ok, I'll need to look up on this to see if it is an option.

> > +module_exit(ezx_pcap_exit);
> 
> Why bother with module_exit when the Kconfig option is bool?

Well, matter of taste. :)

Taking care about cleanup is good style. Still you are right that it is useles
for the time being.

> > +#define PCAP_IRQ_USB4V         (1 << 6)        /* USB above 4volt???
> > +                                               called "USBDET_4V" in blob */
> 
> I assume that's for OTG operation. The VBUS voltage is valid from 4.4 V, and the
> PXA27x UDC controller has "Vbus valid 4.0 V" and "Vbus valid 4.4 V" interrupts

ok

> > +       /*  set core voltage */
> > +       ezx_pcap_set_sw(SW1, SW_VOLTAGE, SW_VOLTAGE_1250);
> 
> Btw, did you see the voltage regulator framework that is in linux-next?

Heard of it, but not read the code yet. It's on my list. I just a bit hesitant
to base on code that is not in mainline yet. But -next should be ok.

> > -
> > +       platform_device_register(&ezx_pcap_device);
> >        platform_add_devices(devices, ARRAY_SIZE(devices));
> 
> You could put ezx_pcap_device into the beginning of devices[].

Duh, will fix.

I send out an updated patch tomorrow. Need some sleep now.

Thanks for the feedback.

regards
Stefan Schmidt

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Patch 06/10] mfd: PCAP driver for the Motorola EZX GSM mobile phones
  2008-07-07 20:02     ` Stefan Schmidt
@ 2008-07-08  6:49       ` Eric Miao
  2008-07-08 14:01         ` Daniel Ribeiro
  0 siblings, 1 reply; 12+ messages in thread
From: Eric Miao @ 2008-07-08  6:49 UTC (permalink / raw)
  To: Stefan Schmidt
  Cc: pHilipp Zabel, linux-arm-kernel, sameo, linux-kernel, Daniel Ribeiro

Stefan Schmidt wrote:

>> Otherwise I'd dislike the hardcoded IRQ numbers in the MFD driver.
> 
> So far it seems to be no problem. If there is a strong feeling to change this we
> will of course work on that. Personally I would prefer to do this when new users
> of this driver appear.

The irq2pcap[] array looks horrible to me. It's actually a sparse array.
Isn't there a nice 1:1 mapping using a formular??

Besides, the IRQ numbering scheme has now changed to a more generic way,
I suggest to pull from Russell's latest git tree and rebase the IRQ
part.

> 
>> The machine_is_xyz() calls inside ezx-pcap could be replaced with
>> configuration via platform_data (have pcap_platform_data->cs_inverted,
>> for example)
> 
> We had this before and it turned out as messy as this solution. We have one
> machine file that supports up to 6 devices right now. So we would have to deal
> with the same machine_is_xyz macros in the machine file.
> 
> We thought it would be better to let the driver handle this. Open for discussion
> of course.

The following block of code:

+	if (pcap_data->cs >= 0) {
+		if (machine_is_ezx_a780() || machine_is_ezx_e680())
+			gpio_direction_output(pcap_data->cs, 1);
+		else
+			gpio_direction_output(pcap_data->cs, 0);
+	}

has 3 occurrences in the driver (2 in ezx_ssp_pcap_putget, 1 in ezx_pcap_probe)
which is good reason to fold this into the platform data.

Well, if the above is done in platform data, I guess you won't mind another bit
flag (e.g. PCAP_REDIRECT_IRQ or something alike) added in platform data, either

> 
>>> +#if 0
>>> +#define DEBUGP(x, args...) printk(x, ## args)
>>> +#else
>>> +#define DEBUGP(x, args...)
>>> +#endif
>> Could you remove the custom debug macros and use pr_debug (or even
>> better, dev_dbg) instead?
> 
> Will do.
> 
>>> +static void __exit ezx_pcap_exit(void)
>>> +{
>>> +       return platform_driver_unregister(&ezxpcap_driver);
>>> +}
>>> +
>>> +module_init(ezx_pcap_init);
>> Depending on what platform_devices depend on this, maybe use
>> subsys_initcall here.
> 
> Ok, I'll need to look up on this to see if it is an option.
> 
>>> +module_exit(ezx_pcap_exit);
>> Why bother with module_exit when the Kconfig option is bool?
> 
> Well, matter of taste. :)
> 
> Taking care about cleanup is good style. Still you are right that it is useles
> for the time being.
> 
>>> +#define PCAP_IRQ_USB4V         (1 << 6)        /* USB above 4volt???
>>> +                                               called "USBDET_4V" in blob */
>> I assume that's for OTG operation. The VBUS voltage is valid from 4.4 V, and the
>> PXA27x UDC controller has "Vbus valid 4.0 V" and "Vbus valid 4.4 V" interrupts
> 
> ok
> 
>>> +       /*  set core voltage */
>>> +       ezx_pcap_set_sw(SW1, SW_VOLTAGE, SW_VOLTAGE_1250);
>> Btw, did you see the voltage regulator framework that is in linux-next?
> 
> Heard of it, but not read the code yet. It's on my list. I just a bit hesitant
> to base on code that is not in mainline yet. But -next should be ok.
> 
>>> -
>>> +       platform_device_register(&ezx_pcap_device);
>>>        platform_add_devices(devices, ARRAY_SIZE(devices));
>> You could put ezx_pcap_device into the beginning of devices[].
> 
> Duh, will fix.
> 
> I send out an updated patch tomorrow. Need some sleep now.
> 
> Thanks for the feedback.
> 
> regards
> Stefan Schmidt
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Patch 06/10] mfd: PCAP driver for the Motorola EZX GSM mobile phones
  2008-07-08  6:49       ` Eric Miao
@ 2008-07-08 14:01         ` Daniel Ribeiro
  2008-07-08 20:32           ` Stefan Schmidt
  0 siblings, 1 reply; 12+ messages in thread
From: Daniel Ribeiro @ 2008-07-08 14:01 UTC (permalink / raw)
  To: Eric Miao
  Cc: Stefan Schmidt, pHilipp Zabel, linux-arm-kernel, sameo, linux-kernel

Eric Miao escreveu:
> The irq2pcap[] array looks horrible to me. It's actually a sparse array.
> Isn't there a nice 1:1 mapping using a formular??
> 
> Besides, the IRQ numbering scheme has now changed to a more generic way,
> I suggest to pull from Russell's latest git tree and rebase the IRQ
> part.

	Will do as you suggested and get rid of the arrays.

> The following block of code:
> 
> +	if (pcap_data->cs >= 0) {
> +		if (machine_is_ezx_a780() || machine_is_ezx_e680())
> +			gpio_direction_output(pcap_data->cs, 1);
> +		else
> +			gpio_direction_output(pcap_data->cs, 0);
> +	}
> 
> has 3 occurrences in the driver (2 in ezx_ssp_pcap_putget, 1 in ezx_pcap_probe)
> which is good reason to fold this into the platform data.
> 
> Well, if the above is done in platform data, I guess you won't mind another bit
> flag (e.g. PCAP_REDIRECT_IRQ or something alike) added in platform data, either

	Moved to platform data. What should PCAP_REDIRECT_IRQ flag do?

-- 
Daniel Ribeiro

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Patch 06/10] mfd: PCAP driver for the Motorola EZX GSM mobile phones
  2008-07-08 14:01         ` Daniel Ribeiro
@ 2008-07-08 20:32           ` Stefan Schmidt
  2008-07-08 21:19             ` pHilipp Zabel
                               ` (3 more replies)
  0 siblings, 4 replies; 12+ messages in thread
From: Stefan Schmidt @ 2008-07-08 20:32 UTC (permalink / raw)
  To: Daniel Ribeiro
  Cc: Eric Miao, pHilipp Zabel, linux-arm-kernel, sameo, linux-kernel

Hello.

On Tue, 2008-07-08 at 11:01, Daniel Ribeiro wrote:
> Eric Miao escreveu:
> > The irq2pcap[] array looks horrible to me. It's actually a sparse array.
> > Isn't there a nice 1:1 mapping using a formular??
> > 
> > Besides, the IRQ numbering scheme has now changed to a more generic way,
> > I suggest to pull from Russell's latest git tree and rebase the IRQ
> > part.
> 
> 	Will do as you suggested and get rid of the arrays.
> 
> > The following block of code:
> > 
> > +	if (pcap_data->cs >= 0) {
> > +		if (machine_is_ezx_a780() || machine_is_ezx_e680())
> > +			gpio_direction_output(pcap_data->cs, 1);
> > +		else
> > +			gpio_direction_output(pcap_data->cs, 0);
> > +	}
> > 
> > has 3 occurrences in the driver (2 in ezx_ssp_pcap_putget, 1 in ezx_pcap_probe)
> > which is good reason to fold this into the platform data.
> > 
> > Well, if the above is done in platform data, I guess you won't mind another bit
> > flag (e.g. PCAP_REDIRECT_IRQ or something alike) added in platform data, either
> 
> 	Moved to platform data. What should PCAP_REDIRECT_IRQ flag do?

Daniel fixed all of Philipps comments besides the voltage framework. From Eric
comments we updated to the new IRQ part and moved to platform data.

Open items we are still working on:
o Voltage framework. Is this needed for the first merge or can we switch to it
  with a later patch?
o Get rid of the arrays
o Eric, what do you like the PCAP_REDIRECT_IRQ flag to do?

Besides the left items we updated and tested the patch below:


Subject: [@num@/@total@] mfd: PCAP driver for the Motorola EZX GSM mobile phones
To: sameo@openedhand.com
Cc: philipp.zabel@gmail.com, linux-kernel@vger.kernel.org

The PCAP Asic as present on EZX phones is a multi function device with voltage
regulators, irq expander, touch screen controller and audio codec.
It is connected to the processor via SPI, this driver provides read/write
functions to its registers and a irq demultiplexer.

Signed-off-by: Daniel Ribeiro <drwyrm@gmail.com>

PATCH FOLLOWS
KernelVersion: 2.6-arm-git pxa branch

Index: linux-2.6-arm/drivers/mfd/ezx-pcap.c
===================================================================
--- /dev/null
+++ linux-2.6-arm/drivers/mfd/ezx-pcap.c
@@ -0,0 +1,372 @@
+/* Driver for Motorola PCAP2 as present in EZX phones
+ *
+ * This is both a SPI device driver for PCAP itself, as well as
+ * an IRQ demultiplexer for handling PCAP generated events such as
+ * headphone jack sense by downstream drivers.
+ *
+ * Copyright (C) 2006 Harald Welte <laforge@openezx.org>
+ * Copyright (C) 2007-2008 Daniel Ribeiro <drwyrm@gmail.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <linux/proc_fs.h>
+#include <linux/mfd/ezx-pcap.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+
+#include <asm/arch/ssp.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/regs-ssp.h>
+#include <asm/arch/mfp-pxa27x.h>
+#include <asm/arch/irqs.h>
+#include <asm/mach/irq.h>
+
+static DEFINE_SPINLOCK(ezx_ssp_lock);
+static struct ssp_dev ezx_ssp_dev;
+static struct ssp_state ezx_ssp_state;
+static struct pcap_platform_data *pcap_data;
+static int pcap_irq;
+
+static u_int32_t ezx_ssp_pcap_putget(u_int32_t data)
+{
+	unsigned long flag;
+	u_int32_t ret = 0;
+
+	spin_lock_irqsave(&ezx_ssp_lock, flag);
+	if (pcap_data->cs >= 0) {
+		if (pcap_data->config & CS_INVERTED)
+			gpio_set_value(pcap_data->cs, 0);
+		else
+			gpio_set_value(pcap_data->cs, 1);
+	}
+
+	ssp_write_word(&ezx_ssp_dev, data);
+	ssp_read_word(&ezx_ssp_dev, &ret);
+
+	if (pcap_data->cs >= 0) {
+		if (pcap_data->config & CS_INVERTED)
+			gpio_set_value(pcap_data->cs, 1);
+		else
+			gpio_set_value(pcap_data->cs, 0);
+	}
+
+	spin_unlock_irqrestore(&ezx_ssp_lock, flag);
+
+	return ret;
+}
+
+void ezx_pcap_write(u_int8_t reg_num, u_int32_t value)
+{
+	value &= PCAP_REGISTER_VALUE_MASK;
+	value |= PCAP_REGISTER_WRITE_OP_BIT
+		| (reg_num<<PCAP_REGISTER_ADDRESS_SHIFT);
+
+	ezx_ssp_pcap_putget(value);
+}
+EXPORT_SYMBOL_GPL(ezx_pcap_write);
+
+void ezx_pcap_read(u_int8_t reg_num, u_int32_t *value)
+{
+	u_int32_t frame = PCAP_REGISTER_READ_OP_BIT
+		| (reg_num<<PCAP_REGISTER_ADDRESS_SHIFT);
+
+	*value = ezx_ssp_pcap_putget(frame);
+}
+EXPORT_SYMBOL_GPL(ezx_pcap_read);
+
+void ezx_pcap_set_sw(u_int8_t sw, u_int8_t what, u_int8_t val)
+{
+	u_int32_t tmp;
+
+	ezx_pcap_read(PCAP_REG_LOWPWR, &tmp);
+	tmp &= ~(0xf << (sw + what));
+	tmp |= ((val & 0xf) << (sw + what));
+	ezx_pcap_write(PCAP_REG_LOWPWR, tmp);
+}
+EXPORT_SYMBOL_GPL(ezx_pcap_set_sw);
+
+static u_int8_t vaux_table[][8] = {
+	/*		EN	INDEX	MASK	STBY	LOWPWR	*/
+	[VAUX1]	= {	1,	2,	0x3,	22,	23,	},
+	[VAUX2]	= {	4,	5,	0x3,	0,	1,	},
+	[VAUX3]	= {	7,	8,	0xf,	2,	3,	},
+	[VAUX4]	= {	12,	13,	0x3,	4,	5,	},
+	[VSIM]	= {	17,	18,	0x1,	0xff,	6,	},
+	[VSIM2]	= {	16,	0xff,	0x0,	0xff,	7,	},
+	[VVIB]	= {	19,	20,	0x3,	0xff,	0xff,	},
+	[VC]	= {	0xff,	0xff,	0x0,	24,	0xff,	},
+};
+
+int ezx_pcap_set_vaux(u_int8_t vaux, u_int8_t what, u_int8_t val)
+{
+	u_int8_t reg, shift, mask;
+	u_int32_t tmp;
+
+	switch (what) {
+	case VAUX_EN:
+		reg = PCAP_REG_AUXVREG;
+		shift = vaux_table[vaux][VAUX_EN];
+		mask = 0x1;
+		break;
+	case VAUX_VAL:
+		reg = PCAP_REG_AUXVREG;
+		shift = vaux_table[vaux][VAUX_VAL];
+		mask = vaux_table[vaux][VAUX_MASK];
+		break;
+	case VAUX_STBY:
+		if (vaux == VAUX1) /* exception */
+			reg = PCAP_REG_AUXVREG;
+		else
+			reg = PCAP_REG_LOWPWR;
+		shift = vaux_table[vaux][VAUX_STBY];
+		mask = 0x1;
+		break;
+	case VAUX_LOWPWR:
+		if (vaux == VAUX1)
+			reg = PCAP_REG_AUXVREG;
+		else
+			reg = PCAP_REG_LOWPWR;
+		shift = vaux_table[vaux][VAUX_LOWPWR];
+		mask = 0x1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* invalid setting */
+	if (shift == 0xff || val > mask)
+		return -EINVAL;
+
+	ezx_pcap_read(reg, &tmp);
+	tmp &= ~(mask << shift);
+	tmp |= ((val & mask) << shift);
+	ezx_pcap_write(reg, tmp);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ezx_pcap_set_vaux);
+
+/* IRQ Handling */
+
+/* Array indexed by BIT POSITION of PCAP register, returns IRQ number */
+static unsigned int pcap2irq[] = {
+	[0]     = EZX_IRQ_ADCDONE,
+	[1]     = EZX_IRQ_TS,
+	[2]     = EZX_IRQ_1HZ, /* 1HZ */
+	[3]     = EZX_IRQ_WH, /* WH */
+	[4]     = EZX_IRQ_WL, /* WL */
+	[5]     = EZX_IRQ_TODA, /* TODA */
+	[6]     = EZX_IRQ_USB4V,
+	[7]     = EZX_IRQ_ONOFF, /* ONOFF */
+	[8]     = EZX_IRQ_ONOFF2, /* ONOFF2 */
+	[9]     = EZX_IRQ_USB1V,
+	[10]    = EZX_IRQ_MOBPORT, /* MOBPORT */
+	[11]    = EZX_IRQ_MIC,
+	[12]    = EZX_IRQ_HEADJACK,
+	[13]    = EZX_IRQ_ST, /* ST */
+	[14]    = EZX_IRQ_PC, /* PC */
+	[15]    = EZX_IRQ_WARM, /* WARM */
+	[16]    = EZX_IRQ_EOL, /* EOL */
+	[17]    = EZX_IRQ_CLK, /* CLK */
+	[18]    = EZX_IRQ_SYSRST, /* SYSRST */
+	[19]    = 0,
+	[20]    = EZX_IRQ_ADCDONE2,
+	[21]    = EZX_IRQ_SOFTRESET, /* SOFTRESET */
+	[22]    = EZX_IRQ_MNEXB, /* MNEXB */
+};
+
+/* Array indexed by IRQ NUMBER, returns PCAP absolute value */
+static unsigned int irq2pcap[] = {
+	[EZX_IRQ_MNEXB]		= PCAP_IRQ_MNEXB,
+	[EZX_IRQ_SOFTRESET]	= PCAP_IRQ_SOFTRESET,
+	[EZX_IRQ_SYSRST]	= PCAP_IRQ_SYSRST,
+	[EZX_IRQ_CLK]		= PCAP_IRQ_CLK,
+	[EZX_IRQ_EOL]		= PCAP_IRQ_EOL,
+	[EZX_IRQ_WARM]		= PCAP_IRQ_WARM,
+	[EZX_IRQ_PC]		= PCAP_IRQ_PC,
+	[EZX_IRQ_ST]		= PCAP_IRQ_ST,
+	[EZX_IRQ_MOBPORT]	= PCAP_IRQ_MOBPORT,
+	[EZX_IRQ_ONOFF2]	= PCAP_IRQ_ONOFF2,
+	[EZX_IRQ_ONOFF]		= PCAP_IRQ_ONOFF,
+	[EZX_IRQ_TODA]		= PCAP_IRQ_TODA,
+	[EZX_IRQ_WL]		= PCAP_IRQ_WL,
+	[EZX_IRQ_WH]		= PCAP_IRQ_WH,
+	[EZX_IRQ_1HZ]		= PCAP_IRQ_1HZ,
+	[EZX_IRQ_USB4V]		= PCAP_IRQ_USB4V,
+	[EZX_IRQ_USB1V]		= PCAP_IRQ_USB1V,
+	[EZX_IRQ_HEADJACK]	= PCAP_IRQ_A1,
+	[EZX_IRQ_MIC]		= PCAP_IRQ_MB2,
+	[EZX_IRQ_TS]		= PCAP_IRQ_TS,
+	[EZX_IRQ_ADCDONE]	= PCAP_IRQ_ADCDONE,
+	[EZX_IRQ_ADCDONE2]	= PCAP_IRQ_ADCDONE2,
+};
+
+static void pcap_ack_irq(unsigned int irq)
+{
+	ezx_pcap_write(PCAP_REG_ISR, irq2pcap[irq]);
+}
+
+static void pcap_mask_irq(unsigned int irq)
+{
+	u_int32_t reg;
+	unsigned long flag;
+
+	spin_lock_irqsave(&ezx_ssp_lock, flag);
+	ezx_pcap_read(PCAP_REG_MSR, &reg);
+	reg |= irq2pcap[irq];
+	ezx_pcap_write(PCAP_REG_MSR, reg);
+	spin_unlock_irqrestore(&ezx_ssp_lock, flag);
+}
+
+static void pcap_unmask_irq(unsigned int irq)
+{
+	u_int32_t tmp;
+	unsigned long flag;
+
+	spin_lock_irqsave(&ezx_ssp_lock, flag);
+	ezx_pcap_read(PCAP_REG_MSR, &tmp);
+	tmp &= ~irq2pcap[irq];
+	ezx_pcap_write(PCAP_REG_MSR, tmp);
+	spin_unlock_irqrestore(&ezx_ssp_lock, flag);
+}
+
+static struct irq_chip pcap_chip = {
+	.name	= "ezx-pcap",
+	.ack    = pcap_ack_irq,
+	.mask   = pcap_mask_irq,
+	.unmask = pcap_unmask_irq,
+};
+
+/* handler for interrupt received from PCAP via GPIO */
+static void pcap_irq_demux_handler(unsigned int irq, struct irq_desc *desc)
+{
+	int i;
+	u_int32_t isr;
+
+	desc->chip->ack(irq);
+	ezx_pcap_read(PCAP_REG_ISR, &isr);
+	for (i = ARRAY_SIZE(pcap2irq)-1; i >= 0; i--) {
+		unsigned int pirq = pcap2irq[i];
+		if (!(isr & irq2pcap[pirq]))
+			continue;
+		desc = &irq_desc[pirq];
+		desc_handle_irq(pirq, desc);
+	}
+}
+
+static int ezx_pcap_remove(struct platform_device *pdev)
+{
+	int irq;
+
+	set_irq_chained_handler(pcap_irq, NULL);
+	for (irq = EZX_IRQ(0); irq <= EZX_IRQ(21); irq++) {
+		set_irq_chip(irq, NULL);
+		set_irq_handler(irq, NULL);
+		set_irq_flags(irq, 0);
+	}
+	ssp_exit(&ezx_ssp_dev);
+
+	return 0;
+}
+
+static int __init ezx_pcap_probe(struct platform_device *pdev)
+{
+	unsigned int ret, irq;
+
+	pcap_data = pdev->dev.platform_data;
+	if (pcap_data->cs >= 0) {
+		if (pcap_data->config & CS_INVERTED)
+			gpio_direction_output(pcap_data->cs, 1);
+		else
+			gpio_direction_output(pcap_data->cs, 0);
+	}
+	pcap_irq = platform_get_irq(pdev, 0);
+	if (pcap_irq < 0) {
+		printk(KERN_ERR "Unable to get IRQ for pcap!\n");
+		return pcap_irq;
+	}
+
+	ret = ssp_init(&ezx_ssp_dev, pcap_data->port, 0);
+	if (ret) {
+		printk(KERN_ERR "Unable to register SSP handler!\n");
+		return ret;
+	}
+
+	ssp_disable(&ezx_ssp_dev);
+	ssp_config(&ezx_ssp_dev,
+		(SSCR0_Motorola | SSCR0_DataSize(16) | SSCR0_EDSS),
+		(SSCR1_TxTresh(1) | SSCR1_RxTresh(1)),
+		0, SSCR0_SerClkDiv(pcap_data->clk));
+	ssp_enable(&ezx_ssp_dev);
+
+	if (pcap_data->init)
+		pcap_data->init();
+
+	/* set up interrupt demultiplexing code for PCAP2 irqs */
+	for (irq = EZX_IRQ(0); irq <= EZX_IRQ(21); irq++) {
+		set_irq_chip(irq, &pcap_chip);
+		set_irq_handler(irq, handle_level_irq);
+		set_irq_flags(irq, IRQF_VALID);
+	}
+	set_irq_type(pcap_irq, IRQ_TYPE_EDGE_RISING);
+	set_irq_chained_handler(pcap_irq, pcap_irq_demux_handler);
+	set_irq_wake(pcap_irq, 1);
+
+	/* mask/ack all PCAP interrupts */
+	ezx_pcap_write(PCAP_REG_MSR, PCAP_MASK_ALL_INTERRUPT);
+	ezx_pcap_write(PCAP_REG_ISR, PCAP_CLEAR_INTERRUPT_REGISTER);
+
+
+	printk(KERN_INFO "ezx-pcap: ssp driver registered\n");
+	return ret;
+}
+
+#ifdef CONFIG_PM
+static int ezx_pcap_suspend(struct platform_device *dev, pm_message_t state)
+{
+	ssp_flush(&ezx_ssp_dev);
+	ssp_save_state(&ezx_ssp_dev, &ezx_ssp_state);
+	ssp_disable(&ezx_ssp_dev);
+	return 0;
+}
+
+static int ezx_pcap_resume(struct platform_device *dev)
+{
+	ssp_restore_state(&ezx_ssp_dev, &ezx_ssp_state);
+	ssp_enable(&ezx_ssp_dev);
+
+	return 0;
+}
+#endif
+
+static struct platform_driver ezxpcap_driver = {
+	.probe		= ezx_pcap_probe,
+	.remove		= ezx_pcap_remove,
+#ifdef CONFIG_PM
+	.suspend	= ezx_pcap_suspend,
+	.resume		= ezx_pcap_resume,
+#endif
+	.driver		= {
+		.name   = "ezx-pcap",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init ezx_pcap_init(void)
+{
+	return platform_driver_register(&ezxpcap_driver);
+}
+
+subsys_initcall(ezx_pcap_init);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Harald Welte");
+MODULE_DESCRIPTION("Motorola PCAP2 ASIC Driver");
Index: linux-2.6-arm/include/linux/mfd/ezx-pcap.h
===================================================================
--- /dev/null
+++ linux-2.6-arm/include/linux/mfd/ezx-pcap.h
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2007 Daniel Ribeiro <drwyrm@gmail.com>
+ *
+ * For further information, please see http://wiki.openezx.org/PCAP2
+ */
+
+#ifndef EZX_PCAP_H
+#define EZX_PCAP_H
+
+struct pcap_platform_data {
+	int port;		/* SSP port */
+	int cs;			/* CS gpio */
+	int config;
+	int clk;
+	int (*init)(void);	/* board specific driver init */
+};
+
+#define CS_INVERTED	1
+
+#define PCAP_REGISTER_WRITE_OP_BIT	0x80000000
+#define PCAP_REGISTER_READ_OP_BIT	0x00000000
+
+#define PCAP_REGISTER_VALUE_MASK	0x01ffffff
+#define PCAP_REGISTER_ADDRESS_MASK	0x7c000000
+#define PCAP_REGISTER_ADDRESS_SHIFT	26
+#define PCAP_REGISTER_NUMBER		32
+#define PCAP_CLEAR_INTERRUPT_REGISTER	0x01ffffff
+#define PCAP_MASK_ALL_INTERRUPT		0x01ffffff
+
+#define pbit(reg, bit)	((reg << PCAP_REGISTER_ADDRESS_SHIFT) | bit)
+
+/* registers acessible by both pcap ports */
+#define PCAP_REG_ISR		0x0	/* Interrupt Status */
+#define PCAP_REG_MSR		0x1	/* Interrupt Mask */
+#define PCAP_REG_PSTAT		0x2	/* Processor Status */
+#define PCAP_REG_VREG2		0x6	/* Regulator Bank 2 Control */
+#define PCAP_REG_AUXVREG	0x7	/* Auxiliary Regulator Control */
+#define PCAP_REG_BATT		0x8	/* Battery Control */
+#define PCAP_REG_ADC		0x9	/* AD Control */
+#define PCAP_REG_ADR		0xa	/* AD Result */
+#define PCAP_REG_CODEC		0xb	/* Audio Codec Control */
+#define PCAP_REG_RX_AMPS	0xc	/* RX Audio Amplifiers Control */
+#define PCAP_REG_ST_DAC		0xd	/* Stereo DAC Control */
+#define PCAP_REG_BUSCTRL	0x14	/* Connectivity Control */
+#define PCAP_REG_PERIPH		0x15	/* Peripheral Control */
+#define PCAP_REG_LOWPWR		0x18	/* Regulator Low Power Control */
+#define PCAP_REG_TX_AMPS	0x1a	/* TX Audio Amplifiers Control */
+#define PCAP_REG_GP		0x1b	/* General Purpose */
+
+/* registers acessible by pcap port 1 only (a1200, e2 & e6) */
+#define PCAP_REG_INT_SEL	0x3	/* Interrupt Select */
+#define PCAP_REG_SWCTRL		0x4	/* Switching Regulator Control */
+#define PCAP_REG_VREG1		0x5	/* Regulator Bank 1 Control */
+#define PCAP_REG_RTC_TOD	0xe	/* RTC Time of Day */
+#define PCAP_REG_RTC_TODA	0xf	/* RTC Time of Day Alarm */
+#define PCAP_REG_RTC_DAY	0x10	/* RTC Day */
+#define PCAP_REG_RTC_DAYA	0x11	/* RTC Day Alarm */
+#define PCAP_REG_MTRTMR		0x12	/* AD Monitor Timer */
+#define PCAP_REG_PWR		0x13	/* Power Control */
+#define PCAP_REG_AUXVREG_MASK	0x16	/* Auxiliary Regulator Mask */
+#define PCAP_REG_VENDOR_REV	0x17
+#define PCAP_REG_PERIPH_MASK	0x19	/* Peripheral Mask */
+
+/* interrupts - registers 0x0, 0x1, 0x2, 0x3 */
+#define PCAP_IRQ_ADCDONE	(1 << 0)	/* AD Conversion Done Port 1 */
+#define PCAP_IRQ_TS		(1 << 1)	/* Touch Screen */
+#define PCAP_IRQ_1HZ		(1 << 2)	/* 1HZ Timer */
+#define PCAP_IRQ_WH		(1 << 3)	/* "...high"??? */
+#define PCAP_IRQ_WL		(1 << 4)	/* "...low"??? */
+#define PCAP_IRQ_TODA		(1 << 5)	/* RTC Time Of Day?
+						     (see "RTC_TODA") */
+#define PCAP_IRQ_USB4V		(1 << 6)	/* USB OTG */
+#define PCAP_IRQ_ONOFF		(1 << 7)	/* in blob: "ONOFFSNS" */
+#define PCAP_IRQ_ONOFF2		(1 << 8)	/* in blob: "ONOFFSNS2" */
+#define PCAP_IRQ_USB1V		(1 << 9)	/* USB below 1volt???
+						     in blob: "USBDET_1V" */
+#define PCAP_IRQ_MOBPORT	(1 << 10)	/* GSM-related?? ("mobport",
+				see 958_MotDoc.pdf); in blob: "MOBSENSB" */
+#define PCAP_IRQ_MB2		(1 << 11)	/* Mic; in blob: "MB2SNS" */
+#define PCAP_IRQ_A1		(1 << 12)	/* Audio jack;
+						     in blob: "A1SNS" */
+#define PCAP_IRQ_ST		(1 << 13)	/* called "MSTB" in blob */
+#define PCAP_IRQ_PC		(1 << 14)
+#define PCAP_IRQ_WARM		(1 << 15)
+#define PCAP_IRQ_EOL		(1 << 16)	/* battery End Of Life???
+					(see below); in blob: "EOL_STAT" */
+#define PCAP_IRQ_CLK		(1 << 17)	/* called "CLK_STAT" in blob */
+#define PCAP_IRQ_SYSRST		(1 << 18)
+#define PCAP_IRQ_ADCDONE2	(1 << 20)	/* AD Conversion Done Port 2 */
+#define PCAP_IRQ_SOFTRESET	(1 << 21)
+#define PCAP_IRQ_MNEXB		(1 << 22)
+
+/* register VREG2 (0x6) */
+#define PCAP_VREG2_V1_STBY	(1 << 0)
+#define PCAP_VREG2_V2_STBY	(1 << 1)
+/* V3, SRAM: */
+#define PCAP_VREG2_V3_STBY	(1 << 2)
+#define PCAP_VREG2_V4_STBY	(1 << 3)
+#define PCAP_VREG2_V5_STBY	(1 << 4)
+/* V6, E680 I2C camera?: */
+#define PCAP_VREG2_V6_STBY	(1 << 5)
+#define PCAP_VREG2_V7_STBY	(1 << 6)
+/* V8, PLL: */
+#define PCAP_VREG2_V8_STBY	(1 << 7)
+#define PCAP_VREG2_V9_STBY	(1 << 8)
+#define PCAP_VREG2_V10_STBY	(1 << 9)
+#define PCAP_VREG2_V1_LOWPWR	(1 << 10)
+#define PCAP_VREG2_V2_LOWPWR	(1 << 11)
+/* V3, SRAM: */
+#define PCAP_VREG2_V3_LOWPWR	(1 << 12)
+#define PCAP_VREG2_V4_LOWPWR	(1 << 13)
+#define PCAP_VREG2_V5_LOWPWR	(1 << 14)
+/* V6, E680 I2C camera?: */
+#define PCAP_VREG2_V6_LOWPWR	(1 << 15)
+#define PCAP_VREG2_V7_LOWPWR	(1 << 16)
+/* V8, PLL: */
+#define PCAP_VREG2_V8_LOWPWR	(1 << 17)
+#define PCAP_VREG2_V9_LOWPWR	(1 << 18)
+#define PCAP_VREG2_V10_LOWPWR	(1 << 19)
+
+/* register AUXVREG (0x7) */
+#define VAUX1		0
+#define VAUX2		1
+#define VAUX3		2
+#define VAUX4		3
+#define VSIM		4
+#define VSIM2		5
+#define VVIB		6
+#define VC		7
+
+#define VAUX_EN		0
+#define VAUX_VAL	1
+#define VAUX_MASK	2
+#define VAUX_STBY	3
+#define VAUX_LOWPWR	4
+
+#define PCAP_BATT_DAC_MASK		0x000000ff
+#define PCAP_BATT_DAC_SHIFT		0
+#define PCAP_BATT_B_FDBK		(1 << 8)
+#define PCAP_BATT_EXT_ISENSE		(1 << 9)
+#define PCAP_BATT_V_COIN_MASK		0x00003c00
+#define PCAP_BATT_V_COIN_SHIFT		10
+#define PCAP_BATT_I_COIN		(1 << 14)
+#define PCAP_BATT_COIN_CH_EN		(1 << 15)
+#define PCAP_BATT_EOL_SEL_MASK		0x000e0000
+#define PCAP_BATT_EOL_SEL_SHIFT		17
+#define PCAP_BATT_EOL_CMP_EN		(1 << 20)
+#define PCAP_BATT_BATT_DET_EN		(1 << 21)
+#define PCAP_BATT_THERMBIAS_CTRL	(1 << 22)
+
+#define PCAP_ADC_ADEN			(1 << 0)
+#define PCAP_ADC_RAND			(1 << 1)
+#define PCAP_ADC_AD_SEL1		(1 << 2)
+#define PCAP_ADC_AD_SEL2		(1 << 3)
+#define PCAP_ADC_ADA1_MASK		0x00000070
+#define PCAP_ADC_ADA1_SHIFT		4
+#define PCAP_ADC_ADA2_MASK		0x00000380
+#define PCAP_ADC_ADA2_SHIFT		7
+#define PCAP_ADC_ATO_MASK		0x00003c00
+#define PCAP_ADC_ATO_SHIFT		10
+#define PCAP_ADC_ATOX			(1 << 14)
+#define PCAP_ADC_MTR1			(1 << 15)
+#define PCAP_ADC_MTR2			(1 << 16)
+#define PCAP_ADC_TS_M_MASK		0x000e0000
+#define PCAP_ADC_TS_M_SHIFT		17
+#define PCAP_ADC_TS_REF_LOWPWR		(1 << 20)
+#define PCAP_ADC_TS_REFENB		(1 << 21)
+#define PCAP_ADC_BATT_I_POLARITY	(1 << 22)
+#define PCAP_ADC_BATT_I_ADC		(1 << 23)
+
+#define PCAP_ADR_ADD1_MASK		0x000003ff
+#define PCAP_ADR_ADD1_SHIFT		0
+#define PCAP_ADR_ADD2_MASK		0x000ffc00
+#define PCAP_ADR_ADD2_SHIFT		10
+#define PCAP_ADR_ADINC1			(1 << 20)
+#define PCAP_ADR_ADINC2			(1 << 21)
+#define PCAP_ADR_ASC			(1 << 22)
+#define PCAP_ADR_ONESHOT		(1 << 23)
+
+#define PCAP_BUSCTRL_FSENB		(1 << 0)
+#define PCAP_BUSCTRL_USB_SUSPEND	(1 << 1)
+#define PCAP_BUSCTRL_USB_PU		(1 << 2)
+#define PCAP_BUSCTRL_USB_PD		(1 << 3)
+#define PCAP_BUSCTRL_VUSB_EN		(1 << 4)
+#define PCAP_BUSCTRL_USB_PS		(1 << 5)
+#define PCAP_BUSCTRL_VUSB_MSTR_EN	(1 << 6)
+#define PCAP_BUSCTRL_VBUS_PD_ENB	(1 << 7)
+#define PCAP_BUSCTRL_CURRLIM		(1 << 8)
+#define PCAP_BUSCTRL_RS232ENB		(1 << 9)
+#define PCAP_BUSCTRL_RS232_DIR		(1 << 10)
+#define PCAP_BUSCTRL_SE0_CONN		(1 << 11)
+#define PCAP_BUSCTRL_USB_PDM		(1 << 12)
+#define PCAP_BUSCTRL_BUS_PRI_ADJ	(1 << 24)
+
+#define PCAP_BIT_PERIPH_BL_CTRL0	0x54000001
+#define PCAP_BIT_PERIPH_BL_CTRL1	0x54000002
+#define PCAP_BIT_PERIPH_BL_CTRL2	0x54000004
+#define PCAP_BIT_PERIPH_BL_CTRL3	0x54000008
+#define PCAP_BIT_PERIPH_BL_CTRL4	0x54000010
+#define PCAP_BIT_PERIPH_LEDR_EN		0x54000020
+#define PCAP_BIT_PERIPH_LEDG_EN		0x54000040
+#define PCAP_BIT_PERIPH_LEDR_CTRL0	0x54000080
+#define PCAP_BIT_PERIPH_LEDR_CTRL1	0x54000100
+#define PCAP_BIT_PERIPH_LEDR_CTRL2	0x54000200
+#define PCAP_BIT_PERIPH_LEDR_CTRL3	0x54000400
+#define PCAP_BIT_PERIPH_LEDG_CTRL0	0x54000800
+#define PCAP_BIT_PERIPH_LEDG_CTRL1	0x54001000
+#define PCAP_BIT_PERIPH_LEDG_CTRL2	0x54002000
+#define PCAP_BIT_PERIPH_LEDG_CTRL3	0x54004000
+#define PCAP_BIT_PERIPH_LEDR_I0		0x54008000
+#define PCAP_BIT_PERIPH_LEDR_I1		0x54010000
+#define PCAP_BIT_PERIPH_LEDG_I0		0x54020000
+#define PCAP_BIT_PERIPH_LEDG_I1		0x54040000
+#define PCAP_BIT_PERIPH_SKIP		0x54080000
+#define PCAP_BIT_PERIPH_BL2_CTRL0	0x54100000
+#define PCAP_BIT_PERIPH_BL2_CTRL1	0x54200000
+#define PCAP_BIT_PERIPH_BL2_CTRL2	0x54400000
+#define PCAP_BIT_PERIPH_BL2_CTRL3	0x54800000
+#define PCAP_BIT_PERIPH_BL2_CTRL4	0x55000000
+
+/* LOWPWR */
+#define SW1		8
+#define SW2		16
+
+#define SW_MODE		0
+#define SW_VOLTAGE	4
+
+#define SW_VOLTAGE_900	0x0
+#define SW_VOLTAGE_950	0x1
+#define SW_VOLTAGE_1000	0x2
+#define SW_VOLTAGE_1050	0x3
+#define SW_VOLTAGE_1100	0x4
+#define SW_VOLTAGE_1150	0x5
+#define SW_VOLTAGE_1200	0x6
+#define SW_VOLTAGE_1250	0x7
+#define SW_VOLTAGE_1300	0x8
+#define SW_VOLTAGE_1350	0x9
+#define SW_VOLTAGE_1400	0xa
+#define SW_VOLTAGE_1500	0xb
+#define SW_VOLTAGE_1600	0xc
+#define SW_VOLTAGE_1875	0xd
+#define SW_VOLTAGE_2250	0xe
+#define SW_VOLTAGE_4400	0xf
+
+void ezx_pcap_write(u_int8_t, u_int32_t);
+void ezx_pcap_read(u_int8_t, u_int32_t *);
+void ezx_pcap_set_sw(u_int8_t, u_int8_t, u_int8_t);
+int ezx_pcap_set_vaux(u_int8_t, u_int8_t, u_int8_t);
+#endif
Index: linux-2.6-arm/drivers/mfd/Kconfig
===================================================================
--- linux-2.6-arm.orig/drivers/mfd/Kconfig
+++ linux-2.6-arm/drivers/mfd/Kconfig
@@ -49,6 +49,13 @@
 	help
 	  Support for Toshiba Mobile IO Controller TC6393XB
 
+config EZX_PCAP
+	bool "PCAP Support"
+	depends on PXA_SSP && PXA_EZX
+	help
+	  This enables the PCAP ASIC present on EZX Phones. This is
+	  needed for MMC, TouchScreen, Sound, USB, etc..
+
 endmenu
 
 menu "Multimedia Capabilities Port drivers"
Index: linux-2.6-arm/drivers/mfd/Makefile
===================================================================
--- linux-2.6-arm.orig/drivers/mfd/Makefile
+++ linux-2.6-arm/drivers/mfd/Makefile
@@ -12,6 +12,8 @@
 
 obj-$(CONFIG_MFD_CORE)		+= mfd-core.o
 
+obj-$(CONFIG_EZX_PCAP)		+= ezx-pcap.o
+
 obj-$(CONFIG_MCP)		+= mcp-core.o
 obj-$(CONFIG_MCP_SA11X0)	+= mcp-sa11x0.o
 obj-$(CONFIG_MCP_UCB1200)	+= ucb1x00-core.o
Index: linux-2.6-arm/arch/arm/mach-pxa/Kconfig
===================================================================
--- linux-2.6-arm.orig/arch/arm/mach-pxa/Kconfig
+++ linux-2.6-arm/arch/arm/mach-pxa/Kconfig
@@ -251,6 +251,8 @@
 	select PXA27x
 	select IWMMXT
 	select HAVE_PWM
+	select PXA_SSP
+	select EZX_PCAP
 
 config MACH_EZX_A780
 	bool "Motorola EZX A780"
Index: linux-2.6-arm/arch/arm/mach-pxa/ezx.c
===================================================================
--- linux-2.6-arm.orig/arch/arm/mach-pxa/ezx.c
+++ linux-2.6-arm/arch/arm/mach-pxa/ezx.c
@@ -15,7 +15,9 @@
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/delay.h>
+#include <linux/gpio.h>
 #include <linux/pwm_backlight.h>
+#include <linux/mfd/ezx-pcap.h>
 
 #include <asm/setup.h>
 #include <asm/arch/pxafb.h>
@@ -87,7 +89,54 @@
 	.lcd_conn	= LCD_COLOR_TFT_18BPP,
 };
 
+/* PCAP */
+static int ezx_pcap_init(void)
+{
+	/* disable all voltage regulators */
+	ezx_pcap_write(PCAP_REG_AUXVREG, 0);
+
+	/* set SW1 sleep to keep SW1 1.3v in sync mode */
+	/*  SW1 active in sync mode */
+	ezx_pcap_set_sw(SW1, SW_MODE, 0x1);
+
+	/*  set core voltage */
+	ezx_pcap_set_sw(SW1, SW_VOLTAGE, SW_VOLTAGE_1250);
+
+	/* redirect all interrupts to AP */
+	if (!(machine_is_ezx_a780() || machine_is_ezx_e680()))
+		ezx_pcap_write(PCAP_REG_INT_SEL, 0);
+
+	return 0;
+}
+
+static struct pcap_platform_data ezx_pcap_platform_data = {
+	.port   = 1,
+	.cs     = 24,
+	.clk    = 1,
+	.config	= 0,
+	.init   = ezx_pcap_init,
+};
+
+static struct resource ezx_pcap_resources[] = {
+	[0] = {
+		.start      = IRQ_GPIO1,
+		.end        = IRQ_GPIO1,
+		.flags      = IORESOURCE_IRQ,
+	},
+};
+
+struct platform_device ezx_pcap_device = {
+	.name       = "ezx-pcap",
+	.id     = -1,
+	.num_resources  = ARRAY_SIZE(ezx_pcap_resources),
+	.resource   = ezx_pcap_resources,
+	.dev        = {
+		.platform_data = &ezx_pcap_platform_data,
+	},
+};
+
 static struct platform_device *devices[] __initdata = {
+	&ezx_pcap_device,
 	&ezx_backlight_device,
 };
 
@@ -105,6 +154,11 @@
 	GPIO46_STUART_RXD,
 	GPIO47_STUART_TXD,
 
+	/* PCAP SSP */
+	GPIO29_SSP1_SCLK,
+	GPIO25_SSP1_TXD,
+	GPIO26_SSP1_RXD,
+
 	/* For A780 support (connected with Neptune GSM chip) */
 	GPIO30_USB_P3_2,	/* ICL_TXENB */
 	GPIO31_USB_P3_6,	/* ICL_VPOUT */
@@ -118,11 +172,12 @@
 {
 	pxa2xx_mfp_config(ARRAY_AND_SIZE(ezx_pin_config));
 	pxa_set_i2c_info(NULL);
-	if (machine_is_ezx_a780() || machine_is_ezx_e680())
+	if (machine_is_ezx_a780() || machine_is_ezx_e680()) {
 		set_pxa_fb_info(&ezx_fb_info_1);
-	else
+		ezx_pcap_platform_data.config |= CS_INVERTED;
+	} else {
 		set_pxa_fb_info(&ezx_fb_info_2);
-
+	}
 	platform_add_devices(devices, ARRAY_SIZE(devices));
 }
 
Index: linux-2.6-arm/include/asm-arm/arch-pxa/irqs.h
===================================================================
--- linux-2.6-arm.orig/include/asm-arm/arch-pxa/irqs.h
+++ linux-2.6-arm/include/asm-arm/arch-pxa/irqs.h
@@ -186,6 +186,39 @@
 #endif
 #endif /* CONFIG_MACH_PCM027 */
 
+#define EZX_IRQ(x)		PXA_BOARD_IRQ(x)
+
+#ifdef CONFIG_PXA_EZX
+#define EZX_IRQ_USB4V		EZX_IRQ(0) /* EMU */
+#define EZX_IRQ_USB1V		EZX_IRQ(1) /* EMU */
+#define EZX_IRQ_HEADJACK	EZX_IRQ(2) /* Audio connector */
+#define EZX_IRQ_MIC		EZX_IRQ(3) /* Audio connector */
+#define EZX_IRQ_ADCDONE		EZX_IRQ(4)
+#define EZX_IRQ_TS		EZX_IRQ(5) /* TS touch */
+#define EZX_IRQ_ADCDONE2	EZX_IRQ(6) /* TS x/y ADC ready */
+#define EZX_IRQ_WH		EZX_IRQ(7)
+#define EZX_IRQ_WL		EZX_IRQ(8)
+#define EZX_IRQ_ONOFF		EZX_IRQ(9)
+#define EZX_IRQ_ONOFF2		EZX_IRQ(10)
+#define EZX_IRQ_MOBPORT		EZX_IRQ(11)
+#define EZX_IRQ_TODA		EZX_IRQ(12)
+#define EZX_IRQ_1HZ		EZX_IRQ(13)
+#define EZX_IRQ_MNEXB		EZX_IRQ(14)
+#define EZX_IRQ_ST		EZX_IRQ(15)
+#define EZX_IRQ_PC		EZX_IRQ(16)
+#define EZX_IRQ_SYSRST		EZX_IRQ(17)
+#define EZX_IRQ_SOFTRESET	EZX_IRQ(18)
+#define EZX_IRQ_EOL		EZX_IRQ(19)
+#define EZX_IRQ_CLK		EZX_IRQ(20)
+#define EZX_IRQ_WARM		EZX_IRQ(21)
+#define EZX_LAST_IRQ		EZX_IRQ_WARM
+
+#if PXA_BOARD_IRQ_END < EZX_LAST_IRQ
+#undef  PXA_BOARD_IRQ_END
+#define PXA_BOARD_IRQ_END	EZX_LAST_IRQ
+#endif
+#endif /* CONFIG_EZX */
+
 /*
  * Extended IRQs for companion chips start from the last board-specific IRQ.
  * NOTE: unlike board specific IRQs, the number space for these IRQs cannot

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Patch 06/10] mfd: PCAP driver for the Motorola EZX GSM mobile phones
  2008-07-08 20:32           ` Stefan Schmidt
@ 2008-07-08 21:19             ` pHilipp Zabel
  2008-07-09  2:34             ` Eric Miao
                               ` (2 subsequent siblings)
  3 siblings, 0 replies; 12+ messages in thread
From: pHilipp Zabel @ 2008-07-08 21:19 UTC (permalink / raw)
  To: Stefan Schmidt
  Cc: Daniel Ribeiro, Eric Miao, linux-arm-kernel, sameo, linux-kernel

On Tue, Jul 8, 2008 at 10:32 PM, Stefan Schmidt
<stefan@datenfreihafen.org> wrote:
> Hello.
>
> On Tue, 2008-07-08 at 11:01, Daniel Ribeiro wrote:
>> Eric Miao escreveu:
>> > The irq2pcap[] array looks horrible to me. It's actually a sparse array.
>> > Isn't there a nice 1:1 mapping using a formular??
>> >
>> > Besides, the IRQ numbering scheme has now changed to a more generic way,
>> > I suggest to pull from Russell's latest git tree and rebase the IRQ
>> > part.
>>
>>       Will do as you suggested and get rid of the arrays.
>>
>> > The following block of code:
>> >
>> > +   if (pcap_data->cs >= 0) {
>> > +           if (machine_is_ezx_a780() || machine_is_ezx_e680())
>> > +                   gpio_direction_output(pcap_data->cs, 1);
>> > +           else
>> > +                   gpio_direction_output(pcap_data->cs, 0);
>> > +   }
>> >
>> > has 3 occurrences in the driver (2 in ezx_ssp_pcap_putget, 1 in ezx_pcap_probe)
>> > which is good reason to fold this into the platform data.
>> >
>> > Well, if the above is done in platform data, I guess you won't mind another bit
>> > flag (e.g. PCAP_REDIRECT_IRQ or something alike) added in platform data, either
>>
>>       Moved to platform data. What should PCAP_REDIRECT_IRQ flag do?
>
> Daniel fixed all of Philipps comments besides the voltage framework. From Eric
> comments we updated to the new IRQ part and moved to platform data.
>
> Open items we are still working on:
> o Voltage framework. Is this needed for the first merge

No way, it's not even in mainline yet.
I just wanted to point out that this might be interesting in the future.

> or can we switch to it with a later patch?
> o Get rid of the arrays
> o Eric, what do you like the PCAP_REDIRECT_IRQ flag to do?
>
> Besides the left items we updated and tested the patch below:
>
>
> Subject: [@num@/@total@] mfd: PCAP driver for the Motorola EZX GSM mobile phones
> To: sameo@openedhand.com
> Cc: philipp.zabel@gmail.com, linux-kernel@vger.kernel.org
>
> The PCAP Asic as present on EZX phones is a multi function device with voltage
> regulators, irq expander, touch screen controller and audio codec.
> It is connected to the processor via SPI, this driver provides read/write
> functions to its registers and a irq demultiplexer.
>
> Signed-off-by: Daniel Ribeiro <drwyrm@gmail.com>
>
> PATCH FOLLOWS
> KernelVersion: 2.6-arm-git pxa branch
>
> Index: linux-2.6-arm/drivers/mfd/ezx-pcap.c
> ===================================================================
> --- /dev/null
> +++ linux-2.6-arm/drivers/mfd/ezx-pcap.c
> @@ -0,0 +1,372 @@
> +/* Driver for Motorola PCAP2 as present in EZX phones
> + *
> + * This is both a SPI device driver for PCAP itself, as well as
> + * an IRQ demultiplexer for handling PCAP generated events such as
> + * headphone jack sense by downstream drivers.
> + *
> + * Copyright (C) 2006 Harald Welte <laforge@openezx.org>
> + * Copyright (C) 2007-2008 Daniel Ribeiro <drwyrm@gmail.com>
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel_stat.h>
> +#include <linux/proc_fs.h>
> +#include <linux/mfd/ezx-pcap.h>
> +#include <linux/delay.h>
> +#include <linux/gpio.h>
> +
> +#include <asm/hardware.h>
> +#include <asm/mach-types.h>
> +
> +#include <asm/arch/ssp.h>
> +#include <asm/arch/pxa-regs.h>
> +#include <asm/arch/regs-ssp.h>
> +#include <asm/arch/mfp-pxa27x.h>
> +#include <asm/arch/irqs.h>
> +#include <asm/mach/irq.h>
> +
> +static DEFINE_SPINLOCK(ezx_ssp_lock);
> +static struct ssp_dev ezx_ssp_dev;
> +static struct ssp_state ezx_ssp_state;
> +static struct pcap_platform_data *pcap_data;
> +static int pcap_irq;
> +
> +static u_int32_t ezx_ssp_pcap_putget(u_int32_t data)
> +{
> +       unsigned long flag;
> +       u_int32_t ret = 0;
> +
> +       spin_lock_irqsave(&ezx_ssp_lock, flag);
> +       if (pcap_data->cs >= 0) {
> +               if (pcap_data->config & CS_INVERTED)
> +                       gpio_set_value(pcap_data->cs, 0);
> +               else
> +                       gpio_set_value(pcap_data->cs, 1);
> +       }
> +
> +       ssp_write_word(&ezx_ssp_dev, data);
> +       ssp_read_word(&ezx_ssp_dev, &ret);
> +
> +       if (pcap_data->cs >= 0) {
> +               if (pcap_data->config & CS_INVERTED)
> +                       gpio_set_value(pcap_data->cs, 1);
> +               else
> +                       gpio_set_value(pcap_data->cs, 0);
> +       }
> +
> +       spin_unlock_irqrestore(&ezx_ssp_lock, flag);
> +
> +       return ret;
> +}
> +
> +void ezx_pcap_write(u_int8_t reg_num, u_int32_t value)
> +{
> +       value &= PCAP_REGISTER_VALUE_MASK;
> +       value |= PCAP_REGISTER_WRITE_OP_BIT
> +               | (reg_num<<PCAP_REGISTER_ADDRESS_SHIFT);
> +
> +       ezx_ssp_pcap_putget(value);
> +}
> +EXPORT_SYMBOL_GPL(ezx_pcap_write);
> +
> +void ezx_pcap_read(u_int8_t reg_num, u_int32_t *value)
> +{
> +       u_int32_t frame = PCAP_REGISTER_READ_OP_BIT
> +               | (reg_num<<PCAP_REGISTER_ADDRESS_SHIFT);
> +
> +       *value = ezx_ssp_pcap_putget(frame);
> +}
> +EXPORT_SYMBOL_GPL(ezx_pcap_read);
> +
> +void ezx_pcap_set_sw(u_int8_t sw, u_int8_t what, u_int8_t val)
> +{
> +       u_int32_t tmp;
> +
> +       ezx_pcap_read(PCAP_REG_LOWPWR, &tmp);
> +       tmp &= ~(0xf << (sw + what));
> +       tmp |= ((val & 0xf) << (sw + what));
> +       ezx_pcap_write(PCAP_REG_LOWPWR, tmp);
> +}
> +EXPORT_SYMBOL_GPL(ezx_pcap_set_sw);
> +
> +static u_int8_t vaux_table[][8] = {
> +       /*              EN      INDEX   MASK    STBY    LOWPWR  */
> +       [VAUX1] = {     1,      2,      0x3,    22,     23,     },
> +       [VAUX2] = {     4,      5,      0x3,    0,      1,      },
> +       [VAUX3] = {     7,      8,      0xf,    2,      3,      },
> +       [VAUX4] = {     12,     13,     0x3,    4,      5,      },
> +       [VSIM]  = {     17,     18,     0x1,    0xff,   6,      },
> +       [VSIM2] = {     16,     0xff,   0x0,    0xff,   7,      },
> +       [VVIB]  = {     19,     20,     0x3,    0xff,   0xff,   },
> +       [VC]    = {     0xff,   0xff,   0x0,    24,     0xff,   },
> +};
> +
> +int ezx_pcap_set_vaux(u_int8_t vaux, u_int8_t what, u_int8_t val)
> +{
> +       u_int8_t reg, shift, mask;
> +       u_int32_t tmp;
> +
> +       switch (what) {
> +       case VAUX_EN:
> +               reg = PCAP_REG_AUXVREG;
> +               shift = vaux_table[vaux][VAUX_EN];
> +               mask = 0x1;
> +               break;
> +       case VAUX_VAL:
> +               reg = PCAP_REG_AUXVREG;
> +               shift = vaux_table[vaux][VAUX_VAL];
> +               mask = vaux_table[vaux][VAUX_MASK];
> +               break;
> +       case VAUX_STBY:
> +               if (vaux == VAUX1) /* exception */
> +                       reg = PCAP_REG_AUXVREG;
> +               else
> +                       reg = PCAP_REG_LOWPWR;
> +               shift = vaux_table[vaux][VAUX_STBY];
> +               mask = 0x1;
> +               break;
> +       case VAUX_LOWPWR:
> +               if (vaux == VAUX1)
> +                       reg = PCAP_REG_AUXVREG;
> +               else
> +                       reg = PCAP_REG_LOWPWR;
> +               shift = vaux_table[vaux][VAUX_LOWPWR];
> +               mask = 0x1;
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       /* invalid setting */
> +       if (shift == 0xff || val > mask)
> +               return -EINVAL;
> +
> +       ezx_pcap_read(reg, &tmp);
> +       tmp &= ~(mask << shift);
> +       tmp |= ((val & mask) << shift);
> +       ezx_pcap_write(reg, tmp);
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(ezx_pcap_set_vaux);
> +
> +/* IRQ Handling */
> +
> +/* Array indexed by BIT POSITION of PCAP register, returns IRQ number */
> +static unsigned int pcap2irq[] = {
> +       [0]     = EZX_IRQ_ADCDONE,
> +       [1]     = EZX_IRQ_TS,
> +       [2]     = EZX_IRQ_1HZ, /* 1HZ */
> +       [3]     = EZX_IRQ_WH, /* WH */
> +       [4]     = EZX_IRQ_WL, /* WL */
> +       [5]     = EZX_IRQ_TODA, /* TODA */
> +       [6]     = EZX_IRQ_USB4V,
> +       [7]     = EZX_IRQ_ONOFF, /* ONOFF */
> +       [8]     = EZX_IRQ_ONOFF2, /* ONOFF2 */
> +       [9]     = EZX_IRQ_USB1V,
> +       [10]    = EZX_IRQ_MOBPORT, /* MOBPORT */
> +       [11]    = EZX_IRQ_MIC,
> +       [12]    = EZX_IRQ_HEADJACK,
> +       [13]    = EZX_IRQ_ST, /* ST */
> +       [14]    = EZX_IRQ_PC, /* PC */
> +       [15]    = EZX_IRQ_WARM, /* WARM */
> +       [16]    = EZX_IRQ_EOL, /* EOL */
> +       [17]    = EZX_IRQ_CLK, /* CLK */
> +       [18]    = EZX_IRQ_SYSRST, /* SYSRST */
> +       [19]    = 0,
> +       [20]    = EZX_IRQ_ADCDONE2,
> +       [21]    = EZX_IRQ_SOFTRESET, /* SOFTRESET */
> +       [22]    = EZX_IRQ_MNEXB, /* MNEXB */
> +};
> +
> +/* Array indexed by IRQ NUMBER, returns PCAP absolute value */
> +static unsigned int irq2pcap[] = {
> +       [EZX_IRQ_MNEXB]         = PCAP_IRQ_MNEXB,
> +       [EZX_IRQ_SOFTRESET]     = PCAP_IRQ_SOFTRESET,
> +       [EZX_IRQ_SYSRST]        = PCAP_IRQ_SYSRST,
> +       [EZX_IRQ_CLK]           = PCAP_IRQ_CLK,
> +       [EZX_IRQ_EOL]           = PCAP_IRQ_EOL,
> +       [EZX_IRQ_WARM]          = PCAP_IRQ_WARM,
> +       [EZX_IRQ_PC]            = PCAP_IRQ_PC,
> +       [EZX_IRQ_ST]            = PCAP_IRQ_ST,
> +       [EZX_IRQ_MOBPORT]       = PCAP_IRQ_MOBPORT,
> +       [EZX_IRQ_ONOFF2]        = PCAP_IRQ_ONOFF2,
> +       [EZX_IRQ_ONOFF]         = PCAP_IRQ_ONOFF,
> +       [EZX_IRQ_TODA]          = PCAP_IRQ_TODA,
> +       [EZX_IRQ_WL]            = PCAP_IRQ_WL,
> +       [EZX_IRQ_WH]            = PCAP_IRQ_WH,
> +       [EZX_IRQ_1HZ]           = PCAP_IRQ_1HZ,
> +       [EZX_IRQ_USB4V]         = PCAP_IRQ_USB4V,
> +       [EZX_IRQ_USB1V]         = PCAP_IRQ_USB1V,
> +       [EZX_IRQ_HEADJACK]      = PCAP_IRQ_A1,
> +       [EZX_IRQ_MIC]           = PCAP_IRQ_MB2,
> +       [EZX_IRQ_TS]            = PCAP_IRQ_TS,
> +       [EZX_IRQ_ADCDONE]       = PCAP_IRQ_ADCDONE,
> +       [EZX_IRQ_ADCDONE2]      = PCAP_IRQ_ADCDONE2,
> +};
> +
> +static void pcap_ack_irq(unsigned int irq)
> +{
> +       ezx_pcap_write(PCAP_REG_ISR, irq2pcap[irq]);
> +}
> +
> +static void pcap_mask_irq(unsigned int irq)
> +{
> +       u_int32_t reg;
> +       unsigned long flag;
> +
> +       spin_lock_irqsave(&ezx_ssp_lock, flag);
> +       ezx_pcap_read(PCAP_REG_MSR, &reg);
> +       reg |= irq2pcap[irq];
> +       ezx_pcap_write(PCAP_REG_MSR, reg);
> +       spin_unlock_irqrestore(&ezx_ssp_lock, flag);
> +}
> +
> +static void pcap_unmask_irq(unsigned int irq)
> +{
> +       u_int32_t tmp;
> +       unsigned long flag;
> +
> +       spin_lock_irqsave(&ezx_ssp_lock, flag);
> +       ezx_pcap_read(PCAP_REG_MSR, &tmp);
> +       tmp &= ~irq2pcap[irq];
> +       ezx_pcap_write(PCAP_REG_MSR, tmp);
> +       spin_unlock_irqrestore(&ezx_ssp_lock, flag);
> +}
> +
> +static struct irq_chip pcap_chip = {
> +       .name   = "ezx-pcap",
> +       .ack    = pcap_ack_irq,
> +       .mask   = pcap_mask_irq,
> +       .unmask = pcap_unmask_irq,
> +};
> +
> +/* handler for interrupt received from PCAP via GPIO */
> +static void pcap_irq_demux_handler(unsigned int irq, struct irq_desc *desc)
> +{
> +       int i;
> +       u_int32_t isr;
> +
> +       desc->chip->ack(irq);
> +       ezx_pcap_read(PCAP_REG_ISR, &isr);
> +       for (i = ARRAY_SIZE(pcap2irq)-1; i >= 0; i--) {
> +               unsigned int pirq = pcap2irq[i];
> +               if (!(isr & irq2pcap[pirq]))
> +                       continue;
> +               desc = &irq_desc[pirq];
> +               desc_handle_irq(pirq, desc);
> +       }
> +}
> +
> +static int ezx_pcap_remove(struct platform_device *pdev)
> +{
> +       int irq;
> +
> +       set_irq_chained_handler(pcap_irq, NULL);
> +       for (irq = EZX_IRQ(0); irq <= EZX_IRQ(21); irq++) {
> +               set_irq_chip(irq, NULL);
> +               set_irq_handler(irq, NULL);
> +               set_irq_flags(irq, 0);
> +       }
> +       ssp_exit(&ezx_ssp_dev);
> +
> +       return 0;
> +}
> +
> +static int __init ezx_pcap_probe(struct platform_device *pdev)
> +{
> +       unsigned int ret, irq;
> +
> +       pcap_data = pdev->dev.platform_data;
> +       if (pcap_data->cs >= 0) {
> +               if (pcap_data->config & CS_INVERTED)
> +                       gpio_direction_output(pcap_data->cs, 1);
> +               else
> +                       gpio_direction_output(pcap_data->cs, 0);
> +       }
> +       pcap_irq = platform_get_irq(pdev, 0);
> +       if (pcap_irq < 0) {
> +               printk(KERN_ERR "Unable to get IRQ for pcap!\n");
> +               return pcap_irq;
> +       }
> +
> +       ret = ssp_init(&ezx_ssp_dev, pcap_data->port, 0);
> +       if (ret) {
> +               printk(KERN_ERR "Unable to register SSP handler!\n");
> +               return ret;
> +       }
> +
> +       ssp_disable(&ezx_ssp_dev);
> +       ssp_config(&ezx_ssp_dev,
> +               (SSCR0_Motorola | SSCR0_DataSize(16) | SSCR0_EDSS),
> +               (SSCR1_TxTresh(1) | SSCR1_RxTresh(1)),
> +               0, SSCR0_SerClkDiv(pcap_data->clk));
> +       ssp_enable(&ezx_ssp_dev);
> +
> +       if (pcap_data->init)
> +               pcap_data->init();
> +
> +       /* set up interrupt demultiplexing code for PCAP2 irqs */
> +       for (irq = EZX_IRQ(0); irq <= EZX_IRQ(21); irq++) {
> +               set_irq_chip(irq, &pcap_chip);
> +               set_irq_handler(irq, handle_level_irq);
> +               set_irq_flags(irq, IRQF_VALID);
> +       }
> +       set_irq_type(pcap_irq, IRQ_TYPE_EDGE_RISING);
> +       set_irq_chained_handler(pcap_irq, pcap_irq_demux_handler);
> +       set_irq_wake(pcap_irq, 1);
> +
> +       /* mask/ack all PCAP interrupts */
> +       ezx_pcap_write(PCAP_REG_MSR, PCAP_MASK_ALL_INTERRUPT);
> +       ezx_pcap_write(PCAP_REG_ISR, PCAP_CLEAR_INTERRUPT_REGISTER);
> +
> +
> +       printk(KERN_INFO "ezx-pcap: ssp driver registered\n");
> +       return ret;
> +}
> +
> +#ifdef CONFIG_PM
> +static int ezx_pcap_suspend(struct platform_device *dev, pm_message_t state)
> +{
> +       ssp_flush(&ezx_ssp_dev);
> +       ssp_save_state(&ezx_ssp_dev, &ezx_ssp_state);
> +       ssp_disable(&ezx_ssp_dev);
> +       return 0;
> +}
> +
> +static int ezx_pcap_resume(struct platform_device *dev)
> +{
> +       ssp_restore_state(&ezx_ssp_dev, &ezx_ssp_state);
> +       ssp_enable(&ezx_ssp_dev);
> +
> +       return 0;
> +}
> +#endif
> +
> +static struct platform_driver ezxpcap_driver = {
> +       .probe          = ezx_pcap_probe,
> +       .remove         = ezx_pcap_remove,
> +#ifdef CONFIG_PM
> +       .suspend        = ezx_pcap_suspend,
> +       .resume         = ezx_pcap_resume,
> +#endif
> +       .driver         = {
> +               .name   = "ezx-pcap",
> +               .owner  = THIS_MODULE,
> +       },
> +};
> +
> +static int __init ezx_pcap_init(void)
> +{
> +       return platform_driver_register(&ezxpcap_driver);
> +}
> +
> +subsys_initcall(ezx_pcap_init);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Harald Welte");
> +MODULE_DESCRIPTION("Motorola PCAP2 ASIC Driver");
> Index: linux-2.6-arm/include/linux/mfd/ezx-pcap.h
> ===================================================================
> --- /dev/null
> +++ linux-2.6-arm/include/linux/mfd/ezx-pcap.h
> @@ -0,0 +1,249 @@
> +/*
> + * Copyright 2007 Daniel Ribeiro <drwyrm@gmail.com>
> + *
> + * For further information, please see http://wiki.openezx.org/PCAP2
> + */
> +
> +#ifndef EZX_PCAP_H
> +#define EZX_PCAP_H
> +
> +struct pcap_platform_data {
> +       int port;               /* SSP port */
> +       int cs;                 /* CS gpio */
> +       int config;
> +       int clk;
> +       int (*init)(void);      /* board specific driver init */
> +};
> +
> +#define CS_INVERTED    1
> +
> +#define PCAP_REGISTER_WRITE_OP_BIT     0x80000000
> +#define PCAP_REGISTER_READ_OP_BIT      0x00000000
> +
> +#define PCAP_REGISTER_VALUE_MASK       0x01ffffff
> +#define PCAP_REGISTER_ADDRESS_MASK     0x7c000000
> +#define PCAP_REGISTER_ADDRESS_SHIFT    26
> +#define PCAP_REGISTER_NUMBER           32
> +#define PCAP_CLEAR_INTERRUPT_REGISTER  0x01ffffff
> +#define PCAP_MASK_ALL_INTERRUPT                0x01ffffff
> +
> +#define pbit(reg, bit) ((reg << PCAP_REGISTER_ADDRESS_SHIFT) | bit)
> +
> +/* registers acessible by both pcap ports */
> +#define PCAP_REG_ISR           0x0     /* Interrupt Status */
> +#define PCAP_REG_MSR           0x1     /* Interrupt Mask */
> +#define PCAP_REG_PSTAT         0x2     /* Processor Status */
> +#define PCAP_REG_VREG2         0x6     /* Regulator Bank 2 Control */
> +#define PCAP_REG_AUXVREG       0x7     /* Auxiliary Regulator Control */
> +#define PCAP_REG_BATT          0x8     /* Battery Control */
> +#define PCAP_REG_ADC           0x9     /* AD Control */
> +#define PCAP_REG_ADR           0xa     /* AD Result */
> +#define PCAP_REG_CODEC         0xb     /* Audio Codec Control */
> +#define PCAP_REG_RX_AMPS       0xc     /* RX Audio Amplifiers Control */
> +#define PCAP_REG_ST_DAC                0xd     /* Stereo DAC Control */
> +#define PCAP_REG_BUSCTRL       0x14    /* Connectivity Control */
> +#define PCAP_REG_PERIPH                0x15    /* Peripheral Control */
> +#define PCAP_REG_LOWPWR                0x18    /* Regulator Low Power Control */
> +#define PCAP_REG_TX_AMPS       0x1a    /* TX Audio Amplifiers Control */
> +#define PCAP_REG_GP            0x1b    /* General Purpose */
> +
> +/* registers acessible by pcap port 1 only (a1200, e2 & e6) */
> +#define PCAP_REG_INT_SEL       0x3     /* Interrupt Select */
> +#define PCAP_REG_SWCTRL                0x4     /* Switching Regulator Control */
> +#define PCAP_REG_VREG1         0x5     /* Regulator Bank 1 Control */
> +#define PCAP_REG_RTC_TOD       0xe     /* RTC Time of Day */
> +#define PCAP_REG_RTC_TODA      0xf     /* RTC Time of Day Alarm */
> +#define PCAP_REG_RTC_DAY       0x10    /* RTC Day */
> +#define PCAP_REG_RTC_DAYA      0x11    /* RTC Day Alarm */
> +#define PCAP_REG_MTRTMR                0x12    /* AD Monitor Timer */
> +#define PCAP_REG_PWR           0x13    /* Power Control */
> +#define PCAP_REG_AUXVREG_MASK  0x16    /* Auxiliary Regulator Mask */
> +#define PCAP_REG_VENDOR_REV    0x17
> +#define PCAP_REG_PERIPH_MASK   0x19    /* Peripheral Mask */
> +
> +/* interrupts - registers 0x0, 0x1, 0x2, 0x3 */
> +#define PCAP_IRQ_ADCDONE       (1 << 0)        /* AD Conversion Done Port 1 */
> +#define PCAP_IRQ_TS            (1 << 1)        /* Touch Screen */
> +#define PCAP_IRQ_1HZ           (1 << 2)        /* 1HZ Timer */
> +#define PCAP_IRQ_WH            (1 << 3)        /* "...high"??? */
> +#define PCAP_IRQ_WL            (1 << 4)        /* "...low"??? */
> +#define PCAP_IRQ_TODA          (1 << 5)        /* RTC Time Of Day?
> +                                                    (see "RTC_TODA") */
> +#define PCAP_IRQ_USB4V         (1 << 6)        /* USB OTG */
> +#define PCAP_IRQ_ONOFF         (1 << 7)        /* in blob: "ONOFFSNS" */
> +#define PCAP_IRQ_ONOFF2                (1 << 8)        /* in blob: "ONOFFSNS2" */
> +#define PCAP_IRQ_USB1V         (1 << 9)        /* USB below 1volt???
> +                                                    in blob: "USBDET_1V" */
> +#define PCAP_IRQ_MOBPORT       (1 << 10)       /* GSM-related?? ("mobport",
> +                               see 958_MotDoc.pdf); in blob: "MOBSENSB" */
> +#define PCAP_IRQ_MB2           (1 << 11)       /* Mic; in blob: "MB2SNS" */
> +#define PCAP_IRQ_A1            (1 << 12)       /* Audio jack;
> +                                                    in blob: "A1SNS" */
> +#define PCAP_IRQ_ST            (1 << 13)       /* called "MSTB" in blob */
> +#define PCAP_IRQ_PC            (1 << 14)
> +#define PCAP_IRQ_WARM          (1 << 15)
> +#define PCAP_IRQ_EOL           (1 << 16)       /* battery End Of Life???
> +                                       (see below); in blob: "EOL_STAT" */
> +#define PCAP_IRQ_CLK           (1 << 17)       /* called "CLK_STAT" in blob */
> +#define PCAP_IRQ_SYSRST                (1 << 18)
> +#define PCAP_IRQ_ADCDONE2      (1 << 20)       /* AD Conversion Done Port 2 */
> +#define PCAP_IRQ_SOFTRESET     (1 << 21)
> +#define PCAP_IRQ_MNEXB         (1 << 22)
> +
> +/* register VREG2 (0x6) */
> +#define PCAP_VREG2_V1_STBY     (1 << 0)
> +#define PCAP_VREG2_V2_STBY     (1 << 1)
> +/* V3, SRAM: */
> +#define PCAP_VREG2_V3_STBY     (1 << 2)
> +#define PCAP_VREG2_V4_STBY     (1 << 3)
> +#define PCAP_VREG2_V5_STBY     (1 << 4)
> +/* V6, E680 I2C camera?: */
> +#define PCAP_VREG2_V6_STBY     (1 << 5)
> +#define PCAP_VREG2_V7_STBY     (1 << 6)
> +/* V8, PLL: */
> +#define PCAP_VREG2_V8_STBY     (1 << 7)
> +#define PCAP_VREG2_V9_STBY     (1 << 8)
> +#define PCAP_VREG2_V10_STBY    (1 << 9)
> +#define PCAP_VREG2_V1_LOWPWR   (1 << 10)
> +#define PCAP_VREG2_V2_LOWPWR   (1 << 11)
> +/* V3, SRAM: */
> +#define PCAP_VREG2_V3_LOWPWR   (1 << 12)
> +#define PCAP_VREG2_V4_LOWPWR   (1 << 13)
> +#define PCAP_VREG2_V5_LOWPWR   (1 << 14)
> +/* V6, E680 I2C camera?: */
> +#define PCAP_VREG2_V6_LOWPWR   (1 << 15)
> +#define PCAP_VREG2_V7_LOWPWR   (1 << 16)
> +/* V8, PLL: */
> +#define PCAP_VREG2_V8_LOWPWR   (1 << 17)
> +#define PCAP_VREG2_V9_LOWPWR   (1 << 18)
> +#define PCAP_VREG2_V10_LOWPWR  (1 << 19)
> +
> +/* register AUXVREG (0x7) */
> +#define VAUX1          0
> +#define VAUX2          1
> +#define VAUX3          2
> +#define VAUX4          3
> +#define VSIM           4
> +#define VSIM2          5
> +#define VVIB           6
> +#define VC             7
> +
> +#define VAUX_EN                0
> +#define VAUX_VAL       1
> +#define VAUX_MASK      2
> +#define VAUX_STBY      3
> +#define VAUX_LOWPWR    4
> +
> +#define PCAP_BATT_DAC_MASK             0x000000ff
> +#define PCAP_BATT_DAC_SHIFT            0
> +#define PCAP_BATT_B_FDBK               (1 << 8)
> +#define PCAP_BATT_EXT_ISENSE           (1 << 9)
> +#define PCAP_BATT_V_COIN_MASK          0x00003c00
> +#define PCAP_BATT_V_COIN_SHIFT         10
> +#define PCAP_BATT_I_COIN               (1 << 14)
> +#define PCAP_BATT_COIN_CH_EN           (1 << 15)
> +#define PCAP_BATT_EOL_SEL_MASK         0x000e0000
> +#define PCAP_BATT_EOL_SEL_SHIFT                17
> +#define PCAP_BATT_EOL_CMP_EN           (1 << 20)
> +#define PCAP_BATT_BATT_DET_EN          (1 << 21)
> +#define PCAP_BATT_THERMBIAS_CTRL       (1 << 22)
> +
> +#define PCAP_ADC_ADEN                  (1 << 0)
> +#define PCAP_ADC_RAND                  (1 << 1)
> +#define PCAP_ADC_AD_SEL1               (1 << 2)
> +#define PCAP_ADC_AD_SEL2               (1 << 3)
> +#define PCAP_ADC_ADA1_MASK             0x00000070
> +#define PCAP_ADC_ADA1_SHIFT            4
> +#define PCAP_ADC_ADA2_MASK             0x00000380
> +#define PCAP_ADC_ADA2_SHIFT            7
> +#define PCAP_ADC_ATO_MASK              0x00003c00
> +#define PCAP_ADC_ATO_SHIFT             10
> +#define PCAP_ADC_ATOX                  (1 << 14)
> +#define PCAP_ADC_MTR1                  (1 << 15)
> +#define PCAP_ADC_MTR2                  (1 << 16)
> +#define PCAP_ADC_TS_M_MASK             0x000e0000
> +#define PCAP_ADC_TS_M_SHIFT            17
> +#define PCAP_ADC_TS_REF_LOWPWR         (1 << 20)
> +#define PCAP_ADC_TS_REFENB             (1 << 21)
> +#define PCAP_ADC_BATT_I_POLARITY       (1 << 22)
> +#define PCAP_ADC_BATT_I_ADC            (1 << 23)
> +
> +#define PCAP_ADR_ADD1_MASK             0x000003ff
> +#define PCAP_ADR_ADD1_SHIFT            0
> +#define PCAP_ADR_ADD2_MASK             0x000ffc00
> +#define PCAP_ADR_ADD2_SHIFT            10
> +#define PCAP_ADR_ADINC1                        (1 << 20)
> +#define PCAP_ADR_ADINC2                        (1 << 21)
> +#define PCAP_ADR_ASC                   (1 << 22)
> +#define PCAP_ADR_ONESHOT               (1 << 23)
> +
> +#define PCAP_BUSCTRL_FSENB             (1 << 0)
> +#define PCAP_BUSCTRL_USB_SUSPEND       (1 << 1)
> +#define PCAP_BUSCTRL_USB_PU            (1 << 2)
> +#define PCAP_BUSCTRL_USB_PD            (1 << 3)
> +#define PCAP_BUSCTRL_VUSB_EN           (1 << 4)
> +#define PCAP_BUSCTRL_USB_PS            (1 << 5)
> +#define PCAP_BUSCTRL_VUSB_MSTR_EN      (1 << 6)
> +#define PCAP_BUSCTRL_VBUS_PD_ENB       (1 << 7)
> +#define PCAP_BUSCTRL_CURRLIM           (1 << 8)
> +#define PCAP_BUSCTRL_RS232ENB          (1 << 9)
> +#define PCAP_BUSCTRL_RS232_DIR         (1 << 10)
> +#define PCAP_BUSCTRL_SE0_CONN          (1 << 11)
> +#define PCAP_BUSCTRL_USB_PDM           (1 << 12)
> +#define PCAP_BUSCTRL_BUS_PRI_ADJ       (1 << 24)
> +
> +#define PCAP_BIT_PERIPH_BL_CTRL0       0x54000001
> +#define PCAP_BIT_PERIPH_BL_CTRL1       0x54000002
> +#define PCAP_BIT_PERIPH_BL_CTRL2       0x54000004
> +#define PCAP_BIT_PERIPH_BL_CTRL3       0x54000008
> +#define PCAP_BIT_PERIPH_BL_CTRL4       0x54000010
> +#define PCAP_BIT_PERIPH_LEDR_EN                0x54000020
> +#define PCAP_BIT_PERIPH_LEDG_EN                0x54000040
> +#define PCAP_BIT_PERIPH_LEDR_CTRL0     0x54000080
> +#define PCAP_BIT_PERIPH_LEDR_CTRL1     0x54000100
> +#define PCAP_BIT_PERIPH_LEDR_CTRL2     0x54000200
> +#define PCAP_BIT_PERIPH_LEDR_CTRL3     0x54000400
> +#define PCAP_BIT_PERIPH_LEDG_CTRL0     0x54000800
> +#define PCAP_BIT_PERIPH_LEDG_CTRL1     0x54001000
> +#define PCAP_BIT_PERIPH_LEDG_CTRL2     0x54002000
> +#define PCAP_BIT_PERIPH_LEDG_CTRL3     0x54004000
> +#define PCAP_BIT_PERIPH_LEDR_I0                0x54008000
> +#define PCAP_BIT_PERIPH_LEDR_I1                0x54010000
> +#define PCAP_BIT_PERIPH_LEDG_I0                0x54020000
> +#define PCAP_BIT_PERIPH_LEDG_I1                0x54040000
> +#define PCAP_BIT_PERIPH_SKIP           0x54080000
> +#define PCAP_BIT_PERIPH_BL2_CTRL0      0x54100000
> +#define PCAP_BIT_PERIPH_BL2_CTRL1      0x54200000
> +#define PCAP_BIT_PERIPH_BL2_CTRL2      0x54400000
> +#define PCAP_BIT_PERIPH_BL2_CTRL3      0x54800000
> +#define PCAP_BIT_PERIPH_BL2_CTRL4      0x55000000
> +
> +/* LOWPWR */
> +#define SW1            8
> +#define SW2            16
> +
> +#define SW_MODE                0
> +#define SW_VOLTAGE     4
> +
> +#define SW_VOLTAGE_900 0x0
> +#define SW_VOLTAGE_950 0x1
> +#define SW_VOLTAGE_1000        0x2
> +#define SW_VOLTAGE_1050        0x3
> +#define SW_VOLTAGE_1100        0x4
> +#define SW_VOLTAGE_1150        0x5
> +#define SW_VOLTAGE_1200        0x6
> +#define SW_VOLTAGE_1250        0x7
> +#define SW_VOLTAGE_1300        0x8
> +#define SW_VOLTAGE_1350        0x9
> +#define SW_VOLTAGE_1400        0xa
> +#define SW_VOLTAGE_1500        0xb
> +#define SW_VOLTAGE_1600        0xc
> +#define SW_VOLTAGE_1875        0xd
> +#define SW_VOLTAGE_2250        0xe
> +#define SW_VOLTAGE_4400        0xf
> +
> +void ezx_pcap_write(u_int8_t, u_int32_t);
> +void ezx_pcap_read(u_int8_t, u_int32_t *);
> +void ezx_pcap_set_sw(u_int8_t, u_int8_t, u_int8_t);
> +int ezx_pcap_set_vaux(u_int8_t, u_int8_t, u_int8_t);
> +#endif
> Index: linux-2.6-arm/drivers/mfd/Kconfig
> ===================================================================
> --- linux-2.6-arm.orig/drivers/mfd/Kconfig
> +++ linux-2.6-arm/drivers/mfd/Kconfig
> @@ -49,6 +49,13 @@
>        help
>          Support for Toshiba Mobile IO Controller TC6393XB
>
> +config EZX_PCAP
> +       bool "PCAP Support"
> +       depends on PXA_SSP && PXA_EZX
> +       help
> +         This enables the PCAP ASIC present on EZX Phones. This is
> +         needed for MMC, TouchScreen, Sound, USB, etc..
> +
>  endmenu
>
>  menu "Multimedia Capabilities Port drivers"
> Index: linux-2.6-arm/drivers/mfd/Makefile
> ===================================================================
> --- linux-2.6-arm.orig/drivers/mfd/Makefile
> +++ linux-2.6-arm/drivers/mfd/Makefile
> @@ -12,6 +12,8 @@
>
>  obj-$(CONFIG_MFD_CORE)         += mfd-core.o
>
> +obj-$(CONFIG_EZX_PCAP)         += ezx-pcap.o
> +
>  obj-$(CONFIG_MCP)              += mcp-core.o
>  obj-$(CONFIG_MCP_SA11X0)       += mcp-sa11x0.o
>  obj-$(CONFIG_MCP_UCB1200)      += ucb1x00-core.o
> Index: linux-2.6-arm/arch/arm/mach-pxa/Kconfig
> ===================================================================
> --- linux-2.6-arm.orig/arch/arm/mach-pxa/Kconfig
> +++ linux-2.6-arm/arch/arm/mach-pxa/Kconfig
> @@ -251,6 +251,8 @@
>        select PXA27x
>        select IWMMXT
>        select HAVE_PWM
> +       select PXA_SSP
> +       select EZX_PCAP
>
>  config MACH_EZX_A780
>        bool "Motorola EZX A780"
> Index: linux-2.6-arm/arch/arm/mach-pxa/ezx.c
> ===================================================================
> --- linux-2.6-arm.orig/arch/arm/mach-pxa/ezx.c
> +++ linux-2.6-arm/arch/arm/mach-pxa/ezx.c
> @@ -15,7 +15,9 @@
>  #include <linux/init.h>
>  #include <linux/platform_device.h>
>  #include <linux/delay.h>
> +#include <linux/gpio.h>
>  #include <linux/pwm_backlight.h>
> +#include <linux/mfd/ezx-pcap.h>
>
>  #include <asm/setup.h>
>  #include <asm/arch/pxafb.h>
> @@ -87,7 +89,54 @@
>        .lcd_conn       = LCD_COLOR_TFT_18BPP,
>  };
>
> +/* PCAP */
> +static int ezx_pcap_init(void)
> +{
> +       /* disable all voltage regulators */
> +       ezx_pcap_write(PCAP_REG_AUXVREG, 0);
> +
> +       /* set SW1 sleep to keep SW1 1.3v in sync mode */
> +       /*  SW1 active in sync mode */
> +       ezx_pcap_set_sw(SW1, SW_MODE, 0x1);
> +
> +       /*  set core voltage */
> +       ezx_pcap_set_sw(SW1, SW_VOLTAGE, SW_VOLTAGE_1250);
> +
> +       /* redirect all interrupts to AP */
> +       if (!(machine_is_ezx_a780() || machine_is_ezx_e680()))
> +               ezx_pcap_write(PCAP_REG_INT_SEL, 0);
> +
> +       return 0;
> +}
> +
> +static struct pcap_platform_data ezx_pcap_platform_data = {
> +       .port   = 1,
> +       .cs     = 24,
> +       .clk    = 1,
> +       .config = 0,
> +       .init   = ezx_pcap_init,
> +};
> +
> +static struct resource ezx_pcap_resources[] = {
> +       [0] = {
> +               .start      = IRQ_GPIO1,
> +               .end        = IRQ_GPIO1,
> +               .flags      = IORESOURCE_IRQ,
> +       },
> +};
> +
> +struct platform_device ezx_pcap_device = {
> +       .name       = "ezx-pcap",
> +       .id     = -1,
> +       .num_resources  = ARRAY_SIZE(ezx_pcap_resources),
> +       .resource   = ezx_pcap_resources,
> +       .dev        = {
> +               .platform_data = &ezx_pcap_platform_data,
> +       },
> +};
> +
>  static struct platform_device *devices[] __initdata = {
> +       &ezx_pcap_device,
>        &ezx_backlight_device,
>  };
>
> @@ -105,6 +154,11 @@
>        GPIO46_STUART_RXD,
>        GPIO47_STUART_TXD,
>
> +       /* PCAP SSP */
> +       GPIO29_SSP1_SCLK,
> +       GPIO25_SSP1_TXD,
> +       GPIO26_SSP1_RXD,
> +
>        /* For A780 support (connected with Neptune GSM chip) */
>        GPIO30_USB_P3_2,        /* ICL_TXENB */
>        GPIO31_USB_P3_6,        /* ICL_VPOUT */
> @@ -118,11 +172,12 @@
>  {
>        pxa2xx_mfp_config(ARRAY_AND_SIZE(ezx_pin_config));
>        pxa_set_i2c_info(NULL);
> -       if (machine_is_ezx_a780() || machine_is_ezx_e680())
> +       if (machine_is_ezx_a780() || machine_is_ezx_e680()) {
>                set_pxa_fb_info(&ezx_fb_info_1);
> -       else
> +               ezx_pcap_platform_data.config |= CS_INVERTED;
> +       } else {
>                set_pxa_fb_info(&ezx_fb_info_2);
> -
> +       }
>        platform_add_devices(devices, ARRAY_SIZE(devices));
>  }
>
> Index: linux-2.6-arm/include/asm-arm/arch-pxa/irqs.h
> ===================================================================
> --- linux-2.6-arm.orig/include/asm-arm/arch-pxa/irqs.h
> +++ linux-2.6-arm/include/asm-arm/arch-pxa/irqs.h
> @@ -186,6 +186,39 @@
>  #endif
>  #endif /* CONFIG_MACH_PCM027 */
>
> +#define EZX_IRQ(x)             PXA_BOARD_IRQ(x)
> +
> +#ifdef CONFIG_PXA_EZX
> +#define EZX_IRQ_USB4V          EZX_IRQ(0) /* EMU */
> +#define EZX_IRQ_USB1V          EZX_IRQ(1) /* EMU */
> +#define EZX_IRQ_HEADJACK       EZX_IRQ(2) /* Audio connector */
> +#define EZX_IRQ_MIC            EZX_IRQ(3) /* Audio connector */
> +#define EZX_IRQ_ADCDONE                EZX_IRQ(4)
> +#define EZX_IRQ_TS             EZX_IRQ(5) /* TS touch */
> +#define EZX_IRQ_ADCDONE2       EZX_IRQ(6) /* TS x/y ADC ready */
> +#define EZX_IRQ_WH             EZX_IRQ(7)
> +#define EZX_IRQ_WL             EZX_IRQ(8)
> +#define EZX_IRQ_ONOFF          EZX_IRQ(9)
> +#define EZX_IRQ_ONOFF2         EZX_IRQ(10)
> +#define EZX_IRQ_MOBPORT                EZX_IRQ(11)
> +#define EZX_IRQ_TODA           EZX_IRQ(12)
> +#define EZX_IRQ_1HZ            EZX_IRQ(13)
> +#define EZX_IRQ_MNEXB          EZX_IRQ(14)
> +#define EZX_IRQ_ST             EZX_IRQ(15)
> +#define EZX_IRQ_PC             EZX_IRQ(16)
> +#define EZX_IRQ_SYSRST         EZX_IRQ(17)
> +#define EZX_IRQ_SOFTRESET      EZX_IRQ(18)
> +#define EZX_IRQ_EOL            EZX_IRQ(19)
> +#define EZX_IRQ_CLK            EZX_IRQ(20)
> +#define EZX_IRQ_WARM           EZX_IRQ(21)
> +#define EZX_LAST_IRQ           EZX_IRQ_WARM
> +
> +#if PXA_BOARD_IRQ_END < EZX_LAST_IRQ
> +#undef  PXA_BOARD_IRQ_END
> +#define PXA_BOARD_IRQ_END      EZX_LAST_IRQ
> +#endif
> +#endif /* CONFIG_EZX */
> +
>  /*
>  * Extended IRQs for companion chips start from the last board-specific IRQ.
>  * NOTE: unlike board specific IRQs, the number space for these IRQs cannot
>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Patch 06/10] mfd: PCAP driver for the Motorola EZX GSM mobile phones
  2008-07-08 20:32           ` Stefan Schmidt
  2008-07-08 21:19             ` pHilipp Zabel
@ 2008-07-09  2:34             ` Eric Miao
  2008-07-09  6:26               ` Stefan Schmidt
  2008-07-09 15:29             ` Liam Girdwood
  2008-07-10 16:21             ` Russell King - ARM Linux
  3 siblings, 1 reply; 12+ messages in thread
From: Eric Miao @ 2008-07-09  2:34 UTC (permalink / raw)
  To: Stefan Schmidt
  Cc: Daniel Ribeiro, pHilipp Zabel, linux-arm-kernel, sameo, linux-kernel


> o Eric, what do you like the PCAP_REDIRECT_IRQ flag to do?
> 

I'm seeing the following the block of code in the original patch:
 
+/* PCAP */
+static int ezx_pcap_init(void)
+{

...

+	/* redirect all interrupts to AP */
+	if (!(machine_is_ezx_a780() || machine_is_ezx_e680()))
+		ezx_pcap_write(PCAP_REG_INT_SEL, 0);
+
+	return 0;
+}

that prevent this driver from being generic, a flag of PCAP_REDIRECT_IRQ
might be used to decide the writing to PCAP_REG_INT_SEL is necessary.


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Patch 06/10] mfd: PCAP driver for the Motorola EZX GSM mobile phones
  2008-07-09  2:34             ` Eric Miao
@ 2008-07-09  6:26               ` Stefan Schmidt
  0 siblings, 0 replies; 12+ messages in thread
From: Stefan Schmidt @ 2008-07-09  6:26 UTC (permalink / raw)
  To: Eric Miao
  Cc: Daniel Ribeiro, pHilipp Zabel, linux-arm-kernel, sameo, linux-kernel

Hello.

On Wed, 2008-07-09 at 10:34, Eric Miao wrote:
> 
> > o Eric, what do you like the PCAP_REDIRECT_IRQ flag to do?
> > 
> 
> I'm seeing the following the block of code in the original patch:
>  
> +/* PCAP */
> +static int ezx_pcap_init(void)
> +{
> 
> ...
> 
> +	/* redirect all interrupts to AP */
> +	if (!(machine_is_ezx_a780() || machine_is_ezx_e680()))
> +		ezx_pcap_write(PCAP_REG_INT_SEL, 0);
> +
> +	return 0;
> +}
> 
> that prevent this driver from being generic, a flag of PCAP_REDIRECT_IRQ
> might be used to decide the writing to PCAP_REG_INT_SEL is necessary.

Ok, now we got it. :)

Will do as requested. Array changes are pending and we also have some more
cleanups open. Will send a new patch when we have this ready.

regards
stefan Schmidt

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Patch 06/10] mfd: PCAP driver for the Motorola EZX GSM mobile phones
  2008-07-08 20:32           ` Stefan Schmidt
  2008-07-08 21:19             ` pHilipp Zabel
  2008-07-09  2:34             ` Eric Miao
@ 2008-07-09 15:29             ` Liam Girdwood
  2008-07-10 16:21             ` Russell King - ARM Linux
  3 siblings, 0 replies; 12+ messages in thread
From: Liam Girdwood @ 2008-07-09 15:29 UTC (permalink / raw)
  To: Stefan Schmidt
  Cc: Daniel Ribeiro, Eric Miao, pHilipp Zabel, linux-arm-kernel,
	sameo, linux-kernel

On Tue, 2008-07-08 at 22:32 +0200, Stefan Schmidt wrote:

> 
> Open items we are still working on:
> o Voltage framework. Is this needed for the first merge or can we switch to it
>   with a later patch?

Hopefully the voltage framework will make the next merge window. I
received an automated message from akpm a while back saying it had been
"merged into mainline or a subsystem tree". I'm now trying to find out
why I've not seen it on linux-next atm.

Liam


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Patch 06/10] mfd: PCAP driver for the Motorola EZX GSM mobile phones
  2008-07-08 20:32           ` Stefan Schmidt
                               ` (2 preceding siblings ...)
  2008-07-09 15:29             ` Liam Girdwood
@ 2008-07-10 16:21             ` Russell King - ARM Linux
  2008-07-10 16:40               ` Stefan Schmidt
  3 siblings, 1 reply; 12+ messages in thread
From: Russell King - ARM Linux @ 2008-07-10 16:21 UTC (permalink / raw)
  To: Stefan Schmidt; +Cc: Daniel Ribeiro, linux-arm-kernel, linux-kernel

On Tue, Jul 08, 2008 at 10:32:13PM +0200, Stefan Schmidt wrote:
> Daniel fixed all of Philipps comments besides the voltage framework. From Eric
> comments we updated to the new IRQ part and moved to platform data.

Due to the problems people were seeing with the IRQ numbering commit, we
(Eric and myself) have decided to drop that commit for the time being.
It will reappear after this merge window however.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Patch 06/10] mfd: PCAP driver for the Motorola EZX GSM mobile phones
  2008-07-10 16:21             ` Russell King - ARM Linux
@ 2008-07-10 16:40               ` Stefan Schmidt
  0 siblings, 0 replies; 12+ messages in thread
From: Stefan Schmidt @ 2008-07-10 16:40 UTC (permalink / raw)
  To: Russell King - ARM Linux; +Cc: Daniel Ribeiro, linux-arm-kernel, linux-kernel

Hello.

On Thu, 2008-07-10 at 17:21, Russell King - ARM Linux wrote:
> On Tue, Jul 08, 2008 at 10:32:13PM +0200, Stefan Schmidt wrote:
> > Daniel fixed all of Philipps comments besides the voltage framework. From Eric
> > comments we updated to the new IRQ part and moved to platform data.
> 
> Due to the problems people were seeing with the IRQ numbering commit, we
> (Eric and myself) have decided to drop that commit for the time being.
> It will reappear after this merge window however.

Thanks for the note.I just run into it while testing your updated tree with our
remaining patches. I remove this for now and will switch back to it when the
dust has settled and it is back.

regards
Stefan Schmidt

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2008-07-10 16:36 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20080707184000.411913919@datenfreihafen.org>
2008-07-07 18:40 ` [Patch 06/10] mfd: PCAP driver for the Motorola EZX GSM mobile phones stefan
2008-07-07 19:21   ` pHilipp Zabel
2008-07-07 20:02     ` Stefan Schmidt
2008-07-08  6:49       ` Eric Miao
2008-07-08 14:01         ` Daniel Ribeiro
2008-07-08 20:32           ` Stefan Schmidt
2008-07-08 21:19             ` pHilipp Zabel
2008-07-09  2:34             ` Eric Miao
2008-07-09  6:26               ` Stefan Schmidt
2008-07-09 15:29             ` Liam Girdwood
2008-07-10 16:21             ` Russell King - ARM Linux
2008-07-10 16:40               ` Stefan Schmidt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).