From: Daniel Ribeiro <drwyrm-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
To: David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
Cc: eric.y.miao-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org,
sameo-RWuK6r/cQWRpLGFMi4vTTA@public.gmane.org,
linux-arm-kernel-xIg/pKzrS19vn6HldHNs0ANdhmdF6hFW@public.gmane.org,
stefan-OrPQZGeq07wqhVmZOOOmNx2eb7JE58TQ@public.gmane.org
Subject: Re: [patch 05/14] mfd: PCAP2 driver
Date: Sat, 22 Nov 2008 15:12:28 -0200 [thread overview]
Message-ID: <1227373948.19591.66.camel@brutus> (raw)
In-Reply-To: <200811212125.49068.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 561 bytes --]
Em Sex, 2008-11-21 às 21:25 -0800, David Brownell escreveu:
> I took a quick glance at this and it seemed like it should be
> able to build without depending on PXA ... should certainly
> not include <mach/ssp.h> or <mach/regs-ssp.h>, and it doesn't
> look like it needs PXA-specific stuff like <mach/mfp-pxa27x.h>
> either.
>
> It's also worth removing the reverse dependencies ("select X")
> from Kconfig; they don't work very well for the things which
> those dependencies rely on.
Above comments are integrated on the attached patch.
--
Daniel Ribeiro
[-- Attachment #2: pcap.diff --]
[-- Type: message/rfc822, Size: 26798 bytes --]
From: Daniel Ribeiro <drwyrm-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
To: sameo-RWuK6r/cQWRpLGFMi4vTTA@public.gmane.org
Cc: linux-arm-kernel-xIg/pKzrS19vn6HldHNs0ANdhmdF6hFW@public.gmane.org, eric.y.miao-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org, linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
Subject: mfd: PCAP driver
Date: Sat, 22 Nov 2008 15:11:49 -0200
Message-ID: <1227373909.19591.65.camel@brutus>
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 <drwyrm-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
arch/arm/mach-pxa/Kconfig | 1 +
drivers/mfd/Kconfig | 7 +
drivers/mfd/Makefile | 2 +
drivers/mfd/ezx-pcap.c | 591 ++++++++++++++++++++++++++++++++++++++++++
include/linux/mfd/ezx-pcap.h | 290 +++++++++++++++++++++
5 files changed, 891 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..79566c2 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -153,6 +153,13 @@ 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
+ 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..939f2ac
--- /dev/null
+++ b/drivers/mfd/ezx-pcap.c
@@ -0,0 +1,591 @@
+/*
+ * Driver for Motorola PCAP2 as present in EZX phones
+ *
+ * Copyright (C) 2006 Harald Welte <laforge-WB6LKoYH/xlAfugRpC6u6w@public.gmane.org>
+ * Copyright (C) 2007-2008 Daniel Ribeiro <drwyrm-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ *
+ */
+
+#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>
+
+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(u32 flags, void *data)
+{
+ 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, void *data, 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;
+ cb->data = data;
+
+ 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", ®, &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", ®, &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, NULL, "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..352ac35
--- /dev/null
+++ b/include/linux/mfd/ezx-pcap.h
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2007 Daniel Ribeiro <drwyrm-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ *
+ * 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 *, 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: (ed31348..) ezx/pcap (depends on: master)
[-- Attachment #3: Type: text/plain, Size: 363 bytes --]
-------------------------------------------------------------------------
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=/
[-- Attachment #4: Type: text/plain, Size: 210 bytes --]
_______________________________________________
spi-devel-general mailing list
spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
https://lists.sourceforge.net/lists/listinfo/spi-devel-general
WARNING: multiple messages have this Message-ID (diff)
From: Daniel Ribeiro <drwyrm@gmail.com>
To: David Brownell <david-b@pacbell.net>
Cc: spi-devel-general@lists.sourceforge.net,
stefan@datenfreihafen.org, eric.y.miao@gmail.com,
sameo@openedhand.com, linux-arm-kernel@lists.arm.linux.org.uk,
linux-kernel@vger.kernel.org
Subject: Re: [spi-devel-general] [patch 05/14] mfd: PCAP2 driver
Date: Sat, 22 Nov 2008 15:12:28 -0200 [thread overview]
Message-ID: <1227373948.19591.66.camel@brutus> (raw)
In-Reply-To: <200811212125.49068.david-b@pacbell.net>
[-- Attachment #1: Type: text/plain, Size: 561 bytes --]
Em Sex, 2008-11-21 às 21:25 -0800, David Brownell escreveu:
> I took a quick glance at this and it seemed like it should be
> able to build without depending on PXA ... should certainly
> not include <mach/ssp.h> or <mach/regs-ssp.h>, and it doesn't
> look like it needs PXA-specific stuff like <mach/mfp-pxa27x.h>
> either.
>
> It's also worth removing the reverse dependencies ("select X")
> from Kconfig; they don't work very well for the things which
> those dependencies rely on.
Above comments are integrated on the attached patch.
--
Daniel Ribeiro
[-- Attachment #2: pcap.diff --]
[-- Type: message/rfc822, Size: 26515 bytes --]
From: Daniel Ribeiro <drwyrm@gmail.com>
To: sameo@openedhand.com
Cc: linux-arm-kernel@lists.arm.linux.org.uk, eric.y.miao@gmail.com, linux-kernel@vger.kernel.org, spi-devel-general@lists.sourceforge.net
Subject: mfd: PCAP driver
Date: Sat, 22 Nov 2008 15:11:49 -0200
Message-ID: <1227373909.19591.65.camel@brutus>
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 <drwyrm@gmail.com>
---
arch/arm/mach-pxa/Kconfig | 1 +
drivers/mfd/Kconfig | 7 +
drivers/mfd/Makefile | 2 +
drivers/mfd/ezx-pcap.c | 591 ++++++++++++++++++++++++++++++++++++++++++
include/linux/mfd/ezx-pcap.h | 290 +++++++++++++++++++++
5 files changed, 891 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..79566c2 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -153,6 +153,13 @@ 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
+ 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..939f2ac
--- /dev/null
+++ b/drivers/mfd/ezx-pcap.c
@@ -0,0 +1,591 @@
+/*
+ * Driver for Motorola PCAP2 as present in EZX phones
+ *
+ * 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/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>
+
+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(u32 flags, void *data)
+{
+ 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, void *data, 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;
+ cb->data = data;
+
+ 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", ®, &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", ®, &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, NULL, "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..352ac35
--- /dev/null
+++ b/include/linux/mfd/ezx-pcap.h
@@ -0,0 +1,290 @@
+/*
+ * 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 {
+ 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 *, 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: (ed31348..) ezx/pcap (depends on: master)
next prev parent reply other threads:[~2008-11-22 17:12 UTC|newest]
Thread overview: 41+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <20081121160403.073751031@dodger.lab.datenfreihafen.org>
2008-11-21 16:04 ` [patch 05/14] mfd: PCAP2 driver stefan-OrPQZGeq07wqhVmZOOOmNx2eb7JE58TQ
2008-11-21 16:04 ` stefan
[not found] ` <20081121160521.016544616-cQQG9CVUopzFITZdfPi31ZcF1vblOVnWhIvA6WVW+J8@public.gmane.org>
2008-11-22 5:25 ` David Brownell
2008-11-22 5:25 ` [spi-devel-general] " David Brownell
2008-11-22 14:01 ` Eric Miao
[not found] ` <f17812d70811220601p1d7af668mf3265224179753ab-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2008-11-22 15:54 ` Daniel Ribeiro
2008-11-22 15:54 ` [spi-devel-general] " Daniel Ribeiro
2008-11-22 19:08 ` David Brownell
2008-11-22 19:08 ` [spi-devel-general] " David Brownell
[not found] ` <200811221108.54331.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-11-22 23:29 ` Stefan Schmidt
2008-11-22 23:29 ` [spi-devel-general] " Stefan Schmidt
[not found] ` <200811212125.49068.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-11-22 17:12 ` Daniel Ribeiro [this message]
2008-11-22 17:12 ` Daniel Ribeiro
2008-11-22 19:19 ` David Brownell
2008-11-22 19:19 ` [spi-devel-general] " David Brownell
[not found] ` <200811221119.27981.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-11-22 23:33 ` Stefan Schmidt
2008-11-22 23:33 ` [spi-devel-general] " Stefan Schmidt
[not found] ` <20081122233356.GC24437-OrPQZGeq07wqhVmZOOOmNx2eb7JE58TQ@public.gmane.org>
2008-11-22 23:58 ` David Brownell
2008-11-22 23:58 ` [spi-devel-general] " David Brownell
[not found] ` <200811221558.03915.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-11-23 0:33 ` Stefan Schmidt
2008-11-23 0:33 ` [spi-devel-general] " Stefan Schmidt
[not found] ` <20081123003306.GE24437-OrPQZGeq07wqhVmZOOOmNx2eb7JE58TQ@public.gmane.org>
2008-11-23 2:19 ` David Brownell
2008-11-23 2:19 ` [spi-devel-general] " David Brownell
[not found] ` <200811221819.53186.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-11-23 3:38 ` Daniel Ribeiro
2008-11-23 3:38 ` [spi-devel-general] " Daniel Ribeiro
2008-11-23 4:59 ` David Brownell
2008-11-23 4:59 ` [spi-devel-general] " David Brownell
[not found] ` <200811222059.59806.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-11-23 7:06 ` Daniel Ribeiro
2008-11-23 7:06 ` [spi-devel-general] " Daniel Ribeiro
2008-11-23 8:26 ` David Brownell
2008-11-23 8:26 ` [spi-devel-general] " David Brownell
2008-11-21 16:04 ` [patch 08/14] input: PCAP2 based touchscreen driver stefan
2008-11-21 16:04 ` stefan
2008-11-21 17:05 ` Dmitry Torokhov
2008-11-22 0:00 ` Stefan Schmidt
2009-04-21 2:18 ` Dmitry Torokhov
2009-04-21 2:38 ` Daniel Ribeiro
2009-04-21 2:38 ` Daniel Ribeiro
2008-11-21 16:04 ` [patch 10/14] LED: PCAP2 based LED driver stefan
2008-11-22 14:08 ` Eric Miao
2008-11-22 15:46 ` Daniel Ribeiro
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1227373948.19591.66.camel@brutus \
--to=drwyrm-re5jqeeqqe8avxtiumwx3w@public.gmane.org \
--cc=david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org \
--cc=eric.y.miao-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org \
--cc=linux-arm-kernel-xIg/pKzrS19vn6HldHNs0ANdhmdF6hFW@public.gmane.org \
--cc=linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
--cc=sameo-RWuK6r/cQWRpLGFMi4vTTA@public.gmane.org \
--cc=spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org \
--cc=stefan-OrPQZGeq07wqhVmZOOOmNx2eb7JE58TQ@public.gmane.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.