lkml.org 
[lkml]   [2008]   [Nov]   [21]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[patch 05/14] mfd: PCAP2 driver
    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 | 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 <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>
    +
    +#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 <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 *, 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)
    --


    \
     
     \ /
      Last update: 2008-11-21 17:47    [W:0.073 / U:30.472 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site