[Patch 06/10] mfd: PCAP driver for the Motorola EZX GSM mobile phones

From: stefan
Date: Mon Jul 07 2008 - 15:20:23 EST


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@xxxxxxxxxxx>

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@xxxxxxxxxxx>
+ * Copyright (C) 2007-2008 Daniel Ribeiro <wyrm@xxxxxxxxxxx>
+ *
+ */
+
+#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@xxxxxxxxxxx>
+ *
+ * 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));
}


--
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/