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.

Since the last submission we are also using the spi subsystem and
pxa2xx-spi instead of ssp.c directly as before.

Signed-off-by: Daniel Ribeiro <[EMAIL PROTECTED]>

---
 arch/arm/mach-pxa/Kconfig    |    1 +
 drivers/mfd/Kconfig          |    8 +
 drivers/mfd/Makefile         |    2 +
 drivers/mfd/ezx-pcap.c       |  598 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/ezx-pcap.h |  290 ++++++++++++++++++++
 5 files changed, 899 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig
index a062235..d64c15a 100644
--- a/arch/arm/mach-pxa/Kconfig
+++ b/arch/arm/mach-pxa/Kconfig
@@ -351,6 +351,7 @@ config PXA_EZX
        select PXA27x
        select IWMMXT
        select HAVE_PWM
+       select EZX_PCAP
 
 config MACH_EZX_A780
        bool "Motorola EZX A780"
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 2572773..c8c3e1e 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -153,6 +153,14 @@ config MFD_WM8350_I2C
          I2C as the control interface.  Additional options must be
          selected to enable support for the functionality of the chip.
 
+config EZX_PCAP
+       bool "PCAP Support"
+       depends on PXA_EZX
+       select SPI_PXA2XX
+       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"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 9a5ad8a..cbf32e0 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -21,6 +21,8 @@ obj-$(CONFIG_TWL4030_CORE)    += twl4030-core.o twl4030-irq.o
 
 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
diff --git a/drivers/mfd/ezx-pcap.c b/drivers/mfd/ezx-pcap.c
new file mode 100644
index 0000000..c660372
--- /dev/null
+++ b/drivers/mfd/ezx-pcap.c
@@ -0,0 +1,598 @@
+/*
+ * Driver for Motorola PCAP2 as present in EZX phones
+ *
+ * Copyright (C) 2006 Harald Welte <[EMAIL PROTECTED]>
+ * Copyright (C) 2007-2008 Daniel Ribeiro <[EMAIL PROTECTED]>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.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 <linux/spi/spi.h>
+
+#include <asm/mach-types.h>
+
+#include <mach/ssp.h>
+#include <mach/pxa-regs.h>
+#include <mach/regs-ssp.h>
+#include <mach/mfp-pxa27x.h>
+#include <mach/irqs.h>
+
+struct pcap_chip {
+       struct spi_device *spi;
+       struct work_struct work;
+       struct workqueue_struct *workqueue;
+       void (*adc_done)(void *);
+       void *adc_data;
+};
+static struct pcap_chip pcap;
+
+static LIST_HEAD(event_list);
+static DEFINE_MUTEX(event_lock);
+static DEFINE_MUTEX(adc_lock);
+
+/* IO */
+static int ezx_pcap_putget(u32 *data)
+{
+       struct spi_transfer t;
+       struct spi_message m;
+
+       memset(&t, 0, sizeof t);
+       spi_message_init(&m);
+       t.len = 4;
+       t.tx_buf = (u8 *)data;
+       t.rx_buf = (u8 *)data;
+       t.bits_per_word = 32;
+       spi_message_add_tail(&t, &m);
+       return spi_sync(pcap.spi, &m);
+}
+
+int ezx_pcap_write(u8 reg_num, u32 value)
+{
+       value &= PCAP_REGISTER_VALUE_MASK;
+       value |= PCAP_REGISTER_WRITE_OP_BIT
+               | (reg_num << PCAP_REGISTER_ADDRESS_SHIFT);
+       return ezx_pcap_putget(&value);
+}
+EXPORT_SYMBOL_GPL(ezx_pcap_write);
+
+int ezx_pcap_read(u8 reg_num, u32 *value)
+{
+       *value = PCAP_REGISTER_READ_OP_BIT
+               | (reg_num << PCAP_REGISTER_ADDRESS_SHIFT);
+
+       return ezx_pcap_putget(value);
+}
+EXPORT_SYMBOL_GPL(ezx_pcap_read);
+
+/* Voltage regulators */
+int ezx_pcap_set_sw(u8 sw, u8 what, u8 val)
+{
+       u32 tmp;
+
+       ezx_pcap_read(PCAP_REG_LOWPWR, &tmp);
+       tmp &= ~(0xf << (sw + what));
+       tmp |= ((val & 0xf) << (sw + what));
+       return ezx_pcap_write(PCAP_REG_LOWPWR, tmp);
+}
+EXPORT_SYMBOL_GPL(ezx_pcap_set_sw);
+
+static u8 vreg_table[][5] = {
+       /*              EN      INDEX   MASK    STBY    LOWPWR  */
+       [V1]    = {     1,      2,      0x7,    18,     0,      },
+       [V2]    = {     5,      6,      0x1,    19,     22,     },
+       [V3]    = {     7,      8,      0x7,    20,     23,     },
+       [V4]    = {     11,     12,     0x7,    21,     24,     },
+       [V5]    = {     15,     16,     0x3,    0xff,   0xff,   },
+       [V6]    = {     1,      0xff,   0x0,    0xff,   0xff,   },
+       /* FIXME: I have no idea of V7-V10 bits -WM */
+       [V7]    = {     0xff,   0xff,   0x0,    0xff,   0xff,   },
+       [V8]    = {     0xff,   0xff,   0x0,    0xff,   0xff,   },
+       [V9]    = {     0xff,   0xff,   0x0,    0xff,   0xff,   },
+       [V10]   = {     0xff,   0xff,   0x0,    0xff,   0xff,   },
+       [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_vreg(u8 vreg, u8 what, u8 val)
+{
+       struct pcap_platform_data *pdata = pcap.spi->dev.platform_data;
+       u8 reg, shift, mask;
+       u32 tmp;
+
+       switch (vreg) {
+       case V1 ... V5:
+               /* vreg1 is not accessible on port 2 */
+               if (pdata->config & PCAP_SECOND_PORT)
+                       return -EINVAL;
+               reg = PCAP_REG_VREG1;
+               break;
+       case V6 ... V10:
+               reg = PCAP_REG_VREG2;
+               break;
+       case VAUX1 ... VC:
+               if ((what == V_LOWPWR || what == V_STBY) && vreg != VAUX1)
+                       reg = PCAP_REG_LOWPWR;
+               else
+                       reg = PCAP_REG_AUXVREG;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (what) {
+       case V_VAL:
+               shift = vreg_table[vreg][V_VAL];
+               mask = vreg_table[vreg][V_MASK];
+               break;
+       case V_EN:
+       case V_STBY:
+       case V_LOWPWR:
+               shift = vreg_table[vreg][what];
+               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_vreg);
+
+/* ADC */
+void ezx_pcap_disable_adc(void)
+{
+       u32 tmp;
+
+       ezx_pcap_read(PCAP_REG_ADC, &tmp);
+       tmp &= ~(PCAP_ADC_ADEN|PCAP_ADC_BATT_I_ADC|PCAP_ADC_BATT_I_POLARITY);
+       tmp |= (PCAP_ADC_TS_M_STANDBY << PCAP_ADC_TS_M_SHIFT);
+       ezx_pcap_write(PCAP_REG_ADC, tmp);
+       mutex_unlock(&adc_lock);
+}
+
+static void ezx_pcap_adc_event(struct work_struct *unused)
+{
+       void (*adc_done)(void *);
+       void *adc_data;
+
+       if (!pcap.adc_done)
+               return;
+
+       adc_done = pcap.adc_done;
+       adc_data = pcap.adc_data;
+       pcap.adc_done = pcap.adc_data = NULL;
+
+       /* let caller get the results */
+       adc_done(adc_data);
+}
+
+void ezx_pcap_start_adc(u8 bank, u8 time, u32 flags,
+                                               void *adc_done, void *adc_data)
+{
+       u32 adc;
+       u32 adr;
+
+       mutex_lock(&adc_lock);
+
+       adc = flags | PCAP_ADC_ADEN;
+
+       if (bank == PCAP_ADC_BANK_1)
+               adc |= PCAP_ADC_AD_SEL1;
+
+       ezx_pcap_write(PCAP_REG_ADC, adc);
+
+       pcap.adc_done = adc_done;
+       pcap.adc_data = adc_data;
+
+       if (time == PCAP_ADC_T_NOW) {
+               ezx_pcap_read(PCAP_REG_ADR, &adr);
+               adr = PCAP_ADR_ASC;
+               ezx_pcap_write(PCAP_REG_ADR, adr);
+               return;
+       }
+
+       if (time == PCAP_ADC_T_IN_BURST)
+               adc |= (PCAP_ADC_ATO_IN_BURST << PCAP_ADC_ATO_SHIFT);
+
+       ezx_pcap_write(PCAP_REG_ADC, adc);
+
+       ezx_pcap_read(PCAP_REG_ADR, &adr);
+       adr &= ~PCAP_ADR_ONESHOT;
+       ezx_pcap_write(PCAP_REG_ADR, adr);
+       adr |= PCAP_ADR_ONESHOT;
+       ezx_pcap_write(PCAP_REG_ADR, adr);
+}
+EXPORT_SYMBOL_GPL(ezx_pcap_start_adc);
+
+void ezx_pcap_get_adc_channel_result(u8 ch1, u8 ch2, u32 res[])
+{
+       u32 tmp;
+
+       ezx_pcap_read(PCAP_REG_ADC, &tmp);
+       tmp &= ~(PCAP_ADC_ADA1_MASK | PCAP_ADC_ADA2_MASK);
+       tmp |= (ch1 << PCAP_ADC_ADA1_SHIFT) | (ch2 << PCAP_ADC_ADA2_SHIFT);
+       ezx_pcap_write(PCAP_REG_ADC, tmp);
+       ezx_pcap_read(PCAP_REG_ADR, &tmp);
+       res[0] = (tmp & PCAP_ADR_ADD1_MASK) >> PCAP_ADR_ADD1_SHIFT;
+       res[1] = (tmp & PCAP_ADR_ADD2_MASK) >> PCAP_ADR_ADD2_SHIFT;
+}
+EXPORT_SYMBOL_GPL(ezx_pcap_get_adc_channel_result);
+
+void ezx_pcap_get_adc_bank_result(u32 res[])
+{
+       int x;
+       u32 tmp[2];
+
+       for (x = 0; x < 7; x += 2) {
+               ezx_pcap_get_adc_channel_result(x, (x+1) % 6, tmp);
+               res[x] = tmp[0];
+               if ((x + 1) < 7)
+                       res[x+1] = tmp[1];
+               else
+                       res[x+1] = 0;
+       }
+}
+EXPORT_SYMBOL_GPL(ezx_pcap_get_adc_bank_result);
+
+static void adc_complete(void *data)
+{
+       complete(data);
+}
+
+void ezx_pcap_do_general_adc(u8 bank, u8 ch, u32 *res)
+{
+       u32 tmp[2];
+       DECLARE_COMPLETION_ONSTACK(done);
+
+       ezx_pcap_start_adc(bank, PCAP_ADC_T_NOW, 0, adc_complete, &done);
+       wait_for_completion(&done);
+       ezx_pcap_get_adc_channel_result(ch, 0, tmp);
+       ezx_pcap_disable_adc();
+
+       *res = tmp[0];
+}
+EXPORT_SYMBOL_GPL(ezx_pcap_do_general_adc);
+
+/* event handling */
+static irqreturn_t pcap_irq_handler(int irq, void *dev_id)
+{
+       queue_work(pcap.workqueue, &pcap.work);
+       return IRQ_HANDLED;
+}
+
+static void pcap_work(struct work_struct *_pcap)
+{
+       u32 msr;
+       u32 isr;
+       u32 service;
+       struct pcap_event *cb;
+
+       mutex_lock(&event_lock);
+       ezx_pcap_read(PCAP_REG_MSR, &msr);
+       ezx_pcap_read(PCAP_REG_ISR, &isr);
+       isr &= ~msr;
+
+       list_for_each_entry(cb, &event_list, node) {
+               service = isr & cb->events;
+               if (service) {
+                       ezx_pcap_write(PCAP_REG_ISR, service);
+                       cb->callback(service, cb->data);
+               }
+       }
+       mutex_unlock(&event_lock);
+}
+
+void ezx_pcap_mask_event(u32 events)
+{
+       u32 msr;
+
+       ezx_pcap_read(PCAP_REG_MSR, &msr);
+       msr |= events;
+       ezx_pcap_write(PCAP_REG_MSR, msr);
+}
+EXPORT_SYMBOL_GPL(ezx_pcap_mask_event);
+
+void ezx_pcap_unmask_event(u32 events)
+{
+       u32 msr;
+
+       ezx_pcap_read(PCAP_REG_MSR, &msr);
+       msr &= ~events;
+       ezx_pcap_write(PCAP_REG_MSR, msr);
+}
+EXPORT_SYMBOL_GPL(ezx_pcap_unmask_event);
+
+int ezx_pcap_register_event(u32 events, void *callback, char *label)
+{
+       struct pcap_event *cb;
+
+       cb = kzalloc(sizeof(struct pcap_event), GFP_KERNEL);
+       if (!cb)
+               return -ENOMEM;
+
+       cb->label = label;
+       cb->events = events;
+       cb->callback = callback;
+
+       mutex_lock(&event_lock);
+       list_add_tail(&cb->node, &event_list);
+       mutex_unlock(&event_lock);
+
+       ezx_pcap_unmask_event(events);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ezx_pcap_register_event);
+
+int ezx_pcap_unregister_event(u32 events)
+{
+       int ret = -EINVAL;
+       struct pcap_event *cb;
+       struct pcap_event *store;
+
+       ezx_pcap_mask_event(events);
+
+       mutex_lock(&event_lock);
+       list_for_each_entry_safe(cb, store, &event_list, node) {
+               if (cb->events & events) {
+                       list_del(&cb->node);
+                       kfree(cb);
+                       ret = 0;
+               }
+       }
+       mutex_unlock(&event_lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(ezx_pcap_unregister_event);
+
+/* sysfs interface */
+static ssize_t pcap_show_regs(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       unsigned int reg, val;
+       char *p = buf;
+
+       for (reg = 0; reg < 32; reg++) {
+               ezx_pcap_read(reg, &val);
+               p += sprintf(p, "%02d %08x\n", reg, val);
+       }
+       return p - buf;
+}
+
+static ssize_t pcap_store_regs(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       unsigned int reg, val;
+       char *p = (char *)buf;
+
+       while (p < (buf + size)) {
+               if ((sscanf(p, "%u %x\n", &reg, &val) != 2) ||
+                       reg < 0 || reg >= 32)
+                       return -EINVAL;
+               p = strchr(p, '\n') + 1;
+       }
+
+       p = (char *)buf;
+       while (p < (buf + size)) {
+               sscanf(p, "%u %x\n", &reg, &val);
+               ezx_pcap_write(reg, val);
+               p = strchr(p, '\n') + 1;
+       }
+
+       return size;
+}
+
+static ssize_t pcap_show_adc_coin(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       u32 res;
+
+       ezx_pcap_do_general_adc(PCAP_ADC_BANK_0, PCAP_ADC_CH_COIN, &res);
+       return sprintf(buf, "%d\n", res);
+}
+static ssize_t pcap_show_adc_battery(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       u32 res;
+
+       ezx_pcap_do_general_adc(PCAP_ADC_BANK_0, PCAP_ADC_CH_BATT, &res);
+       return sprintf(buf, "%d\n", res);
+}
+static ssize_t pcap_show_adc_bplus(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       u32 res;
+
+       ezx_pcap_do_general_adc(PCAP_ADC_BANK_0, PCAP_ADC_CH_BPLUS, &res);
+       return sprintf(buf, "%d\n", res);
+}
+static ssize_t pcap_show_adc_mobportb(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       u32 res;
+
+       ezx_pcap_do_general_adc(PCAP_ADC_BANK_0, PCAP_ADC_CH_MOBPORTB, &res);
+       return sprintf(buf, "%d\n", res);
+}
+static ssize_t pcap_show_adc_temperature(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       u32 res;
+
+       ezx_pcap_do_general_adc(PCAP_ADC_BANK_0, PCAP_ADC_CH_TEMPERATURE, &res);
+       return sprintf(buf, "%d\n", res);
+}
+static ssize_t pcap_show_adc_chargerid(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       u32 res;
+
+       ezx_pcap_do_general_adc(PCAP_ADC_BANK_0, PCAP_ADC_CH_CHARGER_ID, &res);
+       return sprintf(buf, "%d\n", res);
+}
+
+static DEVICE_ATTR(regs, 0600, pcap_show_regs, pcap_store_regs);
+static DEVICE_ATTR(adc_coin, 0400, pcap_show_adc_coin, NULL);
+static DEVICE_ATTR(adc_battery, 0400, pcap_show_adc_battery, NULL);
+static DEVICE_ATTR(adc_bplus, 0400, pcap_show_adc_bplus, NULL);
+static DEVICE_ATTR(adc_mobportb, 0400, pcap_show_adc_mobportb, NULL);
+static DEVICE_ATTR(adc_temperature, 0400, pcap_show_adc_temperature, NULL);
+static DEVICE_ATTR(adc_chargerid, 0400, pcap_show_adc_chargerid, NULL);
+
+static int ezx_pcap_setup_sysfs(int create)
+{
+       int ret = 0;
+
+       if (!create)
+               goto remove_all;
+
+       ret = device_create_file(&pcap.spi->dev, &dev_attr_regs);
+       if (ret)
+               goto ret;
+       ret = device_create_file(&pcap.spi->dev, &dev_attr_adc_coin);
+       if (ret)
+               goto fail1;
+       ret = device_create_file(&pcap.spi->dev, &dev_attr_adc_battery);
+       if (ret)
+               goto fail2;
+       ret = device_create_file(&pcap.spi->dev, &dev_attr_adc_bplus);
+       if (ret)
+               goto fail3;
+       ret = device_create_file(&pcap.spi->dev, &dev_attr_adc_mobportb);
+       if (ret)
+               goto fail4;
+       ret = device_create_file(&pcap.spi->dev, &dev_attr_adc_temperature);
+       if (ret)
+               goto fail5;
+       ret = device_create_file(&pcap.spi->dev, &dev_attr_adc_chargerid);
+       if (ret)
+               goto fail6;
+
+       goto ret;
+
+remove_all:
+fail6: device_remove_file(&pcap.spi->dev, &dev_attr_adc_temperature);
+fail5: device_remove_file(&pcap.spi->dev, &dev_attr_adc_mobportb);
+fail4: device_remove_file(&pcap.spi->dev, &dev_attr_adc_bplus);
+fail3: device_remove_file(&pcap.spi->dev, &dev_attr_adc_battery);
+fail2: device_remove_file(&pcap.spi->dev, &dev_attr_adc_coin);
+fail1: device_remove_file(&pcap.spi->dev, &dev_attr_regs);
+ret:   return ret;
+}
+
+static int ezx_pcap_remove(struct spi_device *spi)
+{
+       struct pcap_platform_data *pdata = spi->dev.platform_data;
+
+       ezx_pcap_setup_sysfs(0);
+       destroy_workqueue(pcap.workqueue);
+       ezx_pcap_unregister_event(PCAP_MASK_ALL_INTERRUPT);
+       free_irq(pdata->irq, NULL);
+
+       return 0;
+}
+
+static int __devinit ezx_pcap_probe(struct spi_device *spi)
+{
+       struct pcap_platform_data *pdata = spi->dev.platform_data;
+       int ret = -ENODEV;
+
+       if (!pdata)
+               goto ret;
+
+       pcap.spi = spi;
+
+       INIT_WORK(&pcap.work, pcap_work);
+       pcap.workqueue = create_singlethread_workqueue("pcapd");
+       if (!pcap.workqueue) {
+               dev_err(&spi->dev, "cant create pcap thread\n");
+               goto ret;
+       }
+
+       /* redirect interrupts to AP */
+       if (!(pdata->config & PCAP_SECOND_PORT))
+               ezx_pcap_write(PCAP_REG_INT_SEL, PCAP_IRQ_ADCDONE2);
+
+       /* set board-specific settings */
+       if (pdata->init)
+               pdata->init();
+
+       ret = ezx_pcap_setup_sysfs(1);
+       if (ret) {
+               dev_err(&spi->dev, "cant create sysfs files\n");
+               goto wq_destroy;
+       }
+
+       /* 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);
+
+       /* register irq for pcap */
+       ret = request_irq(pdata->irq, pcap_irq_handler, IRQF_DISABLED,
+               "PCAP", NULL);
+       if (ret) {
+               dev_err(&spi->dev, "cant request IRQ\n");
+               goto wq_destroy;
+       }
+       set_irq_type(pdata->irq, IRQ_TYPE_EDGE_RISING);
+       set_irq_wake(pdata->irq, 1);
+
+       ezx_pcap_register_event((pdata->config & PCAP_SECOND_PORT) ?
+                       PCAP_IRQ_ADCDONE2 : PCAP_IRQ_ADCDONE,
+                       ezx_pcap_adc_event, "ADC");
+       return 0;
+
+wq_destroy:
+       destroy_workqueue(pcap.workqueue);
+ret:
+       return ret;
+}
+
+static struct spi_driver ezxpcap_driver = {
+       .probe  = ezx_pcap_probe,
+       .remove = ezx_pcap_remove,
+       .driver = {
+               .name   = "ezx-pcap",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init ezx_pcap_init(void)
+{
+       return spi_register_driver(&ezxpcap_driver);
+}
+
+static void __exit ezx_pcap_exit(void)
+{
+       spi_unregister_driver(&ezxpcap_driver);
+}
+
+module_init(ezx_pcap_init);
+module_exit(ezx_pcap_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Daniel Ribeiro / Harald Welte");
+MODULE_DESCRIPTION("Motorola PCAP2 ASIC Driver");
diff --git a/include/linux/mfd/ezx-pcap.h b/include/linux/mfd/ezx-pcap.h
new file mode 100644
index 0000000..e046ee9
--- /dev/null
+++ b/include/linux/mfd/ezx-pcap.h
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2007 Daniel Ribeiro <[EMAIL PROTECTED]>
+ *
+ * For further information, please see http://wiki.openezx.org/PCAP2
+ */
+
+#ifndef EZX_PCAP_H
+#define EZX_PCAP_H
+
+struct pcap_platform_data {
+       unsigned int irq;
+       unsigned int config;
+       void (*init) (void);    /* board specific init */
+};
+
+#define PCAP_SECOND_PORT       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
+
+/* 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 */
+#define PCAP_REG_TEST1         0x1c
+#define PCAP_REG_TEST2         0x1d
+#define PCAP_REG_VENDOR_TEST1  0x1e
+#define PCAP_REG_VENDOR_TEST2  0x1f
+
+/* 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_DUMMY         (1 << 19)
+#define PCAP_IRQ_ADCDONE2      (1 << 20)       /* AD Conversion Done Port 2 */
+#define PCAP_IRQ_SOFTRESET     (1 << 21)
+#define PCAP_IRQ_MNEXB         (1 << 22)
+
+/* voltage regulators */
+#define V1             0
+#define V2             1
+#define V3             2
+#define V4             3
+#define V5             4
+#define V6             5
+#define V7             6
+#define V8             7
+#define V9             8
+#define V10            9
+#define VAUX1          10
+#define VAUX2          11
+#define VAUX3          12
+#define VAUX4          13
+#define VSIM           14
+#define VSIM2          15
+#define VVIB           16
+#define VC             17
+
+#define V_EN           0
+#define V_VAL          1
+#define V_MASK         2
+#define V_STBY         3
+#define V_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_ADC_BANK_0                        0
+#define PCAP_ADC_BANK_1                        1
+/* ADC bank 0 */
+#define PCAP_ADC_CH_COIN               0
+#define PCAP_ADC_CH_BATT               1
+#define PCAP_ADC_CH_BPLUS              2
+#define PCAP_ADC_CH_MOBPORTB           3
+#define PCAP_ADC_CH_TEMPERATURE                4
+#define PCAP_ADC_CH_CHARGER_ID         5
+#define PCAP_ADC_CH_AD6                        6
+/* ADC bank 1 */
+#define PCAP_ADC_CH_AD7                        0
+#define PCAP_ADC_CH_AD8                        1
+#define PCAP_ADC_CH_AD9                        2
+#define PCAP_ADC_CH_TS_X1              3
+#define PCAP_ADC_CH_TS_X2              4
+#define PCAP_ADC_CH_TS_Y1              5
+#define PCAP_ADC_CH_TS_Y2              6
+
+#define PCAP_ADC_T_NOW                 0
+#define PCAP_ADC_T_IN_BURST            1
+#define PCAP_ADC_T_OUT_BURST           2
+
+#define PCAP_ADC_ATO_IN_BURST          6
+#define PCAP_ADC_ATO_OUT_BURST         0
+
+#define PCAP_ADC_TS_M_XY               1
+#define PCAP_ADC_TS_M_PRESSURE         2
+#define PCAP_ADC_TS_M_PLATE_X          3
+#define PCAP_ADC_TS_M_PLATE_Y          4
+#define PCAP_ADC_TS_M_STANDBY          5
+#define PCAP_ADC_TS_M_NONTS            6
+
+#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)
+
+/* leds */
+#define PCAP_LED0              0
+#define PCAP_LED1              1
+#define PCAP_BL0               2
+#define PCAP_BL1               3
+#define PCAP_LED_3MA           0
+#define PCAP_LED_4MA           1
+#define PCAP_LED_5MA           2
+#define PCAP_LED_9MA           3
+#define PCAP_LED_GPIO_VAL_MASK 0x00ffffff
+#define PCAP_LED_GPIO_EN       0x01000000
+#define PCAP_LED_GPIO_INVERT   0x02000000
+#define PCAP_LED_T_MASK                0xf
+#define PCAP_LED_C_MASK                0x3
+#define PCAP_BL_MASK           0x1f
+#define PCAP_BL0_SHIFT         0
+#define PCAP_LED0_EN           (1 << 5)
+#define PCAP_LED1_EN           (1 << 6)
+#define PCAP_LED0_T_SHIFT      7
+#define PCAP_LED1_T_SHIFT      11
+#define PCAP_LED0_C_SHIFT      15
+#define PCAP_LED1_C_SHIFT      17
+#define PCAP_BL1_SHIFT         20
+
+/* RTC */
+#define PCAP_RTC_DAY_MASK      0x3fff
+#define PCAP_RTC_TOD_MASK      0xffff
+#define PCAP_RTC_PC_MASK       0x7
+#define SEC_PER_DAY            86400
+
+/* 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
+
+int ezx_pcap_write(u8, u32);
+int ezx_pcap_read(u8, u32 *);
+int ezx_pcap_set_sw(u8, u8, u8);
+int ezx_pcap_set_vreg(u8, u8, u8);
+void ezx_pcap_start_adc(u8, u8, u32, void *, void *);
+void ezx_pcap_get_adc_channel_result(u8, u8, u32[]);
+void ezx_pcap_get_adc_bank_result(u32[]);
+void ezx_pcap_disable_adc(void);
+void ezx_pcap_do_general_adc(u8, u8, u32 *);
+void ezx_pcap_do_batt_adc(int, u32[]);
+int ezx_pcap_register_event(u32, void *, char *);
+int ezx_pcap_unregister_event(u32);
+void ezx_pcap_mask_event(u32);
+void ezx_pcap_unmask_event(u32);
+
+struct pcap_event {
+       struct list_head node;
+       char *label;
+       u32 events;
+       void (*callback) (u32, void *);
+       void *data;
+};
+
+#endif
-- 
tg: (7f0f598..) ezx/pcap (depends on: master)

-- 

-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
spi-devel-general mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/spi-devel-general

Reply via email to