mfd: cpcap: Add CPCAP drivers
authorGreg Meiste <w30289@motorola.com>
Thu, 13 May 2010 22:04:35 +0000 (17:04 -0500)
committerColin Cross <ccross@android.com>
Wed, 6 Oct 2010 23:32:56 +0000 (16:32 -0700)
Add driver for motorola's CPCAP PMIC.  This includes the core in mfd,
the rtc driver, and regulator drivers.

Change-Id: I96e3a97673002f3264ae8a71f5c8db1fcb7846e3
Signed-off-by: Greg Meiste <w30289@motorola.com>
21 files changed:
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/cpcap-3mm5.c [new file with mode: 0644]
drivers/mfd/cpcap-adc.c [new file with mode: 0644]
drivers/mfd/cpcap-core.c [new file with mode: 0644]
drivers/mfd/cpcap-irq.c [new file with mode: 0644]
drivers/mfd/cpcap-key.c [new file with mode: 0644]
drivers/mfd/cpcap-regacc.c [new file with mode: 0644]
drivers/mfd/cpcap-uc.c [new file with mode: 0644]
drivers/mfd/cpcap-whisper.c [new file with mode: 0644]
drivers/regulator/Kconfig
drivers/regulator/Makefile
drivers/regulator/cpcap-regulator.c [new file with mode: 0644]
drivers/rtc/Kconfig
drivers/rtc/Makefile
drivers/rtc/rtc-cpcap.c [new file with mode: 0644]
firmware/Makefile
firmware/cpcap/firmware_0_2x.HEX [new file with mode: 0644]
firmware/cpcap/firmware_1_2x.H16 [new file with mode: 0644]
include/linux/spi/cpcap-regbits.h [new file with mode: 0644]
include/linux/spi/cpcap.h [new file with mode: 0644]

index 0d762688effef53642da674519fbfebe830c6388..d2924a2cf3cdca63b2431cefd6cb8241f1fe9fbc 100644 (file)
@@ -523,6 +523,12 @@ config MFD_RDC321X
          southbridge which provides access to GPIOs and Watchdog using the
          southbridge PCI device configuration space.
 
          southbridge which provides access to GPIOs and Watchdog using the
          southbridge PCI device configuration space.
 
+config MFD_CPCAP
+       tristate "Support for CPCAP"
+       depends on SPI && FIRMWARE_IN_KERNEL
+       help
+        Say yes here if you want to include drivers for the CPCAP chip.
+
 config MFD_JANZ_CMODIO
        tristate "Support for Janz CMOD-IO PCI MODULbus Carrier Board"
        select MFD_CORE
 config MFD_JANZ_CMODIO
        tristate "Support for Janz CMOD-IO PCI MODULbus Carrier Board"
        select MFD_CORE
index feaeeaeeddb7eb924989cf40ebb63f665e7d9418..713f8176d5068824b246f4e340ac145b3c1bd7ae 100644 (file)
@@ -76,3 +76,14 @@ obj-$(CONFIG_MFD_RDC321X)    += rdc321x-southbridge.o
 obj-$(CONFIG_MFD_JANZ_CMODIO)  += janz-cmodio.o
 obj-$(CONFIG_MFD_JZ4740_ADC)   += jz4740-adc.o
 obj-$(CONFIG_MFD_TPS6586X)     += tps6586x.o
 obj-$(CONFIG_MFD_JANZ_CMODIO)  += janz-cmodio.o
 obj-$(CONFIG_MFD_JZ4740_ADC)   += jz4740-adc.o
 obj-$(CONFIG_MFD_TPS6586X)     += tps6586x.o
+
+cpcap-objs                     := cpcap-core.o \
+                                  cpcap-irq.o \
+                                  cpcap-regacc.o \
+                                  cpcap-key.o \
+                                  cpcap-whisper.o \
+                                  cpcap-adc.o \
+                                  cpcap-uc.o \
+                                  cpcap-3mm5.o
+
+obj-$(CONFIG_MFD_CPCAP)                += cpcap.o
diff --git a/drivers/mfd/cpcap-3mm5.c b/drivers/mfd/cpcap-3mm5.c
new file mode 100644 (file)
index 0000000..68923cf
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2009 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/switch.h>
+#include <linux/workqueue.h>
+
+#include <linux/regulator/consumer.h>
+
+#include <linux/spi/cpcap.h>
+#include <linux/spi/cpcap-regbits.h>
+#include <linux/spi/spi.h>
+
+enum {
+       NO_DEVICE,
+       HEADSET_WITH_MIC,
+       HEADSET_WITHOUT_MIC,
+};
+
+struct cpcap_3mm5_data {
+       struct cpcap_device *cpcap;
+       struct switch_dev sdev;
+       unsigned int key_state;
+       struct regulator *regulator;
+       unsigned char audio_low_pwr_det;
+       unsigned char audio_low_pwr_mac13;
+       struct delayed_work work;
+};
+
+static ssize_t print_name(struct switch_dev *sdev, char *buf)
+{
+       switch (switch_get_state(sdev)) {
+       case NO_DEVICE:
+               return sprintf(buf, "No Device\n");
+       case HEADSET_WITH_MIC:
+               return sprintf(buf, "Headset with mic\n");
+       case HEADSET_WITHOUT_MIC:
+               return sprintf(buf, "Headset without mic\n");
+       }
+
+       return -EINVAL;
+}
+
+static void audio_low_power_set(struct cpcap_3mm5_data *data,
+                               unsigned char *flag)
+{
+       if (!(*flag)) {
+               regulator_set_mode(data->regulator, REGULATOR_MODE_STANDBY);
+               *flag = 1;
+       }
+}
+
+static void audio_low_power_clear(struct cpcap_3mm5_data *data,
+                                 unsigned char *flag)
+{
+       if (*flag) {
+               regulator_set_mode(data->regulator, REGULATOR_MODE_NORMAL);
+               *flag = 0;
+       }
+}
+
+static void send_key_event(struct cpcap_3mm5_data *data, unsigned int state)
+{
+       dev_info(&data->cpcap->spi->dev, "Headset key event: old=%d, new=%d\n",
+                data->key_state, state);
+
+       if (data->key_state != state) {
+               data->key_state = state;
+               cpcap_broadcast_key_event(data->cpcap, KEY_MEDIA, state);
+       }
+}
+
+static void hs_handler(enum cpcap_irqs irq, void *data)
+{
+       struct cpcap_3mm5_data *data_3mm5 = data;
+       int new_state = NO_DEVICE;
+
+       if (irq != CPCAP_IRQ_HS)
+               return;
+
+       /* HS sense of 1 means no headset present, 0 means headset attached. */
+       if (cpcap_irq_sense(data_3mm5->cpcap, CPCAP_IRQ_HS, 1) == 1) {
+               cpcap_regacc_write(data_3mm5->cpcap, CPCAP_REG_TXI, 0,
+                                  (CPCAP_BIT_MB_ON2 | CPCAP_BIT_PTT_CMP_EN));
+               cpcap_regacc_write(data_3mm5->cpcap, CPCAP_REG_RXOA, 0,
+                                  CPCAP_BIT_ST_HS_CP_EN);
+               audio_low_power_set(data_3mm5, &data_3mm5->audio_low_pwr_det);
+
+               cpcap_irq_mask(data_3mm5->cpcap, CPCAP_IRQ_MB2);
+               cpcap_irq_mask(data_3mm5->cpcap, CPCAP_IRQ_UC_PRIMACRO_5);
+
+               cpcap_irq_clear(data_3mm5->cpcap, CPCAP_IRQ_MB2);
+               cpcap_irq_clear(data_3mm5->cpcap, CPCAP_IRQ_UC_PRIMACRO_5);
+
+               cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_HS);
+
+               send_key_event(data_3mm5, 0);
+
+               cpcap_uc_stop(data_3mm5->cpcap, CPCAP_MACRO_5);
+       } else {
+               cpcap_regacc_write(data_3mm5->cpcap, CPCAP_REG_TXI,
+                                  (CPCAP_BIT_MB_ON2 | CPCAP_BIT_PTT_CMP_EN),
+                                  (CPCAP_BIT_MB_ON2 | CPCAP_BIT_PTT_CMP_EN));
+               cpcap_regacc_write(data_3mm5->cpcap, CPCAP_REG_RXOA,
+                                  CPCAP_BIT_ST_HS_CP_EN,
+                                  CPCAP_BIT_ST_HS_CP_EN);
+               audio_low_power_clear(data_3mm5, &data_3mm5->audio_low_pwr_det);
+
+               /* Give PTTS time to settle */
+               mdelay(2);
+
+               if (cpcap_irq_sense(data_3mm5->cpcap, CPCAP_IRQ_PTT, 1) <= 0) {
+                       /* Headset without mic and MFB is detected. (May also
+                        * be a headset with the MFB pressed.) */
+                       new_state = HEADSET_WITHOUT_MIC;
+               } else
+                       new_state = HEADSET_WITH_MIC;
+
+               cpcap_irq_clear(data_3mm5->cpcap, CPCAP_IRQ_MB2);
+               cpcap_irq_clear(data_3mm5->cpcap, CPCAP_IRQ_UC_PRIMACRO_5);
+
+               cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_HS);
+               cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_MB2);
+               cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_UC_PRIMACRO_5);
+
+               cpcap_uc_start(data_3mm5->cpcap, CPCAP_MACRO_5);
+       }
+
+       switch_set_state(&data_3mm5->sdev, new_state);
+       if (data_3mm5->cpcap->h2w_new_state)
+               data_3mm5->cpcap->h2w_new_state(new_state);
+
+       dev_info(&data_3mm5->cpcap->spi->dev, "New headset state: %d\n",
+                new_state);
+}
+
+static void key_handler(enum cpcap_irqs irq, void *data)
+{
+       struct cpcap_3mm5_data *data_3mm5 = data;
+
+       if ((irq != CPCAP_IRQ_MB2) && (irq != CPCAP_IRQ_UC_PRIMACRO_5))
+               return;
+
+       if ((cpcap_irq_sense(data_3mm5->cpcap, CPCAP_IRQ_HS, 1) == 1) ||
+           (switch_get_state(&data_3mm5->sdev) != HEADSET_WITH_MIC)) {
+               hs_handler(CPCAP_IRQ_HS, data_3mm5);
+               return;
+       }
+
+       if ((cpcap_irq_sense(data_3mm5->cpcap, CPCAP_IRQ_MB2, 0) == 0) ||
+           (cpcap_irq_sense(data_3mm5->cpcap, CPCAP_IRQ_PTT, 0) == 0)) {
+               send_key_event(data_3mm5, 1);
+
+               /* If macro not available, only short presses are supported */
+               if (!cpcap_uc_status(data_3mm5->cpcap, CPCAP_MACRO_5)) {
+                       send_key_event(data_3mm5, 0);
+
+                       /* Attempt to restart the macro for next time. */
+                       cpcap_uc_start(data_3mm5->cpcap, CPCAP_MACRO_5);
+               }
+       } else
+               send_key_event(data_3mm5, 0);
+
+       cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_MB2);
+       cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_UC_PRIMACRO_5);
+}
+
+static void mac13_work(struct work_struct *work)
+{
+       struct cpcap_3mm5_data *data_3mm5 =
+               container_of(work, struct cpcap_3mm5_data, work.work);
+
+       audio_low_power_set(data_3mm5, &data_3mm5->audio_low_pwr_mac13);
+       cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_UC_PRIMACRO_13);
+}
+
+static void mac13_handler(enum cpcap_irqs irq, void *data)
+{
+       struct cpcap_3mm5_data *data_3mm5 = data;
+
+       if (irq != CPCAP_IRQ_UC_PRIMACRO_13)
+               return;
+
+       audio_low_power_clear(data_3mm5, &data_3mm5->audio_low_pwr_mac13);
+       schedule_delayed_work(&data_3mm5->work, msecs_to_jiffies(200));
+}
+
+static int __init cpcap_3mm5_probe(struct platform_device *pdev)
+{
+       int retval = 0;
+       struct cpcap_3mm5_data *data;
+
+       if (pdev->dev.platform_data == NULL) {
+               dev_err(&pdev->dev, "no platform_data\n");
+               return -EINVAL;
+       }
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->cpcap = pdev->dev.platform_data;
+       data->audio_low_pwr_det = 1;
+       data->audio_low_pwr_mac13 = 1;
+       data->sdev.name = "h2w";
+       data->sdev.print_name = print_name;
+       switch_dev_register(&data->sdev);
+       INIT_DELAYED_WORK(&data->work, mac13_work);
+       platform_set_drvdata(pdev, data);
+
+       data->regulator = regulator_get(NULL, "vaudio");
+       if (IS_ERR(data->regulator)) {
+               dev_err(&pdev->dev, "Could not get regulator for cpcap_3mm5\n");
+               retval = PTR_ERR(data->regulator);
+               goto free_mem;
+       }
+
+       regulator_set_voltage(data->regulator, 2775000, 2775000);
+
+       retval  = cpcap_irq_clear(data->cpcap, CPCAP_IRQ_HS);
+       retval |= cpcap_irq_clear(data->cpcap, CPCAP_IRQ_MB2);
+       retval |= cpcap_irq_clear(data->cpcap, CPCAP_IRQ_UC_PRIMACRO_5);
+       retval |= cpcap_irq_clear(data->cpcap, CPCAP_IRQ_UC_PRIMACRO_13);
+       if (retval)
+               goto reg_put;
+
+       retval = cpcap_irq_register(data->cpcap, CPCAP_IRQ_HS, hs_handler,
+                                   data);
+       if (retval)
+               goto reg_put;
+
+       retval = cpcap_irq_register(data->cpcap, CPCAP_IRQ_MB2, key_handler,
+                                   data);
+       if (retval)
+               goto free_hs;
+
+       retval = cpcap_irq_register(data->cpcap, CPCAP_IRQ_UC_PRIMACRO_5,
+                                   key_handler, data);
+       if (retval)
+               goto free_mb2;
+
+       if (data->cpcap->vendor == CPCAP_VENDOR_ST) {
+               retval = cpcap_irq_register(data->cpcap,
+                                           CPCAP_IRQ_UC_PRIMACRO_13,
+                                           mac13_handler, data);
+               if (retval)
+                       goto free_mac5;
+
+               cpcap_uc_start(data->cpcap, CPCAP_MACRO_13);
+       }
+
+       hs_handler(CPCAP_IRQ_HS, data);
+
+       return 0;
+
+free_mac5:
+       cpcap_irq_free(data->cpcap, CPCAP_IRQ_UC_PRIMACRO_5);
+free_mb2:
+       cpcap_irq_free(data->cpcap, CPCAP_IRQ_MB2);
+free_hs:
+       cpcap_irq_free(data->cpcap, CPCAP_IRQ_HS);
+reg_put:
+       regulator_put(data->regulator);
+free_mem:
+       kfree(data);
+
+       return retval;
+}
+
+static int __exit cpcap_3mm5_remove(struct platform_device *pdev)
+{
+       struct cpcap_3mm5_data *data = platform_get_drvdata(pdev);
+
+       cancel_delayed_work_sync(&data->work);
+
+       cpcap_irq_free(data->cpcap, CPCAP_IRQ_MB2);
+       cpcap_irq_free(data->cpcap, CPCAP_IRQ_HS);
+       cpcap_irq_free(data->cpcap, CPCAP_IRQ_UC_PRIMACRO_5);
+       cpcap_irq_free(data->cpcap, CPCAP_IRQ_UC_PRIMACRO_13);
+
+       switch_dev_unregister(&data->sdev);
+       regulator_put(data->regulator);
+
+       kfree(data);
+       return 0;
+}
+
+static struct platform_driver cpcap_3mm5_driver = {
+       .probe          = cpcap_3mm5_probe,
+       .remove         = __exit_p(cpcap_3mm5_remove),
+       .driver         = {
+               .name   = "cpcap_3mm5",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init cpcap_3mm5_init(void)
+{
+       return platform_driver_register(&cpcap_3mm5_driver);
+}
+module_init(cpcap_3mm5_init);
+
+static void __exit cpcap_3mm5_exit(void)
+{
+       platform_driver_unregister(&cpcap_3mm5_driver);
+}
+module_exit(cpcap_3mm5_exit);
+
+MODULE_ALIAS("platform:cpcap_3mm5");
+MODULE_DESCRIPTION("CPCAP USB detection driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/cpcap-adc.c b/drivers/mfd/cpcap-adc.c
new file mode 100644 (file)
index 0000000..98b5f3c
--- /dev/null
@@ -0,0 +1,654 @@
+/*
+ * Copyright (C) 2009 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+#include <linux/sched.h>
+
+#include <linux/spi/cpcap.h>
+#include <linux/spi/cpcap-regbits.h>
+#include <linux/spi/spi.h>
+
+
+#define MAX_ADC_FIFO_DEPTH 8 /* this must be a power of 2 */
+#define MAX_TEMP_LVL 27
+
+struct cpcap_adc {
+       struct cpcap_device *cpcap;
+
+       /* Private stuff */
+       struct cpcap_adc_request *queue[MAX_ADC_FIFO_DEPTH];
+       int queue_head;
+       int queue_tail;
+       struct mutex queue_mutex;
+       struct delayed_work work;
+};
+
+struct phasing_tbl {
+       short offset;
+       unsigned short multiplier;
+       unsigned short divider;
+       short min;
+       short max;
+};
+
+static struct phasing_tbl bank0_phasing[CPCAP_ADC_BANK0_NUM] = {
+       [CPCAP_ADC_AD0_BATTDETB] = {0, 0x80, 0x80,    0, 1023},
+       [CPCAP_ADC_BATTP] =        {0, 0x80, 0x80,    0, 1023},
+       [CPCAP_ADC_VBUS] =         {0, 0x80, 0x80,    0, 1023},
+       [CPCAP_ADC_AD3] =          {0, 0x80, 0x80,    0, 1023},
+       [CPCAP_ADC_BPLUS_AD4] =    {0, 0x80, 0x80,    0, 1023},
+       [CPCAP_ADC_CHG_ISENSE] =   {0, 0x80, 0x80, -512,  511},
+       [CPCAP_ADC_BATTI_ADC] =    {0, 0x80, 0x80, -512,  511},
+       [CPCAP_ADC_USB_ID] =       {0, 0x80, 0x80,    0, 1023},
+};
+
+static struct phasing_tbl bank1_phasing[CPCAP_ADC_BANK1_NUM] = {
+       [CPCAP_ADC_AD8] =          {0, 0x80, 0x80,    0, 1023},
+       [CPCAP_ADC_AD9] =          {0, 0x80, 0x80,    0, 1023},
+       [CPCAP_ADC_LICELL] =       {0, 0x80, 0x80,    0, 1023},
+       [CPCAP_ADC_HV_BATTP] =     {0, 0x80, 0x80,    0, 1023},
+       [CPCAP_ADC_TSX1_AD12] =    {0, 0x80, 0x80,    0, 1023},
+       [CPCAP_ADC_TSX2_AD13] =    {0, 0x80, 0x80,    0, 1023},
+       [CPCAP_ADC_TSY1_AD14] =    {0, 0x80, 0x80,    0, 1023},
+       [CPCAP_ADC_TSY2_AD15] =    {0, 0x80, 0x80,    0, 1023},
+};
+
+enum conv_type {
+       CONV_TYPE_NONE,
+       CONV_TYPE_DIRECT,
+       CONV_TYPE_MAPPING,
+};
+
+struct conversion_tbl {
+       enum conv_type conv_type;
+       int align_offset;
+       int conv_offset;
+       int multiplier;
+       int divider;
+};
+
+static struct conversion_tbl bank0_conversion[CPCAP_ADC_BANK0_NUM] = {
+       [CPCAP_ADC_AD0_BATTDETB] = {CONV_TYPE_MAPPING,   0,    0,     1,    1},
+       [CPCAP_ADC_BATTP] =        {CONV_TYPE_DIRECT,    0, 2400,  2300, 1023},
+       [CPCAP_ADC_VBUS] =         {CONV_TYPE_DIRECT,    0,    0, 10000, 1023},
+       [CPCAP_ADC_AD3] =          {CONV_TYPE_MAPPING,   0,    0,     1,    1},
+       [CPCAP_ADC_BPLUS_AD4] =    {CONV_TYPE_DIRECT,    0, 2400,  2300, 1023},
+       [CPCAP_ADC_CHG_ISENSE] =   {CONV_TYPE_DIRECT, -512,    2,  5000, 1023},
+       [CPCAP_ADC_BATTI_ADC] =    {CONV_TYPE_DIRECT, -512,    2,  5000, 1023},
+       [CPCAP_ADC_USB_ID] =       {CONV_TYPE_NONE,      0,    0,     1,    1},
+};
+
+static struct conversion_tbl bank1_conversion[CPCAP_ADC_BANK1_NUM] = {
+       [CPCAP_ADC_AD8] =          {CONV_TYPE_NONE,   0,    0,     1,    1},
+       [CPCAP_ADC_AD9] =          {CONV_TYPE_NONE,   0,    0,     1,    1},
+       [CPCAP_ADC_LICELL] =       {CONV_TYPE_DIRECT, 0,    0,  3400, 1023},
+       [CPCAP_ADC_HV_BATTP] =     {CONV_TYPE_NONE,   0,    0,     1,    1},
+       [CPCAP_ADC_TSX1_AD12] =    {CONV_TYPE_NONE,   0,    0,     1,    1},
+       [CPCAP_ADC_TSX2_AD13] =    {CONV_TYPE_NONE,   0,    0,     1,    1},
+       [CPCAP_ADC_TSY1_AD14] =    {CONV_TYPE_NONE,   0,    0,     1,    1},
+       [CPCAP_ADC_TSY2_AD15] =    {CONV_TYPE_NONE,   0,    0,     1,    1},
+};
+
+static const unsigned short temp_map[MAX_TEMP_LVL][2] = {
+    {0x03ff, 233}, /* -40C */
+    {0x03ff, 238}, /* -35C */
+    {0x03ef, 243}, /* -30C */
+    {0x03b2, 248}, /* -25C */
+    {0x036c, 253}, /* -20C */
+    {0x0320, 258}, /* -15C */
+    {0x02d0, 263}, /* -10C */
+    {0x027f, 268}, /*  -5C */
+    {0x022f, 273}, /*   0C */
+    {0x01e4, 278}, /*   5C */
+    {0x019f, 283}, /*  10C */
+    {0x0161, 288}, /*  15C */
+    {0x012b, 293}, /*  20C */
+    {0x00fc, 298}, /*  25C */
+    {0x00d4, 303}, /*  30C */
+    {0x00b2, 308}, /*  35C */
+    {0x0095, 313}, /*  40C */
+    {0x007d, 318}, /*  45C */
+    {0x0069, 323}, /*  50C */
+    {0x0059, 328}, /*  55C */
+    {0x004b, 333}, /*  60C */
+    {0x003f, 338}, /*  65C */
+    {0x0036, 343}, /*  70C */
+    {0x002e, 348}, /*  75C */
+    {0x0027, 353}, /*  80C */
+    {0x0022, 358}, /*  85C */
+    {0x001d, 363}, /*  90C */
+};
+
+static unsigned short convert_to_kelvins(unsigned short value)
+{
+       int i;
+       unsigned short result = 0;
+       signed short alpha = 0;
+
+       if (value <= temp_map[MAX_TEMP_LVL - 1][0])
+               return temp_map[MAX_TEMP_LVL - 1][1];
+
+       if (value >= temp_map[0][0])
+               return temp_map[0][1];
+
+       for (i = 0; i < MAX_TEMP_LVL - 1; i++) {
+               if ((value <= temp_map[i][0]) &&
+                   (value >= temp_map[i+1][0])) {
+                       if (value == temp_map[i][0])
+                               result = temp_map[i][1];
+                       else if (value == temp_map[i+1][0])
+                               result = temp_map[i+1][1];
+                       else {
+                               alpha = ((value - temp_map[i][0])*1000)/
+                                       (temp_map[i+1][0] - temp_map[i][0]);
+
+                               result = temp_map[i][1] +
+                                       ((alpha*(temp_map[i+1][1] -
+                                                temp_map[i][1]))/1000);
+                       }
+                       break;
+               }
+       }
+       return result;
+}
+
+static void adc_setup(struct cpcap_device *cpcap,
+                     struct cpcap_adc_request *req)
+{
+       struct cpcap_adc_ato *ato;
+       struct cpcap_platform_data *data;
+       unsigned short value1 = 0;
+       unsigned short value2 = 0;
+
+       data = cpcap->spi->controller_data;
+       ato = data->adc_ato;
+
+       if (req->type == CPCAP_ADC_TYPE_BANK_1)
+               value1 |= CPCAP_BIT_AD_SEL1;
+       else if (req->type == CPCAP_ADC_TYPE_BATT_PI)
+               value1 |= CPCAP_BIT_RAND1;
+
+       switch (req->timing) {
+       case CPCAP_ADC_TIMING_IN:
+               value1 |= ato->ato_in;
+               value1 |= ato->atox_in;
+               value2 |= ato->adc_ps_factor_in;
+               value2 |= ato->atox_ps_factor_in;
+               break;
+
+       case CPCAP_ADC_TIMING_OUT:
+               value1 |= ato->ato_out;
+               value1 |= ato->atox_out;
+               value2 |= ato->adc_ps_factor_out;
+               value2 |= ato->atox_ps_factor_out;
+               break;
+
+       case CPCAP_ADC_TIMING_IMM:
+       default:
+               break;
+       }
+
+       cpcap_regacc_write(cpcap, CPCAP_REG_ADCC1, value1,
+                          (CPCAP_BIT_CAL_MODE | CPCAP_BIT_ATOX |
+                           CPCAP_BIT_ATO3 | CPCAP_BIT_ATO2 |
+                           CPCAP_BIT_ATO1 | CPCAP_BIT_ATO0 |
+                           CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 |
+                           CPCAP_BIT_ADA0 | CPCAP_BIT_AD_SEL1 |
+                           CPCAP_BIT_RAND1 | CPCAP_BIT_RAND0));
+
+       cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2, value2,
+                          (CPCAP_BIT_ATOX_PS_FACTOR |
+                           CPCAP_BIT_ADC_PS_FACTOR1 |
+                           CPCAP_BIT_ADC_PS_FACTOR0));
+
+       if (req->timing == CPCAP_ADC_TIMING_IMM) {
+               cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2,
+                                  CPCAP_BIT_ADTRIG_DIS,
+                                  CPCAP_BIT_ADTRIG_DIS);
+               cpcap_irq_clear(cpcap, CPCAP_IRQ_ADCDONE);
+               cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2,
+                                  CPCAP_BIT_ASC,
+                                  CPCAP_BIT_ASC);
+       } else {
+               cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2,
+                                  CPCAP_BIT_ADTRIG_ONESHOT,
+                                  CPCAP_BIT_ADTRIG_ONESHOT);
+               cpcap_irq_clear(cpcap, CPCAP_IRQ_ADCDONE);
+               cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2,
+                                  0,
+                                  CPCAP_BIT_ADTRIG_DIS);
+       }
+
+       schedule_delayed_work(&((struct cpcap_adc *)(cpcap->adcdata))->work,
+                             msecs_to_jiffies(500));
+
+       cpcap_irq_unmask(cpcap, CPCAP_IRQ_ADCDONE);
+}
+
+static void adc_setup_calibrate(struct cpcap_device *cpcap,
+                               enum cpcap_adc_bank0 chan)
+{
+       unsigned short value = 0;
+       unsigned long timeout = jiffies + msecs_to_jiffies(11);
+
+       if ((chan != CPCAP_ADC_CHG_ISENSE) &&
+           (chan != CPCAP_ADC_BATTI_ADC))
+               return;
+
+       value |= CPCAP_BIT_CAL_MODE | CPCAP_BIT_RAND0;
+       value |= ((chan << 4) &
+                  (CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 | CPCAP_BIT_ADA0));
+
+       cpcap_regacc_write(cpcap, CPCAP_REG_ADCC1, value,
+                          (CPCAP_BIT_CAL_MODE | CPCAP_BIT_ATOX |
+                           CPCAP_BIT_ATO3 | CPCAP_BIT_ATO2 |
+                           CPCAP_BIT_ATO1 | CPCAP_BIT_ATO0 |
+                           CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 |
+                           CPCAP_BIT_ADA0 | CPCAP_BIT_AD_SEL1 |
+                           CPCAP_BIT_RAND1 | CPCAP_BIT_RAND0));
+
+       cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2, 0,
+                          (CPCAP_BIT_ATOX_PS_FACTOR |
+                           CPCAP_BIT_ADC_PS_FACTOR1 |
+                           CPCAP_BIT_ADC_PS_FACTOR0));
+
+       cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2,
+                          CPCAP_BIT_ADTRIG_DIS,
+                          CPCAP_BIT_ADTRIG_DIS);
+
+       cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2,
+                          CPCAP_BIT_ASC,
+                          CPCAP_BIT_ASC);
+
+       do {
+               schedule_timeout_uninterruptible(1);
+               cpcap_regacc_read(cpcap, CPCAP_REG_ADCC2, &value);
+       } while ((value & CPCAP_BIT_ASC) && time_before(jiffies, timeout));
+
+       if (value & CPCAP_BIT_ASC)
+               dev_err(&(cpcap->spi->dev),
+                       "Timeout waiting for calibration to complete\n");
+
+       cpcap_irq_clear(cpcap, CPCAP_IRQ_ADCDONE);
+
+       cpcap_regacc_write(cpcap, CPCAP_REG_ADCC1, 0, CPCAP_BIT_CAL_MODE);
+}
+
+static void trigger_next_adc_job_if_any(struct cpcap_device *cpcap)
+{
+       struct cpcap_adc *adc = cpcap->adcdata;
+       int head;
+
+       mutex_lock(&adc->queue_mutex);
+
+       head = adc->queue_head;
+
+       if (!adc->queue[head]) {
+               mutex_unlock(&adc->queue_mutex);
+               return;
+       }
+       mutex_unlock(&adc->queue_mutex);
+
+       adc_setup(cpcap, adc->queue[head]);
+}
+
+static int
+adc_enqueue_request(struct cpcap_device *cpcap, struct cpcap_adc_request *req)
+{
+       struct cpcap_adc *adc = cpcap->adcdata;
+       int head;
+       int tail;
+       int running;
+
+       mutex_lock(&adc->queue_mutex);
+
+       head = adc->queue_head;
+       tail = adc->queue_tail;
+       running = (head != tail);
+
+       if (adc->queue[tail]) {
+               mutex_unlock(&adc->queue_mutex);
+               return -EBUSY;
+       }
+
+       adc->queue[tail] = req;
+       adc->queue_tail = (tail + 1) & (MAX_ADC_FIFO_DEPTH - 1);
+
+       mutex_unlock(&adc->queue_mutex);
+
+       if (!running)
+               trigger_next_adc_job_if_any(cpcap);
+
+       return 0;
+}
+
+static void
+cpcap_adc_sync_read_callback(struct cpcap_device *cpcap, void *param)
+{
+       struct cpcap_adc_request *req = param;
+
+       complete(&req->completion);
+}
+
+int cpcap_adc_sync_read(struct cpcap_device *cpcap,
+                       struct cpcap_adc_request *request)
+{
+       int ret;
+
+       request->callback = cpcap_adc_sync_read_callback;
+       request->callback_param = request;
+       init_completion(&request->completion);
+       ret = adc_enqueue_request(cpcap, request);
+       if (ret)
+               return ret;
+       wait_for_completion(&request->completion);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cpcap_adc_sync_read);
+
+int cpcap_adc_async_read(struct cpcap_device *cpcap,
+                        struct cpcap_adc_request *request)
+{
+       return adc_enqueue_request(cpcap, request);
+}
+EXPORT_SYMBOL_GPL(cpcap_adc_async_read);
+
+void cpcap_adc_phase(struct cpcap_device *cpcap, struct cpcap_adc_phase *phase)
+{
+       bank0_phasing[CPCAP_ADC_BATTI_ADC].offset = phase->offset_batti;
+       bank0_phasing[CPCAP_ADC_BATTI_ADC].multiplier = phase->slope_batti;
+
+       bank0_phasing[CPCAP_ADC_CHG_ISENSE].offset = phase->offset_chrgi;
+       bank0_phasing[CPCAP_ADC_CHG_ISENSE].multiplier = phase->slope_chrgi;
+
+       bank0_phasing[CPCAP_ADC_BATTP].offset = phase->offset_battp;
+       bank0_phasing[CPCAP_ADC_BATTP].multiplier = phase->slope_battp;
+
+       bank0_phasing[CPCAP_ADC_BPLUS_AD4].offset = phase->offset_bp;
+       bank0_phasing[CPCAP_ADC_BPLUS_AD4].multiplier = phase->slope_bp;
+
+       bank0_phasing[CPCAP_ADC_AD0_BATTDETB].offset = phase->offset_battt;
+       bank0_phasing[CPCAP_ADC_AD0_BATTDETB].multiplier = phase->slope_battt;
+
+       bank0_phasing[CPCAP_ADC_VBUS].offset = phase->offset_chrgv;
+       bank0_phasing[CPCAP_ADC_VBUS].multiplier = phase->slope_chrgv;
+}
+EXPORT_SYMBOL_GPL(cpcap_adc_phase);
+
+static void adc_phase(struct cpcap_adc_request *req, int index)
+{
+       struct conversion_tbl *conv_tbl = bank0_conversion;
+       struct phasing_tbl *phase_tbl = bank0_phasing;
+       int tbl_index = index;
+
+       if (req->type == CPCAP_ADC_TYPE_BANK_1) {
+               conv_tbl = bank1_conversion;
+               phase_tbl = bank1_phasing;
+       }
+
+       if (req->type == CPCAP_ADC_TYPE_BATT_PI)
+               tbl_index = (tbl_index % 2) ? CPCAP_ADC_BATTI_ADC :
+                           CPCAP_ADC_BATTP;
+
+       req->result[index] += conv_tbl[tbl_index].align_offset;
+       req->result[index] *= phase_tbl[tbl_index].multiplier;
+       req->result[index] /= phase_tbl[tbl_index].divider;
+       req->result[index] += phase_tbl[tbl_index].offset;
+
+       if (req->result[index] < phase_tbl[tbl_index].min)
+               req->result[index] = phase_tbl[tbl_index].min;
+       else if (req->result[index] > phase_tbl[tbl_index].max)
+               req->result[index] = phase_tbl[tbl_index].max;
+}
+
+static void adc_convert(struct cpcap_adc_request *req, int index)
+{
+       struct conversion_tbl *conv_tbl = bank0_conversion;
+       int tbl_index = index;
+
+       if (req->type == CPCAP_ADC_TYPE_BANK_1)
+               conv_tbl = bank1_conversion;
+
+       if (req->type == CPCAP_ADC_TYPE_BATT_PI)
+               tbl_index = (tbl_index % 2) ? CPCAP_ADC_BATTI_ADC :
+                           CPCAP_ADC_BATTP;
+
+       if (conv_tbl[tbl_index].conv_type == CONV_TYPE_DIRECT) {
+               req->result[index] *= conv_tbl[tbl_index].multiplier;
+               req->result[index] /= conv_tbl[tbl_index].divider;
+               req->result[index] += conv_tbl[tbl_index].conv_offset;
+       } else if (conv_tbl[tbl_index].conv_type == CONV_TYPE_MAPPING)
+               req->result[index] = convert_to_kelvins(req->result[tbl_index]);
+}
+
+static void adc_raw(struct cpcap_adc_request *req, int index)
+{
+       struct conversion_tbl *conv_tbl = bank0_conversion;
+       struct phasing_tbl *phase_tbl = bank0_phasing;
+       int tbl_index = index;
+
+       if (req->type == CPCAP_ADC_TYPE_BANK_1)
+               return;
+
+       if (req->type == CPCAP_ADC_TYPE_BATT_PI)
+               tbl_index = (tbl_index % 2) ? CPCAP_ADC_BATTI_ADC :
+                           CPCAP_ADC_BATTP;
+
+       req->result[index] += conv_tbl[tbl_index].align_offset;
+
+       if (req->result[index] < phase_tbl[tbl_index].min)
+               req->result[index] = phase_tbl[tbl_index].min;
+       else if (req->result[index] > phase_tbl[tbl_index].max)
+               req->result[index] = phase_tbl[tbl_index].max;
+}
+
+static void adc_result(struct cpcap_device *cpcap,
+                      struct cpcap_adc_request *req)
+{
+       int i;
+       int j;
+
+       for (i = CPCAP_REG_ADCD0; i <= CPCAP_REG_ADCD7; i++) {
+               j = i - CPCAP_REG_ADCD0;
+               cpcap_regacc_read(cpcap, i, (unsigned short *)&req->result[j]);
+               req->result[j] &= 0x3FF;
+
+               switch (req->format) {
+               case CPCAP_ADC_FORMAT_PHASED:
+                       adc_phase(req, j);
+                       break;
+
+               case CPCAP_ADC_FORMAT_CONVERTED:
+                       adc_phase(req, j);
+                       adc_convert(req, j);
+                       break;
+
+               case CPCAP_ADC_FORMAT_RAW:
+                       adc_raw(req, j);
+                       break;
+
+               default:
+                       break;
+               }
+       }
+}
+
+static void cpcap_adc_irq(enum cpcap_irqs irq, void *data)
+{
+       struct cpcap_adc *adc = data;
+       struct cpcap_device *cpcap = adc->cpcap;
+       struct cpcap_adc_request *req;
+       int head;
+
+       cancel_delayed_work_sync(&adc->work);
+
+       cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2,
+                          CPCAP_BIT_ADTRIG_DIS,
+                          CPCAP_BIT_ADTRIG_DIS);
+
+       mutex_lock(&adc->queue_mutex);
+       head = adc->queue_head;
+
+       req = adc->queue[head];
+       if (!req) {
+               dev_info(&(cpcap->spi->dev),
+                       "cpcap_adc_irq: ADC queue empty!\n");
+               mutex_unlock(&adc->queue_mutex);
+               return;
+       }
+       adc->queue[head] = NULL;
+       adc->queue_head = (head + 1) & (MAX_ADC_FIFO_DEPTH - 1);
+
+       mutex_unlock(&adc->queue_mutex);
+
+       adc_result(cpcap, req);
+
+       trigger_next_adc_job_if_any(cpcap);
+
+       req->status = 0;
+
+       req->callback(cpcap, req->callback_param);
+}
+
+static void cpcap_adc_cancel(struct work_struct *work)
+{
+       int head;
+       struct cpcap_adc_request *req;
+       struct cpcap_adc *adc =
+               container_of(work, struct cpcap_adc, work.work);
+
+       cpcap_irq_mask(adc->cpcap, CPCAP_IRQ_ADCDONE);
+
+       cpcap_regacc_write(adc->cpcap, CPCAP_REG_ADCC2,
+                          CPCAP_BIT_ADTRIG_DIS,
+                          CPCAP_BIT_ADTRIG_DIS);
+
+       mutex_lock(&adc->queue_mutex);
+       head = adc->queue_head;
+
+       req = adc->queue[head];
+       if (!req) {
+               dev_info(&(adc->cpcap->spi->dev),
+                       "cpcap_adc_cancel: ADC queue empty!\n");
+               mutex_unlock(&adc->queue_mutex);
+               return;
+       }
+       adc->queue[head] = NULL;
+       adc->queue_head = (head + 1) & (MAX_ADC_FIFO_DEPTH - 1);
+
+       mutex_unlock(&adc->queue_mutex);
+
+       req->status = -ETIMEDOUT;
+
+       req->callback(adc->cpcap, req->callback_param);
+
+       trigger_next_adc_job_if_any(adc->cpcap);
+}
+
+static int __devinit cpcap_adc_probe(struct platform_device *pdev)
+{
+       struct cpcap_adc *adc;
+       unsigned short cal_data;
+
+       if (pdev->dev.platform_data == NULL) {
+               dev_err(&pdev->dev, "no platform_data\n");
+               return -EINVAL;
+       }
+
+       adc = kzalloc(sizeof(*adc), GFP_KERNEL);
+       if (!adc)
+               return -ENOMEM;
+
+       adc->cpcap = pdev->dev.platform_data;
+
+       platform_set_drvdata(pdev, adc);
+       adc->cpcap->adcdata = adc;
+
+       mutex_init(&adc->queue_mutex);
+
+       adc_setup_calibrate(adc->cpcap, CPCAP_ADC_CHG_ISENSE);
+       adc_setup_calibrate(adc->cpcap, CPCAP_ADC_BATTI_ADC);
+
+       cal_data = 0;
+       cpcap_regacc_read(adc->cpcap, CPCAP_REG_ADCAL1, &cal_data);
+       bank0_conversion[CPCAP_ADC_CHG_ISENSE].align_offset =
+               ((short)cal_data * -1);
+       cal_data = 0;
+       cpcap_regacc_read(adc->cpcap, CPCAP_REG_ADCAL2, &cal_data);
+       bank0_conversion[CPCAP_ADC_BATTI_ADC].align_offset =
+               ((short)cal_data * -1);
+
+       INIT_DELAYED_WORK(&adc->work, cpcap_adc_cancel);
+
+       cpcap_irq_register(adc->cpcap, CPCAP_IRQ_ADCDONE,
+                          cpcap_adc_irq, adc);
+
+       return 0;
+}
+
+static int __devexit cpcap_adc_remove(struct platform_device *pdev)
+{
+       struct cpcap_adc *adc = platform_get_drvdata(pdev);
+       int head;
+
+       cancel_delayed_work_sync(&adc->work);
+
+       cpcap_irq_free(adc->cpcap, CPCAP_IRQ_ADCDONE);
+
+       mutex_lock(&adc->queue_mutex);
+       head = adc->queue_head;
+
+       if (WARN_ON(adc->queue[head]))
+               dev_err(&pdev->dev,
+                       "adc driver removed with request pending\n");
+
+       mutex_unlock(&adc->queue_mutex);
+       kfree(adc);
+
+       return 0;
+}
+
+static struct platform_driver cpcap_adc_driver = {
+       .driver = {
+               .name = "cpcap_adc",
+       },
+       .probe = cpcap_adc_probe,
+       .remove = __devexit_p(cpcap_adc_remove),
+};
+
+static int __init cpcap_adc_init(void)
+{
+       return platform_driver_register(&cpcap_adc_driver);
+}
+module_init(cpcap_adc_init);
+
+static void __exit cpcap_adc_exit(void)
+{
+       platform_driver_unregister(&cpcap_adc_driver);
+}
+module_exit(cpcap_adc_exit);
+
+MODULE_ALIAS("platform:cpcap_adc");
+MODULE_DESCRIPTION("CPCAP ADC driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/cpcap-core.c b/drivers/mfd/cpcap-core.c
new file mode 100644 (file)
index 0000000..858c0cf
--- /dev/null
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2007-2010 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/machine.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/cpcap.h>
+#include <linux/spi/cpcap-regbits.h>
+#include <linux/uaccess.h>
+#include <linux/reboot.h>
+#include <linux/notifier.h>
+#include <linux/delay.h>
+
+struct cpcap_driver_info {
+       struct list_head list;
+       struct platform_device *pdev;
+};
+
+static int ioctl(struct inode *inode,
+                struct file *file, unsigned int cmd, unsigned long arg);
+static int __devinit cpcap_probe(struct spi_device *spi);
+static int __devexit cpcap_remove(struct spi_device *spi);
+
+const static struct file_operations cpcap_fops = {
+       .owner = THIS_MODULE,
+       .ioctl = ioctl,
+};
+
+static struct miscdevice cpcap_dev = {
+       .minor  = MISC_DYNAMIC_MINOR,
+       .name   = CPCAP_DEV_NAME,
+       .fops   = &cpcap_fops,
+};
+
+static struct spi_driver cpcap_driver = {
+       .driver = {
+                  .name = "cpcap",
+                  .bus = &spi_bus_type,
+                  .owner = THIS_MODULE,
+                  },
+       .probe = cpcap_probe,
+       .remove = __devexit_p(cpcap_remove),
+};
+
+static struct platform_device cpcap_adc_device = {
+       .name           = "cpcap_adc",
+       .id             = -1,
+       .dev.platform_data = NULL,
+};
+
+
+static struct platform_device cpcap_key_device = {
+       .name           = "cpcap_key",
+       .id             = -1,
+       .dev.platform_data = NULL,
+};
+
+static struct platform_device cpcap_uc_device = {
+       .name           = "cpcap_uc",
+       .id             = -1,
+       .dev.platform_data = NULL,
+};
+
+static struct platform_device cpcap_rtc_device = {
+       .name           = "cpcap_rtc",
+       .id             = -1,
+       .dev.platform_data = NULL,
+};
+
+/* List of required CPCAP devices that will ALWAYS be present.
+ *
+ * DO NOT ADD NEW DEVICES TO THIS LIST! You must use cpcap_driver_register()
+ * for any new drivers for non-core functionality of CPCAP.
+ */
+static struct platform_device *cpcap_devices[] = {
+       &cpcap_uc_device,
+       &cpcap_adc_device,
+       &cpcap_key_device,
+       &cpcap_rtc_device,
+};
+
+static struct cpcap_device *misc_cpcap;
+
+static LIST_HEAD(cpcap_device_list);
+static DEFINE_MUTEX(cpcap_driver_lock);
+
+static int cpcap_reboot(struct notifier_block *this, unsigned long code,
+                       void *cmd)
+{
+       int ret = -1;
+       int result = NOTIFY_DONE;
+
+       /* Disable the USB transceiver */
+       ret = cpcap_regacc_write(misc_cpcap, CPCAP_REG_USBC2, 0,
+                                CPCAP_BIT_USBXCVREN);
+
+       if (ret) {
+               dev_err(&(misc_cpcap->spi->dev),
+                       "Disable Transciever failure.\n");
+               result = NOTIFY_BAD;
+       }
+
+       if (code == SYS_RESTART)
+               cpcap_regacc_write(misc_cpcap, CPCAP_REG_MI2, 0, 0xFFFF);
+
+       /* Always clear the power cut bit on SW Shutdown*/
+       ret = cpcap_regacc_write(misc_cpcap, CPCAP_REG_PC1,
+               0, CPCAP_BIT_PC1_PCEN);
+       if (ret) {
+               dev_err(&(misc_cpcap->spi->dev),
+                       "Clear Power Cut bit failure.\n");
+               result = NOTIFY_BAD;
+       }
+
+       /* Clear the charger and charge path settings to avoid a false turn on
+        * event in caused by CPCAP. After clearing these settings, 100ms is
+        * needed to before SYSRSTRTB is pulled low to avoid the false turn on
+        * event.
+        */
+       cpcap_regacc_write(misc_cpcap, CPCAP_REG_CRM, 0, 0x3FFF);
+       mdelay(100);
+
+       return result;
+}
+static struct notifier_block cpcap_reboot_notifier = {
+       .notifier_call = cpcap_reboot,
+};
+
+static int __init cpcap_init(void)
+{
+       return spi_register_driver(&cpcap_driver);
+}
+
+static void cpcap_vendor_read(struct cpcap_device *cpcap)
+{
+       unsigned short value;
+
+       (void)cpcap_regacc_read(cpcap, CPCAP_REG_VERSC1, &value);
+
+       cpcap->vendor = (enum cpcap_vendor)((value >> 6) & 0x0007);
+       cpcap->revision = (enum cpcap_revision)(((value >> 3) & 0x0007) |
+                                               ((value << 3) & 0x0038));
+}
+
+
+int cpcap_device_unregister(struct platform_device *pdev)
+{
+       struct cpcap_driver_info *info;
+       struct cpcap_driver_info *tmp;
+       int found;
+
+
+       found = 0;
+       mutex_lock(&cpcap_driver_lock);
+
+       list_for_each_entry_safe(info, tmp, &cpcap_device_list, list) {
+               if (info->pdev == pdev) {
+                       list_del(&info->list);
+
+                       /*
+                        * misc_cpcap != NULL suggests pdev
+                        * already registered
+                        */
+                       if (misc_cpcap) {
+                               printk(KERN_INFO "CPCAP: unregister %s\n",
+                                       pdev->name);
+                               platform_device_unregister(pdev);
+                       }
+                       info->pdev = NULL;
+                       kfree(info);
+                       found = 1;
+               }
+       }
+
+       mutex_unlock(&cpcap_driver_lock);
+
+       BUG_ON(!found);
+       return 0;
+}
+
+int cpcap_device_register(struct platform_device *pdev)
+{
+       int retval;
+       struct cpcap_driver_info *info;
+
+       retval = 0;
+
+       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       if (!info) {
+               printk(KERN_ERR "Cannot save device %s\n", pdev->name);
+               return -ENOMEM;
+       }
+
+       mutex_lock(&cpcap_driver_lock);
+
+       info->pdev = pdev;
+       list_add_tail(&info->list, &cpcap_device_list);
+
+       /* If misc_cpcap is valid, the CPCAP driver has already been probed.
+        * Therefore, call platform_device_register() to probe the device.
+        */
+       if (misc_cpcap) {
+               dev_info(&(misc_cpcap->spi->dev),
+                        "Probing CPCAP device %s\n", pdev->name);
+
+               /*
+                * platform_data is non-empty indicates
+                * CPCAP client devices need to pass their own data
+                * In that case we put cpcap data in driver_data
+                */
+               if (pdev->dev.platform_data != NULL)
+                       platform_set_drvdata(pdev, misc_cpcap);
+               else
+                       pdev->dev.platform_data = misc_cpcap;
+               retval = platform_device_register(pdev);
+       } else
+               printk(KERN_INFO "CPCAP: delaying %s probe\n",
+                               pdev->name);
+       mutex_unlock(&cpcap_driver_lock);
+
+       return retval;
+}
+
+static int __devinit cpcap_probe(struct spi_device *spi)
+{
+       int retval = -EINVAL;
+       struct cpcap_device *cpcap;
+       struct cpcap_platform_data *data;
+       int i;
+       struct cpcap_driver_info *info;
+
+       cpcap = kzalloc(sizeof(*cpcap), GFP_KERNEL);
+       if (cpcap == NULL)
+               return -ENOMEM;
+
+       cpcap->spi = spi;
+       data = spi->controller_data;
+       spi_set_drvdata(spi, cpcap);
+
+       retval = cpcap_regacc_init(cpcap);
+       if (retval < 0)
+               goto free_mem;
+       retval = cpcap_irq_init(cpcap);
+       if (retval < 0)
+               goto free_cpcap_irq;
+
+       cpcap_vendor_read(cpcap);
+
+       for (i = 0; i < ARRAY_SIZE(cpcap_devices); i++)
+               cpcap_devices[i]->dev.platform_data = cpcap;
+
+       retval = misc_register(&cpcap_dev);
+       if (retval < 0)
+               goto free_cpcap_irq;
+
+       /* loop twice becuase cpcap_regulator_probe may refer to other devices
+        * in this list to handle dependencies between regulators.  Create them
+        * all and then add them */
+       for (i = 0; i < CPCAP_NUM_REGULATORS; i++) {
+               struct platform_device *pdev;
+
+               pdev = platform_device_alloc("cpcap-regltr", i);
+               if (!pdev) {
+                       dev_err(&(spi->dev), "Cannot create regulator\n");
+                       continue;
+               }
+
+               pdev->dev.parent = &(spi->dev);
+               pdev->dev.platform_data = &data->regulator_init[i];
+               platform_set_drvdata(pdev, cpcap);
+               cpcap->regulator_pdev[i] = pdev;
+       }
+
+       for (i = 0; i < CPCAP_NUM_REGULATORS; i++) {
+               /* vusb has to be added after sw5 so skip it for now,
+                * it will be added from probe of sw5 */
+               if (i == CPCAP_VUSB)
+                       continue;
+               platform_device_add(cpcap->regulator_pdev[i]);
+       }
+
+       platform_add_devices(cpcap_devices, ARRAY_SIZE(cpcap_devices));
+
+       mutex_lock(&cpcap_driver_lock);
+       misc_cpcap = cpcap;  /* kept for misc device */
+
+       list_for_each_entry(info, &cpcap_device_list, list) {
+               dev_info(&(spi->dev), "Probing CPCAP device %s\n",
+                        info->pdev->name);
+               if (info->pdev->dev.platform_data != NULL)
+                       platform_set_drvdata(info->pdev, cpcap);
+               else
+                       info->pdev->dev.platform_data = cpcap;
+               platform_device_register(info->pdev);
+       }
+       mutex_unlock(&cpcap_driver_lock);
+
+       register_reboot_notifier(&cpcap_reboot_notifier);
+
+       return 0;
+
+free_cpcap_irq:
+       cpcap_irq_shutdown(cpcap);
+free_mem:
+       kfree(cpcap);
+       return retval;
+}
+
+static int __devexit cpcap_remove(struct spi_device *spi)
+{
+       struct cpcap_device *cpcap = spi_get_drvdata(spi);
+       struct cpcap_driver_info *info;
+       int i;
+
+       unregister_reboot_notifier(&cpcap_reboot_notifier);
+
+       mutex_lock(&cpcap_driver_lock);
+       list_for_each_entry(info, &cpcap_device_list, list) {
+               dev_info(&(spi->dev), "Removing CPCAP device %s\n",
+                        info->pdev->name);
+               platform_device_unregister(info->pdev);
+       }
+       misc_cpcap = NULL;
+       mutex_unlock(&cpcap_driver_lock);
+
+       for (i = ARRAY_SIZE(cpcap_devices); i > 0; i--)
+               platform_device_unregister(cpcap_devices[i-1]);
+
+       for (i = 0; i < CPCAP_NUM_REGULATORS; i++)
+               platform_device_unregister(cpcap->regulator_pdev[i]);
+
+       misc_deregister(&cpcap_dev);
+       cpcap_irq_shutdown(cpcap);
+       kfree(cpcap);
+       return 0;
+}
+
+
+static int test_ioctl(unsigned int cmd, unsigned long arg)
+{
+       int retval = -EINVAL;
+       struct cpcap_regacc read_data;
+       struct cpcap_regacc write_data;
+
+       switch (cmd) {
+       case CPCAP_IOCTL_TEST_READ_REG:
+               if (copy_from_user((void *)&read_data, (void *)arg,
+                                  sizeof(read_data)))
+                       return -EFAULT;
+               retval = cpcap_regacc_read(misc_cpcap, read_data.reg,
+                                          &read_data.value);
+               if (retval < 0)
+                       return retval;
+               if (copy_to_user((void *)arg, (void *)&read_data,
+                                sizeof(read_data)))
+                       return -EFAULT;
+               return 0;
+       break;
+
+       case CPCAP_IOCTL_TEST_WRITE_REG:
+               if (copy_from_user((void *) &write_data,
+                                  (void *) arg,
+                                  sizeof(write_data)))
+                       return -EFAULT;
+               retval = cpcap_regacc_write(misc_cpcap, write_data.reg,
+                                           write_data.value, write_data.mask);
+       break;
+
+       default:
+               retval = -ENOTTY;
+       break;
+       }
+
+       return retval;
+}
+
+static int adc_ioctl(unsigned int cmd, unsigned long arg)
+{
+       int retval = -EINVAL;
+       struct cpcap_adc_phase phase;
+
+       switch (cmd) {
+       case CPCAP_IOCTL_ADC_PHASE:
+               if (copy_from_user((void *) &phase, (void *) arg,
+                                  sizeof(phase)))
+                       return -EFAULT;
+
+               cpcap_adc_phase(misc_cpcap, &phase);
+               retval = 0;
+       break;
+
+       default:
+               retval = -ENOTTY;
+       break;
+       }
+
+       return retval;
+}
+
+static int accy_ioctl(unsigned int cmd, unsigned long arg)
+{
+       int retval = -EINVAL;
+
+       switch (cmd) {
+       case CPCAP_IOCTL_ACCY_WHISPER:
+               retval = cpcap_accy_whisper(misc_cpcap, arg);
+       break;
+
+       default:
+               retval = -ENOTTY;
+       break;
+       }
+
+       return retval;
+}
+
+static int ioctl(struct inode *inode,
+                struct file *file, unsigned int cmd, unsigned long arg)
+{
+       int retval = -ENOTTY;
+       unsigned int cmd_num;
+
+       cmd_num = _IOC_NR(cmd);
+
+       if ((cmd_num > CPCAP_IOCTL_NUM_TEST__START) &&
+           (cmd_num < CPCAP_IOCTL_NUM_TEST__END)) {
+               retval = test_ioctl(cmd, arg);
+       }
+       if ((cmd_num > CPCAP_IOCTL_NUM_ADC__START) &&
+           (cmd_num < CPCAP_IOCTL_NUM_ADC__END)) {
+               retval = adc_ioctl(cmd, arg);
+       }
+       if ((cmd_num > CPCAP_IOCTL_NUM_ACCY__START) &&
+           (cmd_num < CPCAP_IOCTL_NUM_ACCY__END)) {
+               retval = accy_ioctl(cmd, arg);
+       }
+
+       return retval;
+}
+
+static void cpcap_shutdown(void)
+{
+       spi_unregister_driver(&cpcap_driver);
+}
+
+subsys_initcall(cpcap_init);
+module_exit(cpcap_shutdown);
+
+MODULE_ALIAS("platform:cpcap");
+MODULE_DESCRIPTION("CPCAP driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/cpcap-irq.c b/drivers/mfd/cpcap-irq.c
new file mode 100644 (file)
index 0000000..b0726ca
--- /dev/null
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 2009, Motorola, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/wakelock.h>
+
+#include <linux/spi/cpcap.h>
+#include <linux/spi/spi.h>
+#include <linux/debugfs.h>
+
+#define NUM_INT_REGS      5
+#define NUM_INTS_PER_REG  16
+
+#define CPCAP_INT1_VALID_BITS 0xFFFB
+#define CPCAP_INT2_VALID_BITS 0xFFFF
+#define CPCAP_INT3_VALID_BITS 0xFFFF
+#define CPCAP_INT4_VALID_BITS 0x03FF
+#define CPCAP_INT5_VALID_BITS 0xFFFF
+
+struct cpcap_event_handler {
+       void (*func)(enum cpcap_irqs, void *);
+       void *data;
+};
+
+struct cpcap_irqdata {
+       struct mutex lock;
+       struct work_struct work;
+       struct workqueue_struct *workqueue;
+       struct cpcap_device *cpcap;
+       struct cpcap_event_handler event_handler[CPCAP_IRQ__NUM];
+       uint64_t registered;
+       uint64_t enabled;
+       struct wake_lock wake_lock;
+};
+
+#define EVENT_MASK(event) (1 << ((event) % NUM_INTS_PER_REG))
+
+enum pwrkey_states {
+       PWRKEY_RELEASE, /* Power key released state. */
+       PWRKEY_PRESS,   /* Power key pressed state. */
+       PWRKEY_UNKNOWN, /* Unknown power key state. */
+};
+
+static irqreturn_t event_isr(int irq, void *data)
+{
+       struct cpcap_irqdata *irq_data = data;
+       disable_irq_nosync(irq);
+       wake_lock(&irq_data->wake_lock);
+       queue_work(irq_data->workqueue, &irq_data->work);
+
+       return IRQ_HANDLED;
+}
+
+static unsigned short get_int_reg(enum cpcap_irqs event)
+{
+       unsigned short ret;
+
+       if ((event) >= CPCAP_IRQ_INT5_INDEX)
+               ret = CPCAP_REG_MI1;
+       else if ((event) >= CPCAP_IRQ_INT4_INDEX)
+               ret = CPCAP_REG_INT4;
+       else if ((event) >= CPCAP_IRQ_INT3_INDEX)
+               ret = CPCAP_REG_INT3;
+       else if ((event) >= CPCAP_IRQ_INT2_INDEX)
+               ret = CPCAP_REG_INT2;
+       else
+               ret = CPCAP_REG_INT1;
+
+       return ret;
+}
+
+static unsigned short get_mask_reg(enum cpcap_irqs event)
+{
+       unsigned short ret;
+
+       if (event >= CPCAP_IRQ_INT5_INDEX)
+               ret = CPCAP_REG_MIM1;
+       else if (event >= CPCAP_IRQ_INT4_INDEX)
+               ret = CPCAP_REG_INTM4;
+       else if (event >= CPCAP_IRQ_INT3_INDEX)
+               ret = CPCAP_REG_INTM3;
+       else if (event >= CPCAP_IRQ_INT2_INDEX)
+               ret = CPCAP_REG_INTM2;
+       else
+               ret = CPCAP_REG_INTM1;
+
+       return ret;
+}
+
+static unsigned short get_sense_reg(enum cpcap_irqs event)
+{
+       unsigned short ret;
+
+       if (event >= CPCAP_IRQ_INT5_INDEX)
+               ret = CPCAP_REG_MI2;
+       else if (event >= CPCAP_IRQ_INT4_INDEX)
+               ret = CPCAP_REG_INTS4;
+       else if (event >= CPCAP_IRQ_INT3_INDEX)
+               ret = CPCAP_REG_INTS3;
+       else if (event >= CPCAP_IRQ_INT2_INDEX)
+               ret = CPCAP_REG_INTS2;
+       else
+               ret = CPCAP_REG_INTS1;
+
+       return ret;
+}
+
+void cpcap_irq_mask_all(struct cpcap_device *cpcap)
+{
+       int i;
+
+       static const struct {
+               unsigned short mask_reg;
+               unsigned short valid;
+       } int_reg[NUM_INT_REGS] = {
+               {CPCAP_REG_INTM1, CPCAP_INT1_VALID_BITS},
+               {CPCAP_REG_INTM2, CPCAP_INT2_VALID_BITS},
+               {CPCAP_REG_INTM3, CPCAP_INT3_VALID_BITS},
+               {CPCAP_REG_INTM4, CPCAP_INT4_VALID_BITS},
+               {CPCAP_REG_MIM1,  CPCAP_INT5_VALID_BITS}
+       };
+
+       for (i = 0; i < NUM_INT_REGS; i++) {
+               cpcap_regacc_write(cpcap, int_reg[i].mask_reg,
+                                  int_reg[i].valid,
+                                  int_reg[i].valid);
+       }
+}
+
+struct pwrkey_data {
+       struct cpcap_device *cpcap;
+       enum pwrkey_states state;
+       struct wake_lock wake_lock;
+};
+
+static void pwrkey_handler(enum cpcap_irqs irq, void *data)
+{
+       struct pwrkey_data *pwrkey_data = data;
+       enum pwrkey_states new_state, last_state = pwrkey_data->state;
+       struct cpcap_device *cpcap = pwrkey_data->cpcap;
+
+       new_state = (enum pwrkey_states) cpcap_irq_sense(cpcap, irq, 0);
+
+
+       if ((new_state < PWRKEY_UNKNOWN) && (new_state != last_state)) {
+               wake_lock_timeout(&pwrkey_data->wake_lock, 20);
+               cpcap_broadcast_key_event(cpcap, KEY_END, new_state);
+               pwrkey_data->state = new_state;
+       }
+       cpcap_irq_unmask(cpcap, CPCAP_IRQ_ON);
+}
+
+static int pwrkey_init(struct cpcap_device *cpcap)
+{
+       struct pwrkey_data *data = kmalloc(sizeof(struct pwrkey_data),
+                                          GFP_KERNEL);
+       int retval;
+
+       if (!data)
+               return -ENOMEM;
+       data->cpcap = cpcap;
+       data->state = PWRKEY_RELEASE;
+       retval = cpcap_irq_register(cpcap, CPCAP_IRQ_ON, pwrkey_handler, data);
+       if (retval)
+               kfree(data);
+       wake_lock_init(&data->wake_lock, WAKE_LOCK_SUSPEND, "pwrkey");
+       return retval;
+}
+
+static void pwrkey_remove(struct cpcap_device *cpcap)
+{
+       struct pwrkey_data *data;
+
+       cpcap_irq_get_data(cpcap, CPCAP_IRQ_ON, (void **)&data);
+       if (!data)
+               return;
+       cpcap_irq_free(cpcap, CPCAP_IRQ_ON);
+       wake_lock_destroy(&data->wake_lock);
+       kfree(data);
+}
+
+static int int_read_and_clear(struct cpcap_device *cpcap,
+                             unsigned short status_reg,
+                             unsigned short mask_reg,
+                             unsigned short valid_mask,
+                             unsigned short *en)
+{
+       unsigned short ireg_val, mreg_val;
+       int ret;
+       ret = cpcap_regacc_read(cpcap, status_reg, &ireg_val);
+       if (ret)
+               return ret;
+       ret = cpcap_regacc_read(cpcap, mask_reg, &mreg_val);
+       if (ret)
+               return ret;
+       *en |= ireg_val & ~mreg_val;
+       *en &= valid_mask;
+       ret = cpcap_regacc_write(cpcap, mask_reg, *en, *en);
+       if (ret)
+               return ret;
+       ret = cpcap_regacc_write(cpcap, status_reg, *en, *en);
+       if (ret)
+               return ret;
+       return 0;
+}
+
+
+static void irq_work_func(struct work_struct *work)
+{
+       int retval = 0;
+       unsigned short en_ints[NUM_INT_REGS];
+       int i;
+       struct cpcap_irqdata *data;
+       struct cpcap_device *cpcap;
+       struct spi_device *spi;
+
+       static const struct {
+               unsigned short status_reg;
+               unsigned short mask_reg;
+               unsigned short valid;
+       } int_reg[NUM_INT_REGS] = {
+               {CPCAP_REG_INT1, CPCAP_REG_INTM1, CPCAP_INT1_VALID_BITS},
+               {CPCAP_REG_INT2, CPCAP_REG_INTM2, CPCAP_INT2_VALID_BITS},
+               {CPCAP_REG_INT3, CPCAP_REG_INTM3, CPCAP_INT3_VALID_BITS},
+               {CPCAP_REG_INT4, CPCAP_REG_INTM4, CPCAP_INT4_VALID_BITS},
+               {CPCAP_REG_MI1,  CPCAP_REG_MIM1,  CPCAP_INT5_VALID_BITS}
+       };
+
+       for (i = 0; i < NUM_INT_REGS; ++i)
+               en_ints[i] = 0;
+
+       data = container_of(work, struct cpcap_irqdata, work);
+       cpcap = data->cpcap;
+       spi = cpcap->spi;
+
+       for (i = 0; i < NUM_INT_REGS; ++i) {
+               retval = int_read_and_clear(cpcap,
+                                           int_reg[i].status_reg,
+                                           int_reg[i].mask_reg,
+                                           int_reg[i].valid,
+                                           &en_ints[i]);
+               if (retval < 0) {
+                       dev_err(&spi->dev, "Error reading interrupts\n");
+                       break;
+               }
+       }
+       enable_irq(spi->irq);
+
+       /* lock protects event handlers and data */
+       mutex_lock(&data->lock);
+       for (i = 0; i < NUM_INT_REGS; ++i) {
+               unsigned char index;
+
+               while (en_ints[i] > 0) {
+                       struct cpcap_event_handler *event_handler;
+
+                       /* find the first set bit */
+                       index = (unsigned char)(ffs(en_ints[i]) - 1);
+                       if (index >= CPCAP_IRQ__NUM)
+                               goto error;
+                       /* clear the bit */
+                       en_ints[i] &= ~(1 << index);
+                       /* find the event that occurred */
+                       index += CPCAP_IRQ__START + (i * NUM_INTS_PER_REG);
+                       event_handler = &data->event_handler[index];
+
+                       if (event_handler->func)
+                               event_handler->func(index, event_handler->data);
+               }
+       }
+error:
+       mutex_unlock(&data->lock);
+       wake_unlock(&data->wake_lock);
+}
+
+int cpcap_irq_init(struct cpcap_device *cpcap)
+{
+       int retval;
+       struct spi_device *spi = cpcap->spi;
+       struct cpcap_irqdata *data;
+       struct dentry *debug_dir;
+
+       data = kzalloc(sizeof(struct cpcap_irqdata), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       cpcap_irq_mask_all(cpcap);
+
+       data->workqueue = create_workqueue("cpcap_irq");
+       INIT_WORK(&data->work, irq_work_func);
+       mutex_init(&data->lock);
+       wake_lock_init(&data->wake_lock, WAKE_LOCK_SUSPEND, "cpcap-irq");
+       data->cpcap = cpcap;
+
+       retval = request_irq(spi->irq, event_isr, IRQF_DISABLED |
+                            IRQF_TRIGGER_HIGH, "cpcap-irq", data);
+       if (retval) {
+               printk(KERN_ERR "cpcap_irq: Failed requesting irq.\n");
+               goto error;
+       }
+
+       enable_irq_wake(spi->irq);
+
+       cpcap->irqdata = data;
+       retval = pwrkey_init(cpcap);
+       if (retval) {
+               printk(KERN_ERR "cpcap_irq: Failed initializing pwrkey.\n");
+               goto error;
+       }
+
+       debug_dir = debugfs_create_dir("cpcap-irq", NULL);
+       debugfs_create_u64("registered", S_IRUGO, debug_dir,
+                          &data->registered);
+       debugfs_create_u64("enabled", S_IRUGO, debug_dir,
+                          &data->enabled);
+
+       return 0;
+
+error:
+       free_irq(spi->irq, data);
+       kfree(data);
+       printk(KERN_ERR "cpcap_irq: Error registering cpcap irq.\n");
+       return retval;
+}
+
+void cpcap_irq_shutdown(struct cpcap_device *cpcap)
+{
+       struct spi_device *spi = cpcap->spi;
+       struct cpcap_irqdata *data = cpcap->irqdata;
+
+       pwrkey_remove(cpcap);
+       cancel_work_sync(&data->work);
+       destroy_workqueue(data->workqueue);
+       free_irq(spi->irq, data);
+       kfree(data);
+}
+
+int cpcap_irq_register(struct cpcap_device *cpcap,
+                      enum cpcap_irqs irq,
+                      void (*cb_func) (enum cpcap_irqs, void *),
+                      void *data)
+{
+       struct cpcap_irqdata *irqdata = cpcap->irqdata;
+       int retval = 0;
+
+       if ((irq >= CPCAP_IRQ__NUM) || (!cb_func))
+               return -EINVAL;
+
+       mutex_lock(&irqdata->lock);
+
+       if (irqdata->event_handler[irq].func == NULL) {
+               irqdata->registered |= 1 << irq;
+               cpcap_irq_unmask(cpcap, irq);
+               irqdata->event_handler[irq].func = cb_func;
+               irqdata->event_handler[irq].data = data;
+       } else
+               retval = -EPERM;
+
+       mutex_unlock(&irqdata->lock);
+
+       return retval;
+}
+EXPORT_SYMBOL_GPL(cpcap_irq_register);
+
+int cpcap_irq_free(struct cpcap_device *cpcap, enum cpcap_irqs irq)
+{
+       struct cpcap_irqdata *data = cpcap->irqdata;
+       int retval;
+
+       if (irq >= CPCAP_IRQ__NUM)
+               return -EINVAL;
+
+       mutex_lock(&data->lock);
+       retval = cpcap_irq_mask(cpcap, irq);
+       data->event_handler[irq].func = NULL;
+       data->event_handler[irq].data = NULL;
+       data->registered &= ~(1 << irq);
+       mutex_unlock(&data->lock);
+
+       return retval;
+}
+EXPORT_SYMBOL_GPL(cpcap_irq_free);
+
+int cpcap_irq_get_data(struct cpcap_device *cpcap,
+                       enum cpcap_irqs irq,
+                       void **data)
+{
+       struct cpcap_irqdata *irqdata = cpcap->irqdata;
+
+       if (irq >= CPCAP_IRQ__NUM)
+               return -EINVAL;
+
+       mutex_lock(&irqdata->lock);
+       *data = irqdata->event_handler[irq].data;
+       mutex_unlock(&irqdata->lock);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cpcap_irq_get_data);
+
+int cpcap_irq_clear(struct cpcap_device *cpcap,
+                   enum cpcap_irqs irq)
+{
+       int retval = -EINVAL;
+
+       if ((irq < CPCAP_IRQ__NUM) && (irq != CPCAP_IRQ_SECMAC)) {
+               retval = cpcap_regacc_write(cpcap,
+                                           get_int_reg(irq),
+                                           EVENT_MASK(irq),
+                                           EVENT_MASK(irq));
+       }
+
+       return retval;
+}
+EXPORT_SYMBOL_GPL(cpcap_irq_clear);
+
+int cpcap_irq_mask(struct cpcap_device *cpcap,
+                  enum cpcap_irqs irq)
+{
+       struct cpcap_irqdata *data = cpcap->irqdata;
+       int retval = -EINVAL;
+
+       if ((irq < CPCAP_IRQ__NUM) && (irq != CPCAP_IRQ_SECMAC)) {
+               data->enabled &= ~(1 << irq);
+               retval = cpcap_regacc_write(cpcap,
+                                           get_mask_reg(irq),
+                                           EVENT_MASK(irq),
+                                           EVENT_MASK(irq));
+       }
+
+       return retval;
+}
+EXPORT_SYMBOL_GPL(cpcap_irq_mask);
+
+int cpcap_irq_unmask(struct cpcap_device *cpcap,
+                    enum cpcap_irqs irq)
+{
+       struct cpcap_irqdata *data = cpcap->irqdata;
+       int retval = -EINVAL;
+
+       if ((irq < CPCAP_IRQ__NUM) && (irq != CPCAP_IRQ_SECMAC)) {
+               data->enabled |= 1 << irq;
+               retval = cpcap_regacc_write(cpcap,
+                                           get_mask_reg(irq),
+                                           0,
+                                           EVENT_MASK(irq));
+       }
+
+       return retval;
+}
+EXPORT_SYMBOL_GPL(cpcap_irq_unmask);
+
+int cpcap_irq_mask_get(struct cpcap_device *cpcap,
+                      enum cpcap_irqs irq)
+{
+       struct cpcap_irqdata *data = cpcap->irqdata;
+       int retval = -EINVAL;
+
+       if ((irq < CPCAP_IRQ__NUM) && (irq != CPCAP_IRQ_SECMAC))
+               return (data->enabled & (1 << irq)) ? 0 : 1;
+
+       return retval;
+}
+EXPORT_SYMBOL_GPL(cpcap_irq_mask_get);
+
+int cpcap_irq_sense(struct cpcap_device *cpcap,
+                   enum cpcap_irqs irq,
+                   unsigned char clear)
+{
+       unsigned short val;
+       int retval;
+
+       if (irq >= CPCAP_IRQ__NUM)
+               return -EINVAL;
+
+       retval = cpcap_regacc_read(cpcap, get_sense_reg(irq), &val);
+       if (retval)
+               return retval;
+
+       if (clear)
+               retval = cpcap_irq_clear(cpcap, irq);
+       if (retval)
+               return retval;
+
+       return ((val & EVENT_MASK(irq)) != 0) ? 1 : 0;
+}
+EXPORT_SYMBOL_GPL(cpcap_irq_sense);
diff --git a/drivers/mfd/cpcap-key.c b/drivers/mfd/cpcap-key.c
new file mode 100644 (file)
index 0000000..633e6de
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2009 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/spi/cpcap.h>
+#include <linux/spi/cpcap-regbits.h>
+
+
+
+struct cpcap_key_data {
+       struct input_dev *input_dev;
+       struct cpcap_device *cpcap;
+};
+
+static int __init cpcap_key_probe(struct platform_device *pdev)
+{
+       int err;
+       struct cpcap_key_data *key;
+
+       if (pdev->dev.platform_data == NULL) {
+               dev_err(&pdev->dev, "no platform_data\n");
+               return -EINVAL;
+       }
+
+       key = kzalloc(sizeof(*key), GFP_KERNEL);
+       if (!key)
+               return -ENOMEM;
+
+       key->cpcap = pdev->dev.platform_data;
+
+       key->input_dev = input_allocate_device();
+       if (key->input_dev == NULL) {
+               dev_err(&pdev->dev, "can't allocate input device\n");
+               err = -ENOMEM;
+               goto err0;
+       }
+
+       set_bit(EV_KEY, key->input_dev->evbit);
+       set_bit(KEY_MEDIA, key->input_dev->keybit);
+       set_bit(KEY_END, key->input_dev->keybit);
+
+       key->input_dev->name = "cpcap-key";
+
+       err = input_register_device(key->input_dev);
+       if (err < 0) {
+               dev_err(&pdev->dev, "could not register input device.\n");
+               goto err1;
+       }
+
+       platform_set_drvdata(pdev, key);
+       cpcap_set_keydata(key->cpcap, key);
+
+       dev_info(&pdev->dev, "CPCAP key device probed\n");
+
+       return 0;
+
+err1:
+       input_free_device(key->input_dev);
+err0:
+       kfree(key);
+       return err;
+}
+
+static int __exit cpcap_key_remove(struct platform_device *pdev)
+{
+       struct cpcap_key_data *key = platform_get_drvdata(pdev);
+
+       input_unregister_device(key->input_dev);
+       input_free_device(key->input_dev);
+       kfree(key);
+
+       return 0;
+}
+
+void cpcap_broadcast_key_event(struct cpcap_device *cpcap,
+                              unsigned int code, int value)
+{
+       struct cpcap_key_data *key = cpcap_get_keydata(cpcap);
+
+       if (key && key->input_dev)
+               input_report_key(key->input_dev, code, value);
+}
+EXPORT_SYMBOL(cpcap_broadcast_key_event);
+
+static struct platform_driver cpcap_key_driver = {
+       .probe          = cpcap_key_probe,
+       .remove         = __exit_p(cpcap_key_remove),
+       .driver         = {
+               .name   = "cpcap_key",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init cpcap_key_init(void)
+{
+       return platform_driver_register(&cpcap_key_driver);
+}
+module_init(cpcap_key_init);
+
+static void __exit cpcap_key_exit(void)
+{
+       platform_driver_unregister(&cpcap_key_driver);
+}
+module_exit(cpcap_key_exit);
+
+MODULE_ALIAS("platform:cpcap_key");
+MODULE_DESCRIPTION("CPCAP KEY driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/cpcap-regacc.c b/drivers/mfd/cpcap-regacc.c
new file mode 100644 (file)
index 0000000..2b8de54
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2007-2009 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/cpcap.h>
+#include <linux/spi/cpcap-regbits.h>
+
+#define IS_CPCAP(reg) ((reg) >= CPCAP_REG_START && (reg) <= CPCAP_REG_END)
+
+static DEFINE_MUTEX(reg_access);
+
+/*
+ * This table contains information about a single register in the power IC.
+ * It is used during register access to information such as the register address
+ * and the modifiability of each bit in the register.  Special notes for
+ * particular elements of this structure follows:
+ *
+ * constant_mask: A '1' in this mask indicates that the corresponding bit has a
+ * 'constant' modifiability, and therefore must never be changed by any register
+ * access.
+ *
+ * It is important to note that any bits that are 'constant' must have
+ * synchronized read/write values.  That is to say, when a 'constant' bit is
+ * read the value read must be identical to the value that must be written to
+ * that bit in order for that bit to be read with the same value.
+ *
+ * rbw_mask: A '1' in this mask indicates that the corresponding bit (when not
+ * being changed) should be written with the current value of that bit.  A '0'
+ * in this mask indicates that the corresponding bit (when not being changed)
+ * should be written with a value of '0'.
+ */
+static const struct {
+       unsigned short address;         /* Address of the register */
+       unsigned short constant_mask;   /* Constant modifiability mask */
+       unsigned short rbw_mask;        /* Read-before-write mask */
+} register_info_tbl[CPCAP_NUM_REG_CPCAP] = {
+       [CPCAP_REG_INT1]      = {0, 0x0004, 0x0000},
+       [CPCAP_REG_INT2]      = {1, 0x0000, 0x0000},
+       [CPCAP_REG_INT3]      = {2, 0x0000, 0x0000},
+       [CPCAP_REG_INT4]      = {3, 0xFC00, 0x0000},
+       [CPCAP_REG_INTM1]     = {4, 0x0004, 0xFFFF},
+       [CPCAP_REG_INTM2]     = {5, 0x0000, 0xFFFF},
+       [CPCAP_REG_INTM3]     = {6, 0x0000, 0xFFFF},
+       [CPCAP_REG_INTM4]     = {7, 0xFC00, 0xFFFF},
+       [CPCAP_REG_INTS1]     = {8, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_INTS2]     = {9, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_INTS3]     = {10, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_INTS4]     = {11, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_ASSIGN1]   = {12, 0x80F8, 0xFFFF},
+       [CPCAP_REG_ASSIGN2]   = {13, 0x0000, 0xFFFF},
+       [CPCAP_REG_ASSIGN3]   = {14, 0x0004, 0xFFFF},
+       [CPCAP_REG_ASSIGN4]   = {15, 0x0068, 0xFFFF},
+       [CPCAP_REG_ASSIGN5]   = {16, 0x0000, 0xFFFF},
+       [CPCAP_REG_ASSIGN6]   = {17, 0xFC00, 0xFFFF},
+       [CPCAP_REG_VERSC1]    = {18, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_VERSC2]    = {19, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_MI1]       = {128, 0x0000, 0x0000},
+       [CPCAP_REG_MIM1]      = {129, 0x0000, 0xFFFF},
+       [CPCAP_REG_MI2]       = {130, 0x0000, 0xFFFF},
+       [CPCAP_REG_MIM2]      = {131, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_UCC1]      = {132, 0xF000, 0xFFFF},
+       [CPCAP_REG_UCC2]      = {133, 0xFC00, 0xFFFF},
+       [CPCAP_REG_PC1]       = {135, 0xFC00, 0xFFFF},
+       [CPCAP_REG_PC2]       = {136, 0xFC00, 0xFFFF},
+       [CPCAP_REG_BPEOL]     = {137, 0xFE00, 0xFFFF},
+       [CPCAP_REG_PGC]       = {138, 0xFE00, 0xFFFF},
+       [CPCAP_REG_MT1]       = {139, 0x0000, 0x0000},
+       [CPCAP_REG_MT2]       = {140, 0x0000, 0x0000},
+       [CPCAP_REG_MT3]       = {141, 0x0000, 0x0000},
+       [CPCAP_REG_PF]        = {142, 0x0000, 0xFFFF},
+       [CPCAP_REG_SCC]       = {256, 0xFF00, 0xFFFF},
+       [CPCAP_REG_SW1]       = {257, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_SW2]       = {258, 0xFC7F, 0xFFFF},
+       [CPCAP_REG_UCTM]      = {259, 0xFFFE, 0xFFFF},
+       [CPCAP_REG_TOD1]      = {260, 0xFF00, 0xFFFF},
+       [CPCAP_REG_TOD2]      = {261, 0xFE00, 0xFFFF},
+       [CPCAP_REG_TODA1]     = {262, 0xFF00, 0xFFFF},
+       [CPCAP_REG_TODA2]     = {263, 0xFE00, 0xFFFF},
+       [CPCAP_REG_DAY]       = {264, 0x8000, 0xFFFF},
+       [CPCAP_REG_DAYA]      = {265, 0x8000, 0xFFFF},
+       [CPCAP_REG_VAL1]      = {266, 0x0000, 0xFFFF},
+       [CPCAP_REG_VAL2]      = {267, 0x0000, 0xFFFF},
+       [CPCAP_REG_SDVSPLL]   = {384, 0x2488, 0xFFFF},
+       [CPCAP_REG_SI2CC1]    = {385, 0x8000, 0xFFFF},
+       [CPCAP_REG_Si2CC2]    = {386, 0xFF00, 0xFFFF},
+       [CPCAP_REG_S1C1]      = {387, 0x9080, 0xFFFF},
+       [CPCAP_REG_S1C2]      = {388, 0x8080, 0xFFFF},
+       [CPCAP_REG_S2C1]      = {389, 0x9080, 0xFFFF},
+       [CPCAP_REG_S2C2]      = {390, 0x8080, 0xFFFF},
+       [CPCAP_REG_S3C]       = {391, 0xFA84, 0xFFFF},
+       [CPCAP_REG_S4C1]      = {392, 0x9080, 0xFFFF},
+       [CPCAP_REG_S4C2]      = {393, 0x8080, 0xFFFF},
+       [CPCAP_REG_S5C]       = {394, 0xFFD5, 0xFFFF},
+       [CPCAP_REG_S6C]       = {395, 0xFFF4, 0xFFFF},
+       [CPCAP_REG_VCAMC]     = {396, 0xFF48, 0xFFFF},
+       [CPCAP_REG_VCSIC]     = {397, 0xFFA8, 0xFFFF},
+       [CPCAP_REG_VDACC]     = {398, 0xFF48, 0xFFFF},
+       [CPCAP_REG_VDIGC]     = {399, 0xFF48, 0xFFFF},
+       [CPCAP_REG_VFUSEC]    = {400, 0xFF50, 0xFFFF},
+       [CPCAP_REG_VHVIOC]    = {401, 0xFFE8, 0xFFFF},
+       [CPCAP_REG_VSDIOC]    = {402, 0xFF40, 0xFFFF},
+       [CPCAP_REG_VPLLC]     = {403, 0xFFA4, 0xFFFF},
+       [CPCAP_REG_VRF1C]     = {404, 0xFF50, 0xFFFF},
+       [CPCAP_REG_VRF2C]     = {405, 0xFFD4, 0xFFFF},
+       [CPCAP_REG_VRFREFC]   = {406, 0xFFD4, 0xFFFF},
+       [CPCAP_REG_VWLAN1C]   = {407, 0xFFA8, 0xFFFF},
+       [CPCAP_REG_VWLAN2C]   = {408, 0xFD32, 0xFFFF},
+       [CPCAP_REG_VSIMC]     = {409, 0xE154, 0xFFFF},
+       [CPCAP_REG_VVIBC]     = {410, 0xFFF2, 0xFFFF},
+       [CPCAP_REG_VUSBC]     = {411, 0xFEA2, 0xFFFF},
+       [CPCAP_REG_VUSBINT1C] = {412, 0xFFD4, 0xFFFF},
+       [CPCAP_REG_VUSBINT2C] = {413, 0xFFD4, 0xFFFF},
+       [CPCAP_REG_URT]       = {414, 0xFFFE, 0xFFFF},
+       [CPCAP_REG_URM1]      = {415, 0x0000, 0xFFFF},
+       [CPCAP_REG_URM2]      = {416, 0xFC00, 0xFFFF},
+       [CPCAP_REG_VAUDIOC]   = {512, 0xFF88, 0xFFFF},
+       [CPCAP_REG_CC]        = {513, 0x0000, 0xFEDF},
+       [CPCAP_REG_CDI]       = {514, 0x4000, 0xFFFF},
+       [CPCAP_REG_SDAC]      = {515, 0xF000, 0xFCFF},
+       [CPCAP_REG_SDACDI]    = {516, 0xC000, 0xFFFF},
+       [CPCAP_REG_TXI]       = {517, 0x0000, 0xFFFF},
+       [CPCAP_REG_TXMP]      = {518, 0xF000, 0xFFFF},
+       [CPCAP_REG_RXOA]      = {519, 0xF800, 0xFFFF},
+       [CPCAP_REG_RXVC]      = {520, 0x00C3, 0xFFFF},
+       [CPCAP_REG_RXCOA]     = {521, 0xF800, 0xFFFF},
+       [CPCAP_REG_RXSDOA]    = {522, 0xE000, 0xFFFF},
+       [CPCAP_REG_RXEPOA]    = {523, 0x8000, 0xFFFF},
+       [CPCAP_REG_RXLL]      = {524, 0x0000, 0xFFFF},
+       [CPCAP_REG_A2LA]      = {525, 0xFF00, 0xFFFF},
+       [CPCAP_REG_MIPIS1]    = {526, 0x0000, 0xFFFF},
+       [CPCAP_REG_MIPIS2]    = {527, 0xFF00, 0xFFFF},
+       [CPCAP_REG_MIPIS3]    = {528, 0xFFFC, 0xFFFF},
+       [CPCAP_REG_LVAB]      = {529, 0xFFFC, 0xFFFF},
+       [CPCAP_REG_CCC1]      = {640, 0xFFF0, 0xFFFF},
+       [CPCAP_REG_CRM]       = {641, 0xC000, 0xFFFF},
+       [CPCAP_REG_CCCC2]     = {642, 0xFFC0, 0xFFFF},
+       [CPCAP_REG_CCS1]      = {643, 0x0000, 0xFFFF},
+       [CPCAP_REG_CCS2]      = {644, 0xFF00, 0xFFFF},
+       [CPCAP_REG_CCA1]      = {645, 0x0000, 0xFFFF},
+       [CPCAP_REG_CCA2]      = {646, 0x0000, 0xFFFF},
+       [CPCAP_REG_CCM]       = {647, 0xFC00, 0xFFFF},
+       [CPCAP_REG_CCO]       = {648, 0xFC00, 0xFFFF},
+       [CPCAP_REG_CCI]       = {649, 0xC000, 0xFFFF},
+       [CPCAP_REG_ADCC1]     = {768, 0x0000, 0xFFFF},
+       [CPCAP_REG_ADCC2]     = {769, 0x0080, 0xFFFF},
+       [CPCAP_REG_ADCD0]     = {770, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_ADCD1]     = {771, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_ADCD2]     = {772, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_ADCD3]     = {773, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_ADCD4]     = {774, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_ADCD5]     = {775, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_ADCD6]     = {776, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_ADCD7]     = {777, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_ADCAL1]    = {778, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_ADCAL2]    = {779, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_USBC1]     = {896, 0x0000, 0xFFFF},
+       [CPCAP_REG_USBC2]     = {897, 0x0000, 0xFFFF},
+       [CPCAP_REG_USBC3]     = {898, 0x8200, 0xFFFF},
+       [CPCAP_REG_UVIDL]     = {899, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_UVIDH]     = {900, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_UPIDL]     = {901, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_UPIDH]     = {902, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_UFC1]      = {903, 0xFF80, 0xFFFF},
+       [CPCAP_REG_UFC2]      = {904, 0xFF80, 0xFFFF},
+       [CPCAP_REG_UFC3]      = {905, 0xFF80, 0xFFFF},
+       [CPCAP_REG_UIC1]      = {906, 0xFF64, 0xFFFF},
+       [CPCAP_REG_UIC2]      = {907, 0xFF64, 0xFFFF},
+       [CPCAP_REG_UIC3]      = {908, 0xFF64, 0xFFFF},
+       [CPCAP_REG_USBOTG1]   = {909, 0xFFC0, 0xFFFF},
+       [CPCAP_REG_USBOTG2]   = {910, 0xFFC0, 0xFFFF},
+       [CPCAP_REG_USBOTG3]   = {911, 0xFFC0, 0xFFFF},
+       [CPCAP_REG_UIER1]     = {912, 0xFFE0, 0xFFFF},
+       [CPCAP_REG_UIER2]     = {913, 0xFFE0, 0xFFFF},
+       [CPCAP_REG_UIER3]     = {914, 0xFFE0, 0xFFFF},
+       [CPCAP_REG_UIEF1]     = {915, 0xFFE0, 0xFFFF},
+       [CPCAP_REG_UIEF2]     = {916, 0xFFE0, 0xFFFF},
+       [CPCAP_REG_UIEF3]     = {917, 0xFFE0, 0xFFFF},
+       [CPCAP_REG_UIS]       = {918, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_UIL]       = {919, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_USBD]      = {920, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_SCR1]      = {921, 0xFF00, 0xFFFF},
+       [CPCAP_REG_SCR2]      = {922, 0xFF00, 0xFFFF},
+       [CPCAP_REG_SCR3]      = {923, 0xFF00, 0xFFFF},
+       [CPCAP_REG_VMC]       = {939, 0xFFFE, 0xFFFF},
+       [CPCAP_REG_OWDC]      = {940, 0xFFFC, 0xFFFF},
+       [CPCAP_REG_GPIO0]     = {941, 0x0D11, 0x3FFF},
+       [CPCAP_REG_GPIO1]     = {943, 0x0D11, 0x3FFF},
+       [CPCAP_REG_GPIO2]     = {945, 0x0D11, 0x3FFF},
+       [CPCAP_REG_GPIO3]     = {947, 0x0D11, 0x3FFF},
+       [CPCAP_REG_GPIO4]     = {949, 0x0D11, 0x3FFF},
+       [CPCAP_REG_GPIO5]     = {951, 0x0C11, 0x3FFF},
+       [CPCAP_REG_GPIO6]     = {953, 0x0C11, 0x3FFF},
+       [CPCAP_REG_MDLC]      = {1024, 0x0000, 0xFFFF},
+       [CPCAP_REG_KLC]       = {1025, 0x8000, 0xFFFF},
+       [CPCAP_REG_ADLC]      = {1026, 0x8000, 0xFFFF},
+       [CPCAP_REG_REDC]      = {1027, 0xFC00, 0xFFFF},
+       [CPCAP_REG_GREENC]    = {1028, 0xFC00, 0xFFFF},
+       [CPCAP_REG_BLUEC]     = {1029, 0xFC00, 0xFFFF},
+       [CPCAP_REG_CFC]       = {1030, 0xF000, 0xFFFF},
+       [CPCAP_REG_ABC]       = {1031, 0xFFC3, 0xFFFF},
+       [CPCAP_REG_BLEDC]     = {1032, 0xFC00, 0xFFFF},
+       [CPCAP_REG_CLEDC]     = {1033, 0xFC00, 0xFFFF},
+       [CPCAP_REG_OW1C]      = {1152, 0xFF00, 0xFFFF},
+       [CPCAP_REG_OW1D]      = {1153, 0xFF00, 0xFFFF},
+       [CPCAP_REG_OW1I]      = {1154, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_OW1IE]     = {1155, 0xFF00, 0xFFFF},
+       [CPCAP_REG_OW1]       = {1157, 0xFF00, 0xFFFF},
+       [CPCAP_REG_OW2C]      = {1160, 0xFF00, 0xFFFF},
+       [CPCAP_REG_OW2D]      = {1161, 0xFF00, 0xFFFF},
+       [CPCAP_REG_OW2I]      = {1162, 0xFFFF, 0xFFFF},
+       [CPCAP_REG_OW2IE]     = {1163, 0xFF00, 0xFFFF},
+       [CPCAP_REG_OW2]       = {1165, 0xFF00, 0xFFFF},
+       [CPCAP_REG_OW3C]      = {1168, 0xFF00, 0xFFFF},
+       [CPCAP_REG_OW3D]      = {1169, 0xFF00, 0xFFFF},
+       [CPCAP_REG_OW3I]      = {1170, 0xFF00, 0xFFFF},
+       [CPCAP_REG_OW3IE]     = {1171, 0xFF00, 0xFFFF},
+       [CPCAP_REG_OW3]       = {1173, 0xFF00, 0xFFFF},
+       [CPCAP_REG_GCAIC]     = {1174, 0xFF00, 0xFFFF},
+       [CPCAP_REG_GCAIM]     = {1175, 0xFF00, 0xFFFF},
+       [CPCAP_REG_LGDIR]     = {1176, 0xFFE0, 0xFFFF},
+       [CPCAP_REG_LGPU]      = {1177, 0xFFE0, 0xFFFF},
+       [CPCAP_REG_LGPIN]     = {1178, 0xFF00, 0xFFFF},
+       [CPCAP_REG_LGMASK]    = {1179, 0xFFE0, 0xFFFF},
+       [CPCAP_REG_LDEB]      = {1180, 0xFF00, 0xFFFF},
+       [CPCAP_REG_LGDET]     = {1181, 0xFF00, 0xFFFF},
+       [CPCAP_REG_LMISC]     = {1182, 0xFF07, 0xFFFF},
+       [CPCAP_REG_LMACE]     = {1183, 0xFFF8, 0xFFFF},
+};
+
+static int cpcap_spi_access(struct spi_device *spi, u8 *buf,
+                           size_t len)
+{
+       struct spi_message m;
+       struct spi_transfer t = {
+               .tx_buf = buf,
+               .len = len,
+               .rx_buf = buf,
+               .bits_per_word = 32,
+       };
+
+       spi_message_init(&m);
+       spi_message_add_tail(&t, &m);
+       return spi_sync(spi, &m);
+}
+
+static int cpcap_config_for_read(struct spi_device *spi, unsigned short reg,
+                                unsigned short *data)
+{
+       int status = -ENOTTY;
+       u32 buf32;  /* force buf to be 32bit aligned */
+       u8 *buf = (u8 *) &buf32;
+
+       if (spi != NULL) {
+               buf[3] = (reg >> 6) & 0x000000FF;
+               buf[2] = (reg << 2) & 0x000000FF;
+               buf[1] = 0;
+               buf[0] = 0;
+
+               status = cpcap_spi_access(spi, buf, 4);
+
+               if (status == 0)
+                       *data = buf[0] | (buf[1] << 8);
+       }
+
+       return status;
+}
+
+static int cpcap_config_for_write(struct spi_device *spi, unsigned short reg,
+                                 unsigned short data)
+{
+       int status = -ENOTTY;
+       u32 buf32;  /* force buf to be 32bit aligned */
+       u8 *buf = (u8 *) &buf32;
+
+       if (spi != NULL) {
+               buf[3] = ((reg >> 6) & 0x000000FF) | 0x80;
+               buf[2] = (reg << 2) & 0x000000FF;
+               buf[1] = (data >> 8) & 0x000000FF;
+               buf[0] = data & 0x000000FF;
+
+               status = cpcap_spi_access(spi, buf, 4);
+       }
+
+       return status;
+}
+
+int cpcap_regacc_read(struct cpcap_device *cpcap, enum cpcap_reg reg,
+                     unsigned short *value_ptr)
+{
+       int retval = -EINVAL;
+       struct spi_device *spi = cpcap->spi;
+
+       if (IS_CPCAP(reg) && (value_ptr != 0)) {
+               mutex_lock(&reg_access);
+
+               retval = cpcap_config_for_read(spi, register_info_tbl
+                                     [reg].address, value_ptr);
+
+               mutex_unlock(&reg_access);
+       }
+
+       return retval;
+}
+
+int cpcap_regacc_write(struct cpcap_device *cpcap,
+                      enum cpcap_reg reg,
+                      unsigned short value,
+                      unsigned short mask)
+{
+       int retval = -EINVAL;
+       unsigned short old_value = 0;
+       struct cpcap_platform_data *data;
+       struct spi_device *spi = cpcap->spi;
+
+       data = (struct cpcap_platform_data *)spi->controller_data;
+
+       if (IS_CPCAP(reg) &&
+           (mask & register_info_tbl[reg].constant_mask) == 0) {
+               mutex_lock(&reg_access);
+
+               value &= mask;
+
+               if ((register_info_tbl[reg].rbw_mask) != 0) {
+                       retval = cpcap_config_for_read(spi, register_info_tbl
+                                                      [reg].address,
+                                                      &old_value);
+                       if (retval != 0)
+                               goto error;
+               }
+
+               old_value &= register_info_tbl[reg].rbw_mask;
+               old_value &= ~mask;
+               value |= old_value;
+               retval = cpcap_config_for_write(spi,
+                                               register_info_tbl[reg].address,
+                                               value);
+error:
+               mutex_unlock(&reg_access);
+       }
+
+       return retval;
+}
+
+int cpcap_regacc_init(struct cpcap_device *cpcap)
+{
+       unsigned short i;
+       unsigned short mask;
+       int retval = 0;
+       struct cpcap_platform_data *data;
+       struct spi_device *spi = cpcap->spi;
+
+       data = (struct cpcap_platform_data *)spi->controller_data;
+
+       for (i = 0; i < data->init_len; i++) {
+               mask = 0xFFFF;
+               mask &= ~(register_info_tbl[data->init[i].reg].constant_mask);
+
+               retval = cpcap_regacc_write(cpcap, data->init[i].reg,
+                                           data->init[i].data,
+                                           mask);
+               if (retval)
+                       break;
+       }
+
+       return retval;
+}
diff --git a/drivers/mfd/cpcap-uc.c b/drivers/mfd/cpcap-uc.c
new file mode 100644 (file)
index 0000000..d014f04
--- /dev/null
@@ -0,0 +1,880 @@
+/*
+ * Copyright (C) 2008-2010 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/completion.h>
+#include <linux/errno.h>
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include <linux/ihex.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+
+#include <linux/spi/cpcap.h>
+#include <linux/spi/cpcap-regbits.h>
+#include <linux/spi/spi.h>
+
+
+#define ERROR_MACRO_TIMEOUT  0x81
+#define ERROR_MACRO_WRITE    0x82
+#define ERROR_MACRO_READ     0x83
+
+#define RAM_START_TI         0x9000
+#define RAM_END_TI           0x9FA0
+#define RAM_START_ST         0x0000
+#define RAM_END_ST           0x0FFF
+
+enum {
+       READ_STATE_1,   /* Send size and location of RAM read. */
+       READ_STATE_2,   /*!< Read MT registers. */
+       READ_STATE_3,   /*!< Read data from uC. */
+       READ_STATE_4,   /*!< Check for error. */
+};
+
+enum {
+       WRITE_STATE_1,  /* Send size and location of RAM write. */
+       WRITE_STATE_2,  /* Check for error. */
+       WRITE_STATE_3,  /* Write data to uC. */
+       WRITE_STATE_4   /* Check for error. */
+};
+
+struct cpcap_uc_data {
+       struct cpcap_device *cpcap;
+       unsigned char is_supported;
+       unsigned char is_ready;
+       struct completion completion;
+       int cb_status;
+       struct mutex lock;
+       unsigned char uc_reset;
+       unsigned char state;
+       unsigned short state_cntr;
+       struct {
+               unsigned short address;
+               unsigned short *data;
+               unsigned short num_words;
+       } req;
+};
+
+static struct cpcap_uc_data *cpcap_uc_info;
+
+static int fops_open(struct inode *inode, struct file *file);
+static int fops_ioctl(struct inode *inode, struct file *file,
+                     unsigned int cmd, unsigned long arg);
+static ssize_t fops_write(struct file *file, const char *buf,
+                         size_t count, loff_t *ppos);
+static ssize_t fops_read(struct file *file, char *buf,
+                        size_t count, loff_t *ppos);
+
+
+static const struct file_operations fops = {
+       .owner = THIS_MODULE,
+       .ioctl = fops_ioctl,
+       .open = fops_open,
+       .read = fops_read,
+       .write = fops_write,
+};
+
+static struct miscdevice uc_dev = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = "cpcap_uc",
+       .fops = &fops,
+};
+
+static int is_valid_address(struct cpcap_device *cpcap, unsigned short address,
+                           unsigned short num_words)
+{
+       int vld = 0;
+
+       if (cpcap->vendor == CPCAP_VENDOR_TI) {
+               vld = (address >= RAM_START_TI) &&
+                   ((address + num_words) <= RAM_END_TI);
+       } else if (cpcap->vendor == CPCAP_VENDOR_ST) {
+               vld = ((address + num_words) <= RAM_END_ST);
+       }
+
+       return vld;
+}
+
+static void ram_read_state_machine(enum cpcap_irqs irq, void *data)
+{
+       struct cpcap_uc_data *uc_data = data;
+       unsigned short temp;
+
+       if (irq != CPCAP_IRQ_UC_PRIRAMR)
+               return;
+
+       switch (uc_data->state) {
+       case READ_STATE_1:
+               cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT1,
+                                  uc_data->req.address, 0xFFFF);
+               cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT2,
+                                  uc_data->req.num_words, 0xFFFF);
+               cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT3, 0, 0xFFFF);
+
+               if (uc_data->cpcap->vendor == CPCAP_VENDOR_ST)
+                       uc_data->state = READ_STATE_2;
+               else
+                       uc_data->state = READ_STATE_3;
+
+               cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMR);
+               break;
+
+       case READ_STATE_2:
+               cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT1, &temp);
+
+               if (temp == ERROR_MACRO_READ) {
+                       uc_data->state = READ_STATE_1;
+                       uc_data->state_cntr = 0;
+
+                       cpcap_irq_mask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMR);
+
+                       uc_data->cb_status = -EIO;
+
+                       complete(&uc_data->completion);
+               } else {
+                       cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT2, &temp);
+                       cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT3, &temp);
+
+                       uc_data->state = READ_STATE_3;
+                       cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMR);
+               }
+               break;
+
+       case READ_STATE_3:
+               cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT1,
+                                 uc_data->req.data + uc_data->state_cntr);
+
+               uc_data->state_cntr += 1;
+
+               if (uc_data->state_cntr == uc_data->req.num_words)
+                       cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT2, &temp);
+               else {
+                       cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT2,
+                                         uc_data->req.data +
+                                         uc_data->state_cntr);
+
+                       uc_data->state_cntr += 1;
+               }
+
+               if (uc_data->state_cntr == uc_data->req.num_words)
+                       cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT3, &temp);
+               else {
+                       cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT3,
+                                         uc_data->req.data +
+                                         uc_data->state_cntr);
+
+                       uc_data->state_cntr += 1;
+               }
+
+               if (uc_data->state_cntr == uc_data->req.num_words)
+                       uc_data->state = READ_STATE_4;
+
+               cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMR);
+               break;
+
+       case READ_STATE_4:
+               cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT1, &temp);
+
+               if (temp != ERROR_MACRO_READ)
+                       uc_data->cb_status = 0;
+               else
+                       uc_data->cb_status = -EIO;
+
+               complete(&uc_data->completion);
+
+               uc_data->state = READ_STATE_1;
+               uc_data->state_cntr = 0;
+               break;
+
+       default:
+               uc_data->state = READ_STATE_1;
+               uc_data->state_cntr = 0;
+               break;
+       }
+}
+
+static void ram_write_state_machine(enum cpcap_irqs irq, void *data)
+{
+       struct cpcap_uc_data *uc_data = data;
+       unsigned short error_check;
+
+       if (irq != CPCAP_IRQ_UC_PRIRAMW)
+               return;
+
+       switch (uc_data->state) {
+       case WRITE_STATE_1:
+               cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT1,
+                                  uc_data->req.address, 0xFFFF);
+               cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT2,
+                                  uc_data->req.num_words, 0xFFFF);
+               cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT3, 0, 0xFFFF);
+
+               uc_data->state = WRITE_STATE_2;
+               cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMW);
+               break;
+
+       case WRITE_STATE_2:
+               cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT1, &error_check);
+
+               if (error_check == ERROR_MACRO_WRITE) {
+                       uc_data->state = WRITE_STATE_1;
+                       uc_data->state_cntr = 0;
+
+                       cpcap_irq_mask(uc_data->cpcap,
+                                      CPCAP_IRQ_UC_PRIRAMW);
+
+                       uc_data->cb_status = -EIO;
+                       complete(&uc_data->completion);
+                       break;
+               } else
+                       uc_data->state = WRITE_STATE_3;
+
+               /* No error has occured, fall through */
+
+       case WRITE_STATE_3:
+               cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT1,
+                                  *(uc_data->req.data + uc_data->state_cntr),
+                                  0xFFFF);
+               uc_data->state_cntr += 1;
+
+               if (uc_data->state_cntr == uc_data->req.num_words)
+                       cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT2, 0,
+                                          0xFFFF);
+               else {
+                       cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT2,
+                                          *(uc_data->req.data +
+                                            uc_data->state_cntr), 0xFFFF);
+
+                       uc_data->state_cntr += 1;
+               }
+
+               if (uc_data->state_cntr == uc_data->req.num_words)
+                       cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT3, 0,
+                                          0xFFFF);
+               else {
+                       cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT3,
+                                          *(uc_data->req.data +
+                                            uc_data->state_cntr), 0xFFFF);
+
+                       uc_data->state_cntr += 1;
+               }
+
+               if (uc_data->state_cntr == uc_data->req.num_words)
+                       uc_data->state = WRITE_STATE_4;
+
+               cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMW);
+               break;
+
+       case WRITE_STATE_4:
+               cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT1, &error_check);
+
+               if (error_check != ERROR_MACRO_WRITE)
+                       uc_data->cb_status = 0;
+               else
+                       uc_data->cb_status = -EIO;
+
+               complete(&uc_data->completion);
+
+               uc_data->state = WRITE_STATE_1;
+               uc_data->state_cntr = 0;
+               break;
+
+       default:
+               uc_data->state = WRITE_STATE_1;
+               uc_data->state_cntr = 0;
+               break;
+       }
+}
+
+static void reset_handler(enum cpcap_irqs irq, void *data)
+{
+       int i;
+       unsigned short regval;
+       struct cpcap_uc_data *uc_data = data;
+
+       if (irq != CPCAP_IRQ_UCRESET)
+               return;
+
+       cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_UCC1,
+                          CPCAP_BIT_PRIHALT, CPCAP_BIT_PRIHALT);
+
+       cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_PGC,
+                          CPCAP_BIT_PRI_UC_SUSPEND, CPCAP_BIT_PRI_UC_SUSPEND);
+
+       uc_data->uc_reset = 1;
+       uc_data->cb_status = -EIO;
+       complete(&uc_data->completion);
+
+       cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MI2, 0, 0xFFFF);
+       cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MIM1, 0xFFFF, 0xFFFF);
+       cpcap_irq_mask(uc_data->cpcap, CPCAP_IRQ_PRIMAC);
+       cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UCRESET);
+
+       for (i = 0; i <= CPCAP_REG_END; i++) {
+               cpcap_regacc_read(uc_data->cpcap, i, &regval);
+               dev_err(&uc_data->cpcap->spi->dev,
+                       "cpcap reg %d = 0x%04X\n", i, regval);
+       }
+
+       BUG();
+}
+
+static void primac_handler(enum cpcap_irqs irq, void *data)
+{
+       struct cpcap_uc_data *uc_data = data;
+
+       if (irq == CPCAP_IRQ_PRIMAC)
+               cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_PRIMAC);
+}
+
+static int ram_write(struct cpcap_uc_data *uc_data, unsigned short address,
+                    unsigned short num_words, unsigned short *data)
+{
+       int retval = -EFAULT;
+
+       mutex_lock(&uc_data->lock);
+
+       if ((uc_data->cpcap->vendor == CPCAP_VENDOR_ST) &&
+           (uc_data->cpcap->revision <= CPCAP_REVISION_2_0)) {
+               cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_UCTM,
+                                  CPCAP_BIT_UCTM, CPCAP_BIT_UCTM);
+       }
+
+       if (uc_data->is_supported && (num_words > 0) &&
+               (data != NULL) &&
+               is_valid_address(uc_data->cpcap, address, num_words) &&
+           !uc_data->uc_reset) {
+               uc_data->req.address = address;
+               uc_data->req.data = data;
+               uc_data->req.num_words = num_words;
+               uc_data->state = WRITE_STATE_1;
+               uc_data->state_cntr = 0;
+               INIT_COMPLETION(uc_data->completion);
+
+               retval = cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MI2,
+                                       CPCAP_BIT_PRIRAMW,
+                                       CPCAP_BIT_PRIRAMW);
+               if (retval)
+                       goto err;
+
+               /* Cannot call cpcap_irq_register() here because unregister
+                * cannot be called from the state machine. Doing so causes
+                * a deadlock. */
+               retval = cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMW);
+               if (retval)
+                       goto err;
+
+               wait_for_completion(&uc_data->completion);
+               retval = uc_data->cb_status;
+       }
+
+err:
+       if ((uc_data->cpcap->vendor == CPCAP_VENDOR_ST) &&
+           (uc_data->cpcap->revision <= CPCAP_REVISION_2_0)) {
+               cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_UCTM,
+                                  0, CPCAP_BIT_UCTM);
+       }
+
+       mutex_unlock(&uc_data->lock);
+       return retval;
+}
+
+static int ram_read(struct cpcap_uc_data *uc_data, unsigned short address,
+                   unsigned short num_words, unsigned short *data)
+{
+       int retval = -EFAULT;
+
+       mutex_lock(&uc_data->lock);
+
+       if ((uc_data->cpcap->vendor == CPCAP_VENDOR_ST) &&
+           (uc_data->cpcap->revision <= CPCAP_REVISION_2_0)) {
+               cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_UCTM,
+                                  CPCAP_BIT_UCTM, CPCAP_BIT_UCTM);
+       }
+
+       if (uc_data->is_supported && (num_words > 0) &&
+           is_valid_address(uc_data->cpcap, address, num_words) &&
+               !uc_data->uc_reset) {
+               uc_data->req.address = address;
+               uc_data->req.data = data;
+               uc_data->req.num_words = num_words;
+               uc_data->state = READ_STATE_1;
+               uc_data->state_cntr = 0;
+               INIT_COMPLETION(uc_data->completion);
+
+               retval = cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MI2,
+                                           CPCAP_BIT_PRIRAMR,
+                                           CPCAP_BIT_PRIRAMR);
+               if (retval)
+                       goto err;
+
+               /* Cannot call cpcap_irq_register() here because unregister
+                * cannot be called from the state machine. Doing so causes
+                * a deadlock. */
+               retval = cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMR);
+               if (retval)
+                       goto err;
+
+               wait_for_completion(&uc_data->completion);
+               retval = uc_data->cb_status;
+       }
+
+err:
+       if ((uc_data->cpcap->vendor == CPCAP_VENDOR_ST) &&
+           (uc_data->cpcap->revision <= CPCAP_REVISION_2_0)) {
+               cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_UCTM,
+                                  0, CPCAP_BIT_UCTM);
+       }
+
+       mutex_unlock(&uc_data->lock);
+       return retval;
+}
+
+static int ram_load(struct cpcap_uc_data *uc_data, unsigned int num_words,
+                   unsigned short *data)
+{
+       int retval = -EINVAL;
+
+       if ((data != NULL) && (num_words > 0))
+               retval = ram_write(uc_data, data[0], (num_words - 1),
+                                  (data + 1));
+
+       return retval;
+}
+
+static ssize_t fops_write(struct file *file, const char *buf,
+                         size_t count, loff_t *ppos)
+{
+       ssize_t retval = -EINVAL;
+       unsigned short address;
+       unsigned short num_words;
+       unsigned short *data;
+       struct cpcap_uc_data *uc_data = file->private_data;
+
+       if ((buf != NULL) && (ppos != NULL) && (count >= 2)) {
+               data = kzalloc(count, GFP_KERNEL);
+
+               if (data != NULL) {
+                       num_words = (unsigned short) (count >> 1);
+
+                       /* If the position (uC RAM address) is zero then the
+                        * data contains the address */
+                       if (*ppos == 0) {
+                               if (copy_from_user((void *) data, (void *) buf,
+                                                  count) == 0)
+                                       retval = ram_load(uc_data, num_words,
+                                                         data);
+                               else
+                                       retval = -EFAULT;
+                       }
+                       /* If the position (uC RAM address) is not zero then the
+                        * position holds the address to load the data */
+                       else {
+                               address = (unsigned short) (*ppos);
+
+                               if (copy_from_user((void *) data, (void *) buf,
+                                                  count) == 0)
+                                       retval = ram_write(uc_data, address,
+                                                          num_words, data);
+                               else
+                                       retval = -EFAULT;
+                       }
+
+                       kfree(data);
+               } else {
+                       retval = -ENOMEM;
+               }
+       }
+
+       if (retval == 0)
+               retval = num_words;
+
+       return retval;
+}
+
+static ssize_t fops_read(struct file *file, char *buf,
+                        size_t count, loff_t *ppos)
+{
+       ssize_t retval = -EFAULT;
+       unsigned short address;
+       unsigned short num_words;
+       unsigned short *data;
+       struct cpcap_uc_data *uc_data = file->private_data;
+
+       if ((buf != NULL) && (ppos != NULL) && (count >= 2)) {
+               data = kzalloc(count, GFP_KERNEL);
+
+               if (data != NULL) {
+                       address = (unsigned short) (*ppos);
+                       num_words = (unsigned short) (count >> 1);
+
+                       retval = ram_read(uc_data, address, num_words, data);
+                       if (retval)
+                               goto err;
+
+                       if (copy_to_user((void *)buf, (void *)data, count) == 0)
+                               retval = count;
+                       else
+                               retval = -EFAULT;
+
+err:
+                       kfree(data);
+               } else {
+                       retval = -ENOMEM;
+               }
+       }
+
+       return retval;
+}
+
+static int fops_ioctl(struct inode *inode, struct file *file,
+                     unsigned int cmd, unsigned long arg)
+{
+       int retval = -ENOTTY;
+       struct cpcap_uc_data *data = file->private_data;
+
+       switch (cmd) {
+       case CPCAP_IOCTL_UC_MACRO_START:
+               /* User space will only attempt to start the init macro if
+                * the ram load requests complete successfully. This is used
+                * as an indication that kernel requests to start macros can
+                * be allowed.
+                */
+               data->is_ready = 1;
+
+               retval = cpcap_uc_start(data->cpcap, (enum cpcap_macro)arg);
+
+               break;
+
+       case CPCAP_IOCTL_UC_MACRO_STOP:
+               retval = cpcap_uc_stop(data->cpcap, (enum cpcap_macro)arg);
+               break;
+
+       case CPCAP_IOCTL_UC_GET_VENDOR:
+               retval = copy_to_user((enum cpcap_vendor *)arg,
+                                       &(data->cpcap->vendor),
+                                       sizeof(enum cpcap_vendor));
+               break;
+
+       case CPCAP_IOCTL_UC_SET_TURBO_MODE:
+               if (arg != 0)
+                       arg = 1;
+               retval = cpcap_regacc_write(data->cpcap, CPCAP_REG_UCTM,
+                                       (unsigned short)arg,
+                                       CPCAP_BIT_UCTM);
+               break;
+
+       default:
+               break;
+       }
+
+       return retval;
+}
+
+static int fops_open(struct inode *inode, struct file *file)
+{
+       int retval = -ENOTTY;
+
+       if (cpcap_uc_info->is_supported)
+               retval = 0;
+
+       file->private_data = cpcap_uc_info;
+       dev_info(&cpcap_uc_info->cpcap->spi->dev, "CPCAP uC: open status:%d\n",
+                retval);
+
+       return retval;
+}
+
+int cpcap_uc_start(struct cpcap_device *cpcap, enum cpcap_macro macro)
+{
+       int retval = -EFAULT;
+       struct cpcap_uc_data *data = cpcap->ucdata;
+
+       if ((data->is_ready) &&
+           (macro > CPCAP_MACRO_USEROFF) && (macro < CPCAP_MACRO__END) &&
+           (data->uc_reset == 0)) {
+               if ((macro == CPCAP_MACRO_4) ||
+                   ((cpcap->vendor == CPCAP_VENDOR_ST) &&
+                    (macro == CPCAP_MACRO_12))) {
+                       retval = cpcap_regacc_write(cpcap, CPCAP_REG_MI2,
+                                                   (1 << macro),
+                                                   (1 << macro));
+               } else {
+                       retval = cpcap_regacc_write(cpcap, CPCAP_REG_MIM1,
+                                                   0, (1 << macro));
+               }
+       }
+
+       return retval;
+}
+EXPORT_SYMBOL_GPL(cpcap_uc_start);
+
+int cpcap_uc_stop(struct cpcap_device *cpcap, enum cpcap_macro macro)
+{
+       int retval = -EFAULT;
+
+       if ((macro > CPCAP_MACRO_4) &&
+           (macro < CPCAP_MACRO__END)) {
+               if ((cpcap->vendor == CPCAP_VENDOR_ST) &&
+                    (macro == CPCAP_MACRO_12)) {
+                       retval = cpcap_regacc_write(cpcap, CPCAP_REG_MI2,
+                                                   0, (1 << macro));
+               } else {
+                       retval = cpcap_regacc_write(cpcap, CPCAP_REG_MIM1,
+                                                   (1 << macro), (1 << macro));
+               }
+       }
+
+       return retval;
+}
+EXPORT_SYMBOL_GPL(cpcap_uc_stop);
+
+unsigned char cpcap_uc_status(struct cpcap_device *cpcap,
+                             enum cpcap_macro macro)
+{
+       unsigned char retval = 0;
+       unsigned short regval;
+
+       if (macro < CPCAP_MACRO__END) {
+               if ((macro <= CPCAP_MACRO_4) ||
+                   ((cpcap->vendor == CPCAP_VENDOR_ST) &&
+                    (macro == CPCAP_MACRO_12))) {
+                       cpcap_regacc_read(cpcap, CPCAP_REG_MI2, &regval);
+
+                       if (regval & (1 << macro))
+                               retval = 1;
+               } else {
+                       cpcap_regacc_read(cpcap, CPCAP_REG_MIM1, &regval);
+
+                       if (!(regval & (1 << macro)))
+                               retval = 1;
+               }
+       }
+
+       return retval;
+}
+EXPORT_SYMBOL_GPL(cpcap_uc_status);
+
+static int fw_load(struct cpcap_uc_data *uc_data, struct device *dev)
+{
+       int err;
+       const struct ihex_binrec *rec;
+       const struct firmware *fw;
+       unsigned short *buf;
+       int i;
+       unsigned short num_bytes;
+       unsigned short num_words;
+       unsigned char odd_bytes;
+
+       if (!uc_data || !dev)
+               return -EINVAL;
+
+       if (uc_data->cpcap->vendor == CPCAP_VENDOR_ST)
+               err = request_ihex_firmware(&fw, "cpcap/firmware_0_2x.fw", dev);
+       else
+               err = request_ihex_firmware(&fw, "cpcap/firmware_1_2x.fw", dev);
+
+       if (err) {
+               dev_err(dev, "Failed to load \"cpcap/firmware_%d_2x.fw\": %d\n",
+                       uc_data->cpcap->vendor, err);
+               goto err;
+       }
+
+       for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) {
+               odd_bytes = 0;
+               num_bytes = be16_to_cpu(rec->len);
+
+               /* Since loader requires words, need even number of bytes. */
+               if (be16_to_cpu(rec->len) % 2) {
+                       num_bytes++;
+                       odd_bytes = 1;
+               }
+
+               num_words = num_bytes >> 1;
+               dev_info(dev, "Loading %d word(s) at 0x%04x\n",
+                        num_words, be32_to_cpu(rec->addr));
+
+               buf = kzalloc(num_bytes, GFP_KERNEL);
+               if (buf) {
+                       for (i = 0; i < num_words; i++) {
+                               if (odd_bytes && (i == (num_words - 1)))
+                                       buf[i] = rec->data[i * 2];
+                               else
+                                       buf[i] = ((uint16_t *)rec->data)[i];
+
+                               buf[i] = be16_to_cpu(buf[i]);
+                       }
+
+                       err = ram_write(uc_data, be32_to_cpu(rec->addr),
+                                       num_words, buf);
+                       kfree(buf);
+
+                       if (err) {
+                               dev_err(dev, "RAM write failed: %d\n", err);
+                               break;
+                       }
+               } else {
+                       err = -ENOMEM;
+                       dev_err(dev, "RAM write failed: %d\n", err);
+                       break;
+               }
+       }
+
+       release_firmware(fw);
+
+       if (!err) {
+               uc_data->is_ready = 1;
+
+               err = cpcap_uc_start(uc_data->cpcap, CPCAP_MACRO_4);
+               dev_info(dev, "Started macro 4: %d\n", err);
+       }
+
+err:
+       return err;
+}
+
+static int cpcap_uc_probe(struct platform_device *pdev)
+{
+       int retval = 0;
+       struct cpcap_uc_data *data;
+
+       if (pdev->dev.platform_data == NULL) {
+               dev_err(&pdev->dev, "no platform_data\n");
+               return -EINVAL;
+       }
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->cpcap = pdev->dev.platform_data;
+       data->uc_reset = 0;
+       data->is_supported = 0;
+       data->req.address = 0;
+       data->req.data = NULL;
+       data->req.num_words = 0;
+
+       init_completion(&data->completion);
+       mutex_init(&data->lock);
+       platform_set_drvdata(pdev, data);
+       cpcap_uc_info = data;
+       data->cpcap->ucdata = data;
+
+       if (((data->cpcap->vendor == CPCAP_VENDOR_TI) &&
+            (data->cpcap->revision >= CPCAP_REVISION_2_0)) ||
+               (data->cpcap->vendor == CPCAP_VENDOR_ST)) {
+               retval = cpcap_irq_register(data->cpcap, CPCAP_IRQ_PRIMAC,
+                                           primac_handler, data);
+               if (retval)
+                       goto err_free;
+
+               cpcap_irq_clear(data->cpcap, CPCAP_IRQ_UCRESET);
+               retval = cpcap_irq_register(data->cpcap, CPCAP_IRQ_UCRESET,
+                                           reset_handler, data);
+               if (retval)
+                       goto err_primac;
+
+               retval = cpcap_irq_register(data->cpcap,
+                                           CPCAP_IRQ_UC_PRIRAMR,
+                                           ram_read_state_machine, data);
+               if (retval)
+                       goto err_ucreset;
+
+               retval = cpcap_irq_register(data->cpcap,
+                                           CPCAP_IRQ_UC_PRIRAMW,
+                                           ram_write_state_machine, data);
+               if (retval)
+                       goto err_priramr;
+
+               retval = misc_register(&uc_dev);
+               if (retval)
+                       goto err_priramw;
+
+               data->is_supported = 1;
+
+               cpcap_regacc_write(data->cpcap, CPCAP_REG_MIM1, 0xFFFF,
+                                  0xFFFF);
+
+               retval = fw_load(data, &pdev->dev);
+               if (retval)
+                       goto err_fw;
+       } else
+               retval = -ENODEV;
+
+       return retval;
+
+err_fw:
+       misc_deregister(&uc_dev);
+err_priramw:
+       cpcap_irq_free(data->cpcap, CPCAP_IRQ_UC_PRIRAMW);
+err_priramr:
+       cpcap_irq_free(data->cpcap, CPCAP_IRQ_UC_PRIRAMR);
+err_ucreset:
+       cpcap_irq_free(data->cpcap, CPCAP_IRQ_UCRESET);
+err_primac:
+       cpcap_irq_free(data->cpcap, CPCAP_IRQ_PRIMAC);
+err_free:
+       kfree(data);
+
+       return retval;
+}
+
+static int __exit cpcap_uc_remove(struct platform_device *pdev)
+{
+       struct cpcap_uc_data *data = platform_get_drvdata(pdev);
+
+       misc_deregister(&uc_dev);
+
+       cpcap_irq_free(data->cpcap, CPCAP_IRQ_PRIMAC);
+       cpcap_irq_free(data->cpcap, CPCAP_IRQ_UC_PRIRAMW);
+       cpcap_irq_free(data->cpcap, CPCAP_IRQ_UC_PRIRAMR);
+       cpcap_irq_free(data->cpcap, CPCAP_IRQ_UCRESET);
+
+       kfree(data);
+       return 0;
+}
+
+
+static struct platform_driver cpcap_uc_driver = {
+       .probe          = cpcap_uc_probe,
+       .remove         = __exit_p(cpcap_uc_remove),
+       .driver         = {
+               .name   = "cpcap_uc",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init cpcap_uc_init(void)
+{
+       return platform_driver_register(&cpcap_uc_driver);
+}
+subsys_initcall(cpcap_uc_init);
+
+static void __exit cpcap_uc_exit(void)
+{
+       platform_driver_unregister(&cpcap_uc_driver);
+}
+module_exit(cpcap_uc_exit);
+
+MODULE_ALIAS("platform:cpcap_uc");
+MODULE_DESCRIPTION("CPCAP uC driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("cpcap/firmware_0_2x.fw");
+MODULE_FIRMWARE("cpcap/firmware_1_2x.fw");
diff --git a/drivers/mfd/cpcap-whisper.c b/drivers/mfd/cpcap-whisper.c
new file mode 100644 (file)
index 0000000..68bd873
--- /dev/null
@@ -0,0 +1,551 @@
+/*
+ * Copyright (C) 2010 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/switch.h>
+#include <linux/wakelock.h>
+#include <linux/workqueue.h>
+
+#include <linux/regulator/consumer.h>
+
+#include <linux/spi/cpcap.h>
+#include <linux/spi/cpcap-regbits.h>
+#include <linux/spi/spi.h>
+
+
+#define SENSE_USB_CLIENT    (CPCAP_BIT_ID_FLOAT_S  | \
+                            CPCAP_BIT_VBUSVLD_S   | \
+                            CPCAP_BIT_SESSVLD_S)
+
+#define SENSE_USB_FLASH     (CPCAP_BIT_VBUSVLD_S   | \
+                            CPCAP_BIT_SESSVLD_S)
+
+#define SENSE_USB_HOST      (CPCAP_BIT_ID_GROUND_S)
+
+#define SENSE_FACTORY       (CPCAP_BIT_ID_FLOAT_S  | \
+                            CPCAP_BIT_ID_GROUND_S | \
+                            CPCAP_BIT_VBUSVLD_S   | \
+                            CPCAP_BIT_SESSVLD_S)
+
+#define SENSE_WHISPER_PPD   (CPCAP_BIT_SE1_S)
+
+/* TODO: Update with appropriate value. */
+#define ADC_AUDIO_THRES     0x12C
+
+enum cpcap_det_state {
+       CONFIG,
+       SAMPLE_1,
+       SAMPLE_2,
+       IDENTIFY,
+       WHISPER,
+};
+
+enum cpcap_accy {
+       CPCAP_ACCY_USB,
+       CPCAP_ACCY_WHISPER,
+       CPCAP_ACCY_NONE,
+
+       /* Used while debouncing the accessory. */
+       CPCAP_ACCY_UNKNOWN,
+};
+
+enum {
+       NO_DOCK,
+       DESK_DOCK,
+       CAR_DOCK,
+};
+
+struct cpcap_whisper_data {
+       struct cpcap_device *cpcap;
+       struct cpcap_whisper_pdata *pdata;
+       struct delayed_work work;
+       unsigned short sense;
+       unsigned short prev_sense;
+       enum cpcap_det_state state;
+       struct regulator *regulator;
+       struct wake_lock wake_lock;
+       unsigned char is_vusb_enabled;
+       struct switch_dev wsdev;
+       struct switch_dev dsdev;
+       unsigned char audio;
+};
+
+static int whisper_debug;
+module_param(whisper_debug, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+static ssize_t print_name(struct switch_dev *dsdev, char *buf)
+{
+       switch (switch_get_state(dsdev)) {
+       case NO_DOCK:
+               return sprintf(buf, "None\n");
+       case DESK_DOCK:
+               return sprintf(buf, "DESK\n");
+       case CAR_DOCK:
+               return sprintf(buf, "CAR\n");
+       }
+
+       return -EINVAL;
+}
+
+static void vusb_enable(struct cpcap_whisper_data *data)
+{
+       if (!data->is_vusb_enabled) {
+               wake_lock(&data->wake_lock);
+               regulator_enable(data->regulator);
+               data->is_vusb_enabled = 1;
+       }
+}
+
+static void vusb_disable(struct cpcap_whisper_data *data)
+{
+       if (data->is_vusb_enabled) {
+               wake_unlock(&data->wake_lock);
+               regulator_disable(data->regulator);
+               data->is_vusb_enabled = 0;
+       }
+}
+
+static int get_sense(struct cpcap_whisper_data *data)
+{
+       int retval = -EFAULT;
+       unsigned short value;
+       struct cpcap_device *cpcap;
+
+       if (!data)
+               return -EFAULT;
+       cpcap = data->cpcap;
+
+       retval = cpcap_regacc_read(cpcap, CPCAP_REG_INTS1, &value);
+       if (retval)
+               return retval;
+
+       /* Clear ASAP after read. */
+       retval = cpcap_regacc_write(cpcap, CPCAP_REG_INT1,
+                                    (CPCAP_BIT_CHRG_DET_I |
+                                     CPCAP_BIT_ID_GROUND_I),
+                                    (CPCAP_BIT_CHRG_DET_I |
+                                     CPCAP_BIT_ID_GROUND_I));
+       if (retval)
+               return retval;
+
+       data->sense = value & (CPCAP_BIT_ID_FLOAT_S |
+                              CPCAP_BIT_ID_GROUND_S);
+
+       retval = cpcap_regacc_read(cpcap, CPCAP_REG_INTS2, &value);
+       if (retval)
+               return retval;
+
+       /* Clear ASAP after read. */
+       retval = cpcap_regacc_write(cpcap, CPCAP_REG_INT2,
+                                   (CPCAP_BIT_VBUSVLD_I |
+                                    CPCAP_BIT_SESSVLD_I |
+                                    CPCAP_BIT_SE1_I),
+                                   (CPCAP_BIT_VBUSVLD_I |
+                                    CPCAP_BIT_SESSVLD_I |
+                                    CPCAP_BIT_SE1_I));
+       if (retval)
+               return retval;
+
+       data->sense |= value & (CPCAP_BIT_VBUSVLD_S |
+                               CPCAP_BIT_SESSVLD_S |
+                               CPCAP_BIT_SE1_S);
+       return 0;
+}
+
+static int configure_hardware(struct cpcap_whisper_data *data,
+                             enum cpcap_accy accy)
+{
+       int retval;
+
+       retval = cpcap_regacc_write(data->cpcap, CPCAP_REG_USBC1,
+                                   CPCAP_BIT_DP150KPU,
+                                   (CPCAP_BIT_DP150KPU | CPCAP_BIT_DP1K5PU |
+                                    CPCAP_BIT_DM1K5PU | CPCAP_BIT_DPPD |
+                                    CPCAP_BIT_DMPD));
+
+       switch (accy) {
+       case CPCAP_ACCY_USB:
+               retval |= cpcap_regacc_write(data->cpcap, CPCAP_REG_USBC1, 0,
+                                            CPCAP_BIT_VBUSPD);
+               gpio_set_value(data->pdata->gpio, 1);
+               /* TODO: Does this driver enable "reverse mode" for hosts? */
+               break;
+
+       case CPCAP_ACCY_WHISPER:
+               retval |= cpcap_regacc_write(data->cpcap, CPCAP_REG_USBC1, 0,
+                                            CPCAP_BIT_VBUSPD);
+               retval |= cpcap_regacc_write(data->cpcap, CPCAP_REG_USBC2,
+                                            ((data->pdata->uartmux << 8) |
+                                             CPCAP_BIT_EMUMODE0),
+                                            (CPCAP_BIT_UARTMUX1 |
+                                             CPCAP_BIT_UARTMUX0 |
+                                             CPCAP_BIT_EMUMODE2 |
+                                             CPCAP_BIT_EMUMODE1 |
+                                             CPCAP_BIT_EMUMODE0));
+               /* TODO: Need to turn on "reverse mode" for PPD's */
+               break;
+
+       case CPCAP_ACCY_UNKNOWN:
+               gpio_set_value(data->pdata->gpio, 0);
+               retval |= cpcap_regacc_write(data->cpcap, CPCAP_REG_USBC1, 0,
+                                            (CPCAP_BIT_VBUSPD |
+                                             CPCAP_BIT_ID100KPU));
+               retval |= cpcap_regacc_write(data->cpcap, CPCAP_REG_USBC2, 0,
+                                            (CPCAP_BIT_EMUMODE2 |
+                                             CPCAP_BIT_EMUMODE1 |
+                                             CPCAP_BIT_EMUMODE0));
+               /* TODO: Need to turn off "reverse mode" */
+               break;
+
+       case CPCAP_ACCY_NONE:
+       default:
+               retval |= cpcap_regacc_write(data->cpcap, CPCAP_REG_USBC1,
+                                            CPCAP_BIT_VBUSPD,
+                                            CPCAP_BIT_VBUSPD);
+               retval |= cpcap_regacc_write(data->cpcap, CPCAP_REG_USBC2, 0,
+                                            CPCAP_BIT_USBXCVREN);
+               vusb_disable(data);
+               break;
+       }
+
+       if (retval != 0)
+               retval = -EFAULT;
+
+       return retval;
+}
+
+static const char *accy_names[3] = {"USB", "whisper", "none"};
+
+static void whisper_notify(struct cpcap_whisper_data *di, enum cpcap_accy accy)
+{
+       pr_info("%s: accy=%s\n", __func__, accy_names[accy]);
+
+       configure_hardware(di, accy);
+
+       if (accy == CPCAP_ACCY_WHISPER)
+               switch_set_state(&di->wsdev, 1);
+       else {
+               switch_set_state(&di->wsdev, 0);
+               switch_set_state(&di->dsdev, NO_DOCK);
+       }
+}
+
+static void whisper_audio_check(struct cpcap_whisper_data *di)
+{
+       struct cpcap_adc_request req;
+       int ret;
+       unsigned short value;
+
+       cpcap_regacc_read(di->cpcap, CPCAP_REG_USBC1, &value);
+       value &= CPCAP_BIT_ID100KPU;
+
+       cpcap_regacc_write(di->cpcap, CPCAP_REG_USBC1, CPCAP_BIT_IDPUCNTRL,
+                          (CPCAP_BIT_ID100KPU | CPCAP_BIT_IDPUCNTRL));
+
+       mdelay(1);
+
+       req.format = CPCAP_ADC_FORMAT_RAW;
+       req.timing = CPCAP_ADC_TIMING_IMM;
+       req.type = CPCAP_ADC_TYPE_BANK_0;
+
+       ret = cpcap_adc_sync_read(di->cpcap, &req);
+
+       cpcap_regacc_write(di->cpcap, CPCAP_REG_USBC1, value,
+                          (CPCAP_BIT_ID100KPU | CPCAP_BIT_IDPUCNTRL));
+
+       if (whisper_debug)
+               pr_info("%s: ADC result=0x%X (ret=%d, status=%d)\n", __func__,
+                       req.result[CPCAP_ADC_USB_ID], ret, req.status);
+
+       di->audio = (req.result[CPCAP_ADC_USB_ID] > ADC_AUDIO_THRES) ? 1 : 0;
+
+       pr_info("%s: Audio cable %s present\n", __func__,
+               (di->audio ? "is" : "not"));
+}
+
+static void whisper_det_work(struct work_struct *work)
+{
+       struct cpcap_whisper_data *data =
+               container_of(work, struct cpcap_whisper_data, work.work);
+
+       switch (data->state) {
+       case CONFIG:
+               vusb_enable(data);
+               cpcap_irq_mask(data->cpcap, CPCAP_IRQ_CHRG_DET);
+               cpcap_irq_mask(data->cpcap, CPCAP_IRQ_IDFLOAT);
+               cpcap_irq_mask(data->cpcap, CPCAP_IRQ_IDGND);
+
+               configure_hardware(data, CPCAP_ACCY_UNKNOWN);
+
+               data->state = SAMPLE_1;
+               schedule_delayed_work(&data->work, msecs_to_jiffies(11));
+               break;
+
+       case SAMPLE_1:
+               get_sense(data);
+               data->state = SAMPLE_2;
+               schedule_delayed_work(&data->work, msecs_to_jiffies(100));
+               break;
+
+       case SAMPLE_2:
+               data->prev_sense = data->sense;
+               get_sense(data);
+
+               if (data->prev_sense != data->sense) {
+                       /* Stay in this state */
+                       data->state = SAMPLE_2;
+                       schedule_delayed_work(&data->work,
+                                             msecs_to_jiffies(100));
+               } else if (!(data->sense & CPCAP_BIT_SE1_S) &&
+                          (data->sense & CPCAP_BIT_ID_FLOAT_S) &&
+                          !(data->sense & CPCAP_BIT_ID_GROUND_S) &&
+                          !(data->sense & CPCAP_BIT_SESSVLD_S)) {
+                       data->state = IDENTIFY;
+                       schedule_delayed_work(&data->work,
+                                             msecs_to_jiffies(100));
+               } else {
+                       data->state = IDENTIFY;
+                       schedule_delayed_work(&data->work, 0);
+               }
+               break;
+
+       case IDENTIFY:
+               get_sense(data);
+               data->state = CONFIG;
+
+               if (whisper_debug)
+                       pr_info("%s: sense=0x%04x\n", __func__, data->sense);
+
+               if ((data->sense == SENSE_USB_CLIENT) ||
+                   (data->sense == SENSE_USB_FLASH) ||
+                   (data->sense == SENSE_FACTORY)) {
+                       whisper_notify(data, CPCAP_ACCY_USB);
+
+                       cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_CHRG_DET);
+               } else if (data->sense == SENSE_USB_HOST) {
+                       whisper_notify(data, CPCAP_ACCY_USB);
+
+                       cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_IDFLOAT);
+               } else if (data->sense == SENSE_WHISPER_PPD) {
+                       whisper_notify(data, CPCAP_ACCY_WHISPER);
+                       whisper_audio_check(data);
+
+                       cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_IDFLOAT);
+                       cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_IDGND);
+
+                       /* Special handling of Whisper undetect. */
+                       data->state = WHISPER;
+               } else {
+                       whisper_notify(data, CPCAP_ACCY_NONE);
+
+                       cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_CHRG_DET);
+                       cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_IDFLOAT);
+               }
+               break;
+
+       case WHISPER:
+               get_sense(data);
+
+               /* The removal of a Whisper accessory can only be detected
+                * if ID is floating.
+                */
+               if (data->sense & CPCAP_BIT_ID_FLOAT_S) {
+                       data->state = CONFIG;
+                       schedule_delayed_work(&data->work, 0);
+               } else {
+                       if (!(data->sense & CPCAP_BIT_ID_GROUND_S)) {
+                               whisper_audio_check(data);
+                       }
+
+                       cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_IDFLOAT);
+                       cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_IDGND);
+               }
+               break;
+
+       default:
+               /* This shouldn't happen.  Need to reset state machine. */
+               vusb_disable(data);
+               data->state = CONFIG;
+               schedule_delayed_work(&data->work, 0);
+               break;
+       }
+}
+
+static void whisper_int_handler(enum cpcap_irqs int_event, void *data)
+{
+       struct cpcap_whisper_data *di = data;
+
+       if (whisper_debug)
+               pr_info("%s: irq=%d\n", __func__, int_event);
+
+       schedule_delayed_work(&(di->work), 0);
+}
+
+int cpcap_accy_whisper(struct cpcap_device *cpcap, unsigned long cmd)
+{
+       struct cpcap_whisper_data *di = cpcap->accydata;
+       int retval = -EAGAIN;
+       unsigned short value;
+
+       if (!di)
+               return -ENODEV;
+
+       /* Can only change settings if not debouncing and whisper device
+        * is present. */
+       if (di->state == WHISPER) {
+               value = (cmd & CPCAP_WHISPER_MODE_PU) ? CPCAP_BIT_ID100KPU : 0;
+
+               retval = cpcap_regacc_write(cpcap, CPCAP_REG_USBC1,
+                                           value, CPCAP_BIT_ID100KPU);
+
+               /* TODO: Report dock type to system. */
+       }
+
+       return retval;
+}
+
+static int __init cpcap_whisper_probe(struct platform_device *pdev)
+{
+       int retval;
+       struct cpcap_whisper_data *data;
+
+       if (pdev->dev.platform_data == NULL) {
+               dev_err(&pdev->dev, "no platform_data\n");
+               return -EINVAL;
+       }
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->pdata = pdev->dev.platform_data;
+       data->cpcap = platform_get_drvdata(pdev);
+       data->state = CONFIG;
+       INIT_DELAYED_WORK(&data->work, whisper_det_work);
+       wake_lock_init(&data->wake_lock, WAKE_LOCK_SUSPEND, "whisper");
+
+       data->wsdev.name = "whisper";
+       switch_dev_register(&data->wsdev);
+
+       data->dsdev.name = "dock";
+       data->dsdev.print_name = print_name;
+       switch_dev_register(&data->dsdev);
+
+       platform_set_drvdata(pdev, data);
+
+       data->regulator = regulator_get(&pdev->dev, "vusb");
+       if (IS_ERR(data->regulator)) {
+               dev_err(&pdev->dev,
+                       "Could not get regulator for cpcap_whisper\n");
+               retval = PTR_ERR(data->regulator);
+               goto free_mem;
+       }
+       regulator_set_voltage(data->regulator, 3300000, 3300000);
+
+       retval = cpcap_irq_register(data->cpcap, CPCAP_IRQ_CHRG_DET,
+                                   whisper_int_handler, data);
+       retval |= cpcap_irq_register(data->cpcap, CPCAP_IRQ_IDFLOAT,
+                                    whisper_int_handler, data);
+       retval |= cpcap_irq_register(data->cpcap, CPCAP_IRQ_IDGND,
+                                    whisper_int_handler, data);
+
+       if (retval != 0) {
+               dev_err(&pdev->dev, "Initialization Error\n");
+               retval = -ENODEV;
+               goto free_irqs;
+       }
+
+       data->cpcap->accydata = data;
+       dev_info(&pdev->dev, "CPCAP Whisper detection probed\n");
+
+       /* Perform initial detection */
+       whisper_det_work(&(data->work.work));
+
+       return 0;
+
+free_irqs:
+       cpcap_irq_free(data->cpcap, CPCAP_IRQ_IDGND);
+       cpcap_irq_free(data->cpcap, CPCAP_IRQ_IDFLOAT);
+       cpcap_irq_free(data->cpcap, CPCAP_IRQ_CHRG_DET);
+       regulator_put(data->regulator);
+free_mem:
+       switch_dev_unregister(&data->wsdev);
+       switch_dev_unregister(&data->dsdev);
+       wake_lock_destroy(&data->wake_lock);
+       kfree(data);
+
+       return retval;
+}
+
+static int __exit cpcap_whisper_remove(struct platform_device *pdev)
+{
+       struct cpcap_whisper_data *data = platform_get_drvdata(pdev);
+
+       cpcap_irq_free(data->cpcap, CPCAP_IRQ_CHRG_DET);
+       cpcap_irq_free(data->cpcap, CPCAP_IRQ_IDFLOAT);
+       cpcap_irq_free(data->cpcap, CPCAP_IRQ_IDGND);
+
+       configure_hardware(data, CPCAP_ACCY_NONE);
+       cancel_delayed_work_sync(&data->work);
+
+       switch_dev_unregister(&data->wsdev);
+       switch_dev_unregister(&data->dsdev);
+
+       gpio_set_value(data->pdata->gpio, 1);
+
+       vusb_disable(data);
+       regulator_put(data->regulator);
+
+       wake_lock_destroy(&data->wake_lock);
+
+       data->cpcap->accydata = NULL;
+       kfree(data);
+
+       return 0;
+}
+
+static struct platform_driver cpcap_whisper_driver = {
+       .probe          = cpcap_whisper_probe,
+       .remove         = __exit_p(cpcap_whisper_remove),
+       .driver         = {
+               .name   = "cpcap_whisper",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init cpcap_whisper_init(void)
+{
+       return cpcap_driver_register(&cpcap_whisper_driver);
+}
+late_initcall(cpcap_whisper_init);
+
+static void __exit cpcap_whisper_exit(void)
+{
+       cpcap_driver_unregister(&cpcap_whisper_driver);
+}
+module_exit(cpcap_whisper_exit);
+
+MODULE_ALIAS("platform:cpcap_whisper");
+MODULE_DESCRIPTION("CPCAP Whisper detection driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
index 172951bf23a4c01e3f440b6b9ce85911d53cb174..d520bc0fd23a44488d62382878c47c97d8599875 100644 (file)
@@ -235,5 +235,11 @@ config REGULATOR_TPS6586X
        help
          This driver supports TPS6586X voltage regulator chips.
 
        help
          This driver supports TPS6586X voltage regulator chips.
 
+config REGULATOR_CPCAP
+       tristate "CPCAP regulator driver"
+       depends on MFD_CPCAP
+       help
+         Say Y here to support the voltage regulators on CPCAP
+
 endif
 
 endif
 
index 8285fd832e16fbe9492cbd64cfd9604a015221f4..085384e7e27ef042cc2dc613b027b719cc63c298 100644 (file)
@@ -30,6 +30,7 @@ obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
 obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
 obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
 obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o
 obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
 obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
 obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o
+obj-$(CONFIG_REGULATOR_CPCAP) += cpcap-regulator.o
 
 obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o
 obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o
 
 obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o
 obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o
diff --git a/drivers/regulator/cpcap-regulator.c b/drivers/regulator/cpcap-regulator.c
new file mode 100644 (file)
index 0000000..d3049ad
--- /dev/null
@@ -0,0 +1,574 @@
+/*
+ * Copyright (C) 2009 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/cpcap.h>
+#include <linux/spi/cpcap-regbits.h>
+
+#define CPCAP_REGULATOR(_name, _id)            \
+       {                                       \
+               .name = _name,                  \
+               .id = _id,                      \
+               .ops = &cpcap_regulator_ops,    \
+               .type = REGULATOR_VOLTAGE,      \
+               .owner = THIS_MODULE,           \
+       }
+
+
+static const int sw5_val_tbl[] = {0, 5050000};
+static const int vcam_val_tbl[] = {2600000, 2700000, 2800000, 2900000};
+static const int vcsi_val_tbl[] = {1200000, 1800000};
+static const int vdac_val_tbl[] = {1200000, 1500000, 1800000, 2500000};
+static const int vdig_val_tbl[] = {1200000, 1350000, 1500000, 1875000};
+static const int vfuse_val_tbl[] = {1500000, 1600000, 1700000, 1800000, 1900000,
+                                   2000000, 2100000, 2200000, 2300000, 2400000,
+                                   2500000, 2600000, 2700000, 3150000};
+static const int vhvio_val_tbl[] = {2775000};
+static const int vsdio_val_tbl[] = {1500000, 1600000, 1800000, 2600000,
+                                   2700000, 2800000, 2900000, 3000000};
+static const int vpll_val_tbl[] = {1200000, 1300000, 1400000, 1800000};
+static const int vrf1_val_tbl[] = {2775000, 2500000}; /* Yes, this is correct */
+static const int vrf2_val_tbl[] = {0, 2775000};
+static const int vrfref_val_tbl[] = {2500000, 2775000};
+static const int vwlan1_val_tbl[] = {1800000, 1900000};
+static const int vwlan2_val_tbl[] = {2775000, 3000000, 3300000, 3300000};
+static const int vsim_val_tbl[] = {1800000, 2900000};
+static const int vsimcard_val_tbl[] = {1800000, 2900000};
+static const int vvib_val_tbl[] = {1300000, 1800000, 2000000, 3000000};
+static const int vusb_val_tbl[] = {0, 3300000};
+static const int vaudio_val_tbl[] = {0, 2775000};
+
+static struct {
+       const enum cpcap_reg reg;
+       const unsigned short mode_mask;
+       const unsigned short volt_mask;
+       const unsigned char volt_shft;
+       unsigned short mode_val;
+       unsigned short off_mode_val;
+       const int val_tbl_sz;
+       const int *val_tbl;
+       unsigned int mode_cntr;
+       const unsigned int volt_trans_time; /* in micro seconds */
+       const unsigned int turn_on_time; /* in micro seconds */
+} cpcap_regltr_data[CPCAP_NUM_REGULATORS] = {
+       [CPCAP_SW5]      = {CPCAP_REG_S5C,
+                           0x002A,
+                           0x0000,
+                           0,
+                           0x0000,
+                           0x0000,
+                           ARRAY_SIZE(sw5_val_tbl),
+                           sw5_val_tbl,
+                           0,
+                           0,
+                           1500},
+
+       [CPCAP_VCAM]     = {CPCAP_REG_VCAMC,
+                           0x0087,
+                           0x0030,
+                           4,
+                           0x0000,
+                           0x0000,
+                           ARRAY_SIZE(vcam_val_tbl),
+                           vcam_val_tbl,
+                           0,
+                           420,
+                           1000},
+
+       [CPCAP_VCSI]     = {CPCAP_REG_VCSIC,
+                           0x0047,
+                           0x0010,
+                           4,
+                           0x0000,
+                           0x0000,
+                           ARRAY_SIZE(vcsi_val_tbl),
+                           vcsi_val_tbl,
+                           0,
+                           350,
+                           1000},
+
+       [CPCAP_VDAC]     = {CPCAP_REG_VDACC,
+                           0x0087,
+                           0x0030,
+                           4,
+                           0x0000,
+                           0x0000,
+                           ARRAY_SIZE(vdac_val_tbl),
+                           vdac_val_tbl,
+                           0,
+                           420,
+                           1000},
+
+       [CPCAP_VDIG]     = {CPCAP_REG_VDIGC,
+                           0x0087,
+                           0x0030,
+                           4,
+                           0x0000,
+                           0x0000,
+                           ARRAY_SIZE(vdig_val_tbl),
+                           vdig_val_tbl,
+                           0,
+                           420,
+                           1000},
+
+       [CPCAP_VFUSE]    = {CPCAP_REG_VFUSEC,
+                           0x0080,
+                           0x000F,
+                           0,
+                           0x0000,
+                           0x0000,
+                           ARRAY_SIZE(vfuse_val_tbl),
+                           vfuse_val_tbl,
+                           0,
+                           420,
+                           1000},
+
+       [CPCAP_VHVIO]    = {CPCAP_REG_VHVIOC,
+                           0x0017,
+                           0x0000,
+                           0,
+                           0x0000,
+                           0x0000,
+                           ARRAY_SIZE(vhvio_val_tbl),
+                           vhvio_val_tbl,
+                           0,
+                           0,
+                           1000},
+
+       [CPCAP_VSDIO]    = {CPCAP_REG_VSDIOC,
+                           0x0087,
+                           0x0038,
+                           3,
+                           0x0000,
+                           0x0000,
+                           ARRAY_SIZE(vsdio_val_tbl),
+                           vsdio_val_tbl,
+                           0,
+                           420,
+                           1000},
+
+       [CPCAP_VPLL]     = {CPCAP_REG_VPLLC,
+                           0x0043,
+                           0x0018,
+                           3,
+                           0x0000,
+                           0x0000,
+                           ARRAY_SIZE(vpll_val_tbl),
+                           vpll_val_tbl,
+                           0,
+                           420,
+                           100},
+
+       [CPCAP_VRF1]     = {CPCAP_REG_VRF1C,
+                           0x00AC,
+                           0x0002,
+                           1,
+                           0x0000,
+                           0x0000,
+                           ARRAY_SIZE(vrf1_val_tbl),
+                           vrf1_val_tbl,
+                           0,
+                           10,
+                           1000},
+
+       [CPCAP_VRF2]     = {CPCAP_REG_VRF2C,
+                           0x0023,
+                           0x0008,
+                           3,
+                           0x0000,
+                           0x0000,
+                           ARRAY_SIZE(vrf2_val_tbl),
+                           vrf2_val_tbl,
+                           0,
+                           10,
+                           1000},
+
+       [CPCAP_VRFREF]   = {CPCAP_REG_VRFREFC,
+                           0x0023,
+                           0x0008,
+                           3,
+                           0x0000,
+                           0x0000,
+                           ARRAY_SIZE(vrfref_val_tbl),
+                           vrfref_val_tbl,
+                           0,
+                           420,
+                           100},
+
+       [CPCAP_VWLAN1]   = {CPCAP_REG_VWLAN1C,
+                           0x0047,
+                           0x0010,
+                           4,
+                           0x0000,
+                           0x0000,
+                           ARRAY_SIZE(vwlan1_val_tbl),
+                           vwlan1_val_tbl,
+                           0,
+                           420,
+                           1000},
+
+       [CPCAP_VWLAN2]   = {CPCAP_REG_VWLAN2C,
+                           0x020C,
+                           0x00C0,
+                           6,
+                           0x0000,
+                           0x0000,
+                           ARRAY_SIZE(vwlan2_val_tbl),
+                           vwlan2_val_tbl,
+                           0,
+                           420,
+                           1000},
+
+       [CPCAP_VSIM]     = {CPCAP_REG_VSIMC,
+                           0x0023,
+                           0x0008,
+                           3,
+                           0x0000,
+                           0x0000,
+                           ARRAY_SIZE(vsim_val_tbl),
+                           vsim_val_tbl,
+                           0,
+                           420,
+                           1000},
+
+       [CPCAP_VSIMCARD] = {CPCAP_REG_VSIMC,
+                           0x1E80,
+                           0x0008,
+                           3,
+                           0x0000,
+                           0x0000,
+                           ARRAY_SIZE(vsimcard_val_tbl),
+                           vsimcard_val_tbl,
+                           0,
+                           420,
+                           1000},
+
+       [CPCAP_VVIB]     = {CPCAP_REG_VVIBC,
+                           0x0001,
+                           0x000C,
+                           2,
+                           0x0000,
+                           0x0000,
+                           ARRAY_SIZE(vvib_val_tbl),
+                           vvib_val_tbl,
+                           0,
+                           500,
+                           500},
+
+       [CPCAP_VUSB]     = {CPCAP_REG_VUSBC,
+                           0x011C,
+                           0x0040,
+                           6,
+                           0x0000,
+                           0x0000,
+                           ARRAY_SIZE(vusb_val_tbl),
+                           vusb_val_tbl,
+                           0,
+                           0,
+                           1000},
+
+       [CPCAP_VAUDIO]   = {CPCAP_REG_VAUDIOC,
+                           0x0016,
+                           0x0001,
+                           0,
+                           0x0000,
+                           0x0000,
+                           ARRAY_SIZE(vaudio_val_tbl),
+                           vaudio_val_tbl,
+                           0,
+                           0,
+                           1000},
+};
+
+static int cpcap_regulator_set_voltage(struct regulator_dev *rdev,
+                                      int min_uV, int max_uV)
+{
+       struct cpcap_device *cpcap;
+       int regltr_id;
+       int retval;
+       enum cpcap_reg regnr;
+       int i;
+
+       cpcap = rdev_get_drvdata(rdev);
+
+       regltr_id = rdev_get_id(rdev);
+       if (regltr_id >= CPCAP_NUM_REGULATORS)
+               return -EINVAL;
+
+       regnr = cpcap_regltr_data[regltr_id].reg;
+
+       if (regltr_id == CPCAP_VRF1) {
+               if (min_uV > 2500000)
+                       i = 0;
+               else
+                       i = cpcap_regltr_data[regltr_id].volt_mask;
+       } else {
+               for (i = 0; i < cpcap_regltr_data[regltr_id].val_tbl_sz; i++)
+                       if (cpcap_regltr_data[regltr_id].val_tbl[i] >= min_uV)
+                               break;
+
+               if (i >= cpcap_regltr_data[regltr_id].val_tbl_sz)
+                       i--;
+
+               i <<= cpcap_regltr_data[regltr_id].volt_shft;
+       }
+
+       retval = cpcap_regacc_write(cpcap, regnr, i,
+                                   cpcap_regltr_data[regltr_id].volt_mask);
+
+       if ((cpcap_regltr_data[regltr_id].volt_trans_time) && (retval == 0))
+               udelay(cpcap_regltr_data[regltr_id].volt_trans_time);
+
+       return retval;
+}
+
+static int cpcap_regulator_get_voltage(struct regulator_dev *rdev)
+{
+       struct cpcap_device *cpcap;
+       int regltr_id;
+       unsigned short volt_bits;
+       enum cpcap_reg regnr;
+       unsigned int shift;
+
+       cpcap = rdev_get_drvdata(rdev);
+
+       regltr_id = rdev_get_id(rdev);
+       if (regltr_id >= CPCAP_NUM_REGULATORS)
+               return -EINVAL;
+
+       regnr = cpcap_regltr_data[regltr_id].reg;
+
+       if (cpcap_regacc_read(cpcap, regnr, &volt_bits) < 0)
+               return -1;
+
+       if (!(volt_bits & cpcap_regltr_data[regltr_id].mode_mask))
+               return 0;
+
+       volt_bits &= cpcap_regltr_data[regltr_id].volt_mask;
+       shift = cpcap_regltr_data[regltr_id].volt_shft;
+
+       return cpcap_regltr_data[regltr_id].val_tbl[volt_bits >> shift];
+}
+
+static int cpcap_regulator_enable(struct regulator_dev *rdev)
+{
+       struct cpcap_device *cpcap = rdev_get_drvdata(rdev);
+       int regltr_id;
+       int retval;
+       enum cpcap_reg regnr;
+
+       regltr_id = rdev_get_id(rdev);
+       if (regltr_id >= CPCAP_NUM_REGULATORS)
+               return -EINVAL;
+
+       regnr = cpcap_regltr_data[regltr_id].reg;
+
+       retval = cpcap_regacc_write(cpcap, regnr,
+                                   cpcap_regltr_data[regltr_id].mode_val,
+                                   cpcap_regltr_data[regltr_id].mode_mask);
+
+       if ((cpcap_regltr_data[regltr_id].turn_on_time) && (retval == 0))
+               udelay(cpcap_regltr_data[regltr_id].turn_on_time);
+
+       return retval;
+}
+
+static int cpcap_regulator_disable(struct regulator_dev *rdev)
+{
+       struct cpcap_device *cpcap = rdev_get_drvdata(rdev);
+       int regltr_id;
+       enum cpcap_reg regnr;
+
+       regltr_id = rdev_get_id(rdev);
+       if (regltr_id >= CPCAP_NUM_REGULATORS)
+               return -EINVAL;
+
+       regnr = cpcap_regltr_data[regltr_id].reg;
+
+       return cpcap_regacc_write(cpcap, regnr,
+                                 cpcap_regltr_data[regltr_id].off_mode_val,
+                                 cpcap_regltr_data[regltr_id].mode_mask);
+}
+
+static int cpcap_regulator_is_enabled(struct regulator_dev *rdev)
+{
+       struct cpcap_device *cpcap = rdev_get_drvdata(rdev);
+       int regltr_id;
+       enum cpcap_reg regnr;
+       unsigned short value;
+
+       regltr_id = rdev_get_id(rdev);
+       if (regltr_id >= CPCAP_NUM_REGULATORS)
+               return -EINVAL;
+
+       regnr = cpcap_regltr_data[regltr_id].reg;
+
+       if (cpcap_regacc_read(cpcap, regnr, &value))
+               return -1;
+
+       return (value & cpcap_regltr_data[regltr_id].mode_mask) ? 1 : 0;
+}
+
+static int cpcap_regulator_set_mode(struct regulator_dev *rdev,
+                                   unsigned int mode)
+{
+       struct cpcap_device *cpcap = rdev_get_drvdata(rdev);
+       int regltr_id;
+       enum cpcap_reg regnr;
+       int ret = 0;
+
+       regltr_id = rdev_get_id(rdev);
+       if (regltr_id != CPCAP_VAUDIO)
+               return -EINVAL;
+
+       regnr = cpcap_regltr_data[regltr_id].reg;
+
+       if (mode == REGULATOR_MODE_NORMAL) {
+               if (cpcap_regltr_data[regltr_id].mode_cntr == 0) {
+                       ret = cpcap_regacc_write(cpcap, regnr,
+                                                0,
+                                                CPCAP_BIT_AUDIO_LOW_PWR);
+               }
+               if (ret == 0)
+                       cpcap_regltr_data[regltr_id].mode_cntr++;
+       } else if (mode == REGULATOR_MODE_STANDBY) {
+               if (cpcap_regltr_data[regltr_id].mode_cntr == 1) {
+                       ret = cpcap_regacc_write(cpcap, regnr,
+                                                CPCAP_BIT_AUDIO_LOW_PWR,
+                                                CPCAP_BIT_AUDIO_LOW_PWR);
+               } else if (WARN((cpcap_regltr_data[regltr_id].mode_cntr == 0),
+                               "Unbalanced modes for supply vaudio\n"))
+                       ret = -EIO;
+
+               if (ret == 0)
+                       cpcap_regltr_data[regltr_id].mode_cntr--;
+       }
+
+       return ret;
+}
+
+static struct regulator_ops cpcap_regulator_ops = {
+       .set_voltage = cpcap_regulator_set_voltage,
+       .get_voltage = cpcap_regulator_get_voltage,
+       .enable = cpcap_regulator_enable,
+       .disable = cpcap_regulator_disable,
+       .is_enabled = cpcap_regulator_is_enabled,
+       .set_mode = cpcap_regulator_set_mode,
+};
+
+static struct regulator_desc regulators[] = {
+       [CPCAP_SW5]      = CPCAP_REGULATOR("sw5", CPCAP_SW5),
+       [CPCAP_VCAM]     = CPCAP_REGULATOR("vcam", CPCAP_VCAM),
+       [CPCAP_VCSI]     = CPCAP_REGULATOR("vcsi", CPCAP_VCSI),
+       [CPCAP_VDAC]     = CPCAP_REGULATOR("vdac", CPCAP_VDAC),
+       [CPCAP_VDIG]     = CPCAP_REGULATOR("vdig", CPCAP_VDIG),
+       [CPCAP_VFUSE]    = CPCAP_REGULATOR("vfuse", CPCAP_VFUSE),
+       [CPCAP_VHVIO]    = CPCAP_REGULATOR("vhvio", CPCAP_VHVIO),
+       [CPCAP_VSDIO]    = CPCAP_REGULATOR("vsdio", CPCAP_VSDIO),
+       [CPCAP_VPLL]     = CPCAP_REGULATOR("vpll", CPCAP_VPLL),
+       [CPCAP_VRF1]     = CPCAP_REGULATOR("vrf1", CPCAP_VRF1),
+       [CPCAP_VRF2]     = CPCAP_REGULATOR("vrf2", CPCAP_VRF2),
+       [CPCAP_VRFREF]   = CPCAP_REGULATOR("vrfref", CPCAP_VRFREF),
+       [CPCAP_VWLAN1]   = CPCAP_REGULATOR("vwlan1", CPCAP_VWLAN1),
+       [CPCAP_VWLAN2]   = CPCAP_REGULATOR("vwlan2", CPCAP_VWLAN2),
+       [CPCAP_VSIM]     = CPCAP_REGULATOR("vsim", CPCAP_VSIM),
+       [CPCAP_VSIMCARD] = CPCAP_REGULATOR("vsimcard", CPCAP_VSIMCARD),
+       [CPCAP_VVIB]     = CPCAP_REGULATOR("vvib", CPCAP_VVIB),
+       [CPCAP_VUSB]     = CPCAP_REGULATOR("vusb", CPCAP_VUSB),
+       [CPCAP_VAUDIO]   = CPCAP_REGULATOR("vaudio", CPCAP_VAUDIO),
+};
+
+static int __devinit cpcap_regulator_probe(struct platform_device *pdev)
+{
+       struct regulator_dev *rdev;
+       struct cpcap_device *cpcap;
+       struct cpcap_platform_data *data;
+       struct regulator_init_data *init;
+       int i;
+
+       /* Already set by core driver */
+       cpcap = platform_get_drvdata(pdev);
+       data = cpcap->spi->controller_data;
+       init = pdev->dev.platform_data;
+
+       for (i = 0; i < CPCAP_NUM_REGULATORS; i++) {
+               cpcap_regltr_data[i].mode_val = data->regulator_mode_values[i];
+               cpcap_regltr_data[i].off_mode_val =
+                       data->regulator_off_mode_values[i];
+       }
+
+       rdev = regulator_register(&regulators[pdev->id], &pdev->dev,
+                                 init, cpcap);
+       if (IS_ERR(rdev))
+               return PTR_ERR(rdev);
+       /* this is ok since the cpcap is still reachable from the rdev */
+       platform_set_drvdata(pdev, rdev);
+
+       if (pdev->id == CPCAP_SW5) {
+               init = cpcap->regulator_pdev[CPCAP_VUSB]->dev.platform_data;
+               init->supply_regulator_dev = rdev_get_dev(rdev);
+               platform_device_add(cpcap->regulator_pdev[CPCAP_VUSB]);
+       }
+
+       return 0;
+}
+
+static int __devexit cpcap_regulator_remove(struct platform_device *pdev)
+{
+       struct regulator_dev *rdev = platform_get_drvdata(pdev);
+
+       regulator_unregister(rdev);
+
+       return 0;
+}
+
+static struct platform_driver cpcap_regulator_driver = {
+       .driver = {
+               .name = "cpcap-regltr",
+       },
+       .probe = cpcap_regulator_probe,
+       .remove = __devexit_p(cpcap_regulator_remove),
+};
+
+static int __init cpcap_regulator_init(void)
+{
+       return platform_driver_register(&cpcap_regulator_driver);
+}
+subsys_initcall(cpcap_regulator_init);
+
+static void __exit cpcap_regulator_exit(void)
+{
+       platform_driver_unregister(&cpcap_regulator_driver);
+}
+module_exit(cpcap_regulator_exit);
+
+MODULE_ALIAS("platform:cpcap-regulator");
+MODULE_DESCRIPTION("CPCAP regulator driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
index a86401ea8e718197d68615cfb4a59ffe5cb18e87..9c9228fbab6113306f0679bea47f6e76b4ef8b5e 100644 (file)
@@ -126,6 +126,13 @@ config RTC_INTF_ALARM_DEV
        help
          Exports the alarm interface to user-space.
 
        help
          Exports the alarm interface to user-space.
 
+config RTC_INTF_CPCAP_SECCLKD
+       bool "Secure Clock Daemon support"
+       depends on RTC_DRV_CPCAP
+       default n
+       help
+         CPCAP RTC driver support for secure clock daemon by maintaining a
+         counter to keep track of changes in RTC time.
 
 config RTC_DRV_TEST
        tristate "Test driver/device"
 
 config RTC_DRV_TEST
        tristate "Test driver/device"
@@ -679,6 +686,13 @@ config RTC_DRV_NUC900
          If you say yes here you get support for the RTC subsystem of the
          NUC910/NUC920 used in embedded systems.
 
          If you say yes here you get support for the RTC subsystem of the
          NUC910/NUC920 used in embedded systems.
 
+config RTC_DRV_CPCAP
+       depends on MFD_CPCAP
+       tristate "CPCAP RTC"
+       help
+         If you say yes here you get support for the RTC subsystem of the
+         CPCAP used in embedded systems.
+
 comment "on-CPU RTC drivers"
 
 config RTC_DRV_DAVINCI
 comment "on-CPU RTC drivers"
 
 config RTC_DRV_DAVINCI
index 5520733748d2f3d6983e586a44438692c9c42dd5..1849ff0b5963ab0ed9ec7bba6c56250de58bdab3 100644 (file)
@@ -101,3 +101,4 @@ obj-$(CONFIG_RTC_DRV_VR41XX)        += rtc-vr41xx.o
 obj-$(CONFIG_RTC_DRV_WM831X)   += rtc-wm831x.o
 obj-$(CONFIG_RTC_DRV_WM8350)   += rtc-wm8350.o
 obj-$(CONFIG_RTC_DRV_X1205)    += rtc-x1205.o
 obj-$(CONFIG_RTC_DRV_WM831X)   += rtc-wm831x.o
 obj-$(CONFIG_RTC_DRV_WM8350)   += rtc-wm8350.o
 obj-$(CONFIG_RTC_DRV_X1205)    += rtc-x1205.o
+obj-$(CONFIG_RTC_DRV_CPCAP)    += rtc-cpcap.o
diff --git a/drivers/rtc/rtc-cpcap.c b/drivers/rtc/rtc-cpcap.c
new file mode 100644 (file)
index 0000000..7e9c705
--- /dev/null
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2009 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/spi/cpcap.h>
+#ifdef RTC_INTF_CPCAP_SECCLKD
+#include <linux/miscdevice.h>
+
+#define CNT_MASK  0xFFFF
+#endif
+#define SECS_PER_DAY 86400
+#define DAY_MASK  0x7FFF
+#define TOD1_MASK 0x00FF
+#define TOD2_MASK 0x01FF
+
+#ifdef RTC_INTF_CPCAP_SECCLKD
+static int cpcap_rtc_open(struct inode *inode, struct file *file);
+static int cpcap_rtc_ioctl(struct inode *inode, struct file *file,
+                           unsigned int cmd, unsigned long arg);
+static unsigned int cpcap_rtc_poll(struct file *file, poll_table *wait);
+static int cpcap_rtc_read_time(struct device *dev, struct rtc_time *tm);
+#endif
+
+struct cpcap_time {
+       unsigned short day;
+       unsigned short tod1;
+       unsigned short tod2;
+};
+
+struct cpcap_rtc {
+       struct cpcap_device *cpcap;
+       struct rtc_device *rtc_dev;
+       int alarm_enabled;
+       int second_enabled;
+#ifdef RTC_INTF_CPCAP_SECCLKD
+       struct device *dev;
+       struct mutex lock;      /* protect access to flags */
+       wait_queue_head_t wait;
+       bool data_pending;
+       bool reset_flag;
+#endif
+};
+
+#ifdef RTC_INTF_CPCAP_SECCLKD
+static const struct file_operations cpcap_rtc_fops = {
+       .owner = THIS_MODULE,
+       .open = cpcap_rtc_open,
+       .ioctl = cpcap_rtc_ioctl,
+       .poll = cpcap_rtc_poll,
+};
+
+static struct cpcap_rtc *rtc_ptr;
+
+static struct miscdevice cpcap_rtc_dev = {
+       .minor  = MISC_DYNAMIC_MINOR,
+       .name   = "cpcap_mot_rtc",
+       .fops   = &cpcap_rtc_fops,
+};
+
+static int cpcap_rtc_open(struct inode *inode, struct file *file)
+{
+       file->private_data = rtc_ptr;
+       return 0;
+}
+
+static int cpcap_rtc_ioctl(struct inode *inode,
+                           struct file *file,
+                           unsigned int cmd,
+                           unsigned long arg)
+{
+       struct cpcap_rtc *rtc = file->private_data;
+       struct cpcap_rtc_time_cnt local_val;
+       int ret = 0;
+
+       mutex_lock(&rtc->lock);
+       switch (cmd) {
+       case CPCAP_IOCTL_GET_RTC_TIME_COUNTER:
+               ret = cpcap_rtc_read_time(rtc->dev, &local_val.time);
+               ret |= cpcap_regacc_read(rtc->cpcap, CPCAP_REG_VAL2,
+                               &local_val.count);
+
+               if (ret)
+                       break;
+
+               if (rtc->reset_flag) {
+                       rtc->reset_flag = 0;
+                       local_val.count = 0;
+               }
+
+               /* Copy the result back to the user. */
+               if (copy_to_user((struct cpcap_rtc_time_cnt *)arg, &local_val,
+                       sizeof(struct cpcap_rtc_time_cnt)) == 0) {
+                       if (local_val.count == 0) {
+                               ret = cpcap_regacc_write(rtc->cpcap,
+                                               CPCAP_REG_VAL2, 0x0001,
+                                               CNT_MASK);
+                               if (ret)
+                                       break;
+                       }
+                       rtc->data_pending = 0;
+               } else
+                       ret = -EFAULT;
+               break;
+
+       default:
+               ret = -ENOTTY;
+
+       }
+
+       mutex_unlock(&rtc->lock);
+       return ret;
+}
+
+static unsigned int cpcap_rtc_poll(struct file *file, poll_table *wait)
+{
+       struct cpcap_rtc *rtc = file->private_data;
+       unsigned int ret = 0;
+
+       poll_wait(file, &rtc->wait, wait);
+
+       if (rtc->data_pending)
+               ret = (POLLIN | POLLRDNORM);
+
+       return ret;
+}
+#endif
+
+static void cpcap2rtc_time(struct rtc_time *rtc, struct cpcap_time *cpcap)
+{
+       unsigned long int tod;
+       unsigned long int time;
+
+       tod = (cpcap->tod1 & TOD1_MASK) | ((cpcap->tod2 & TOD2_MASK) << 8);
+       time = tod + ((cpcap->day & DAY_MASK) * SECS_PER_DAY);
+
+       rtc_time_to_tm(time, rtc);
+}
+
+static void rtc2cpcap_time(struct cpcap_time *cpcap, struct rtc_time *rtc)
+{
+       unsigned long time;
+
+       rtc_tm_to_time(rtc, &time);
+
+       cpcap->day = time / SECS_PER_DAY;
+       time %= SECS_PER_DAY;
+       cpcap->tod2 = (time >> 8) & TOD2_MASK;
+       cpcap->tod1 = time & TOD1_MASK;
+}
+
+static int
+cpcap_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+       struct cpcap_rtc *rtc = dev_get_drvdata(dev);
+       int err;
+
+       if (enabled)
+               err = cpcap_irq_unmask(rtc->cpcap, CPCAP_IRQ_TODA);
+       else
+               err = cpcap_irq_mask(rtc->cpcap, CPCAP_IRQ_TODA);
+
+       if (err < 0)
+               return err;
+
+       rtc->alarm_enabled = enabled;
+
+       return 0;
+}
+
+static int
+cpcap_rtc_update_irq_enable(struct device *dev, unsigned int enabled)
+{
+       struct cpcap_rtc *rtc = dev_get_drvdata(dev);
+       int err;
+
+       if (enabled)
+               err = cpcap_irq_unmask(rtc->cpcap, CPCAP_IRQ_1HZ);
+       else
+               err = cpcap_irq_mask(rtc->cpcap, CPCAP_IRQ_1HZ);
+
+       if (err < 0)
+               return err;
+
+       rtc->second_enabled = enabled;
+
+       return 0;
+}
+
+static int cpcap_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+       struct cpcap_rtc *rtc;
+       struct cpcap_time cpcap_tm;
+       unsigned short temp_tod2;
+       int ret;
+
+       rtc = dev_get_drvdata(dev);
+
+       ret = cpcap_regacc_read(rtc->cpcap, CPCAP_REG_TOD2, &temp_tod2);
+       ret |= cpcap_regacc_read(rtc->cpcap, CPCAP_REG_DAY, &cpcap_tm.day);
+       ret |= cpcap_regacc_read(rtc->cpcap, CPCAP_REG_TOD1, &cpcap_tm.tod1);
+       ret |= cpcap_regacc_read(rtc->cpcap, CPCAP_REG_TOD2, &cpcap_tm.tod2);
+       if (temp_tod2 > cpcap_tm.tod2)
+               ret |= cpcap_regacc_read(rtc->cpcap, CPCAP_REG_DAY,
+                                        &cpcap_tm.day);
+
+       if (ret) {
+               dev_err(dev, "Failed to read time\n");
+               return -EIO;
+       }
+
+       cpcap2rtc_time(tm, &cpcap_tm);
+
+       dev_dbg(dev, "RTC_TIME: %u.%u.%u %u:%u:%u\n",
+               tm->tm_mday, tm->tm_mon, tm->tm_year,
+               tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+       return rtc_valid_tm(tm);
+}
+
+static int cpcap_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+       struct cpcap_rtc *rtc;
+       struct cpcap_time cpcap_tm;
+       int second_masked;
+       int alarm_masked;
+       int ret = 0;
+#ifdef RTC_INTF_CPCAP_SECCLKD
+       unsigned short local_cnt;
+#endif
+
+       rtc = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "RTC_TIME: %u.%u.%u %u:%u:%u\n",
+               tm->tm_mday, tm->tm_mon, tm->tm_year,
+               tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+       rtc2cpcap_time(&cpcap_tm, tm);
+
+       second_masked = cpcap_irq_mask_get(rtc->cpcap, CPCAP_IRQ_1HZ);
+       alarm_masked = cpcap_irq_mask_get(rtc->cpcap, CPCAP_IRQ_TODA);
+
+       if (!second_masked)
+               cpcap_irq_mask(rtc->cpcap, CPCAP_IRQ_1HZ);
+       if (!alarm_masked)
+               cpcap_irq_mask(rtc->cpcap, CPCAP_IRQ_TODA);
+
+#ifdef RTC_INTF_CPCAP_SECCLKD
+       /* Increment the counter and update validity 2 register */
+       ret = cpcap_regacc_read(rtc->cpcap, CPCAP_REG_VAL2, &local_cnt);
+
+       if (local_cnt == 0)
+               rtc->reset_flag = 1;
+
+       if (local_cnt == CNT_MASK)
+               local_cnt = 0x0001;
+       else
+               local_cnt++;
+
+       ret |= cpcap_regacc_write(rtc->cpcap, CPCAP_REG_VAL2, local_cnt,
+                                        CNT_MASK);
+#endif
+
+       if (rtc->cpcap->vendor == CPCAP_VENDOR_ST) {
+               /* The TOD1 and TOD2 registers MUST be written in this order
+                * for the change to properly set. */
+               ret |= cpcap_regacc_write(rtc->cpcap, CPCAP_REG_TOD1,
+                                         cpcap_tm.tod1, TOD1_MASK);
+               ret |= cpcap_regacc_write(rtc->cpcap, CPCAP_REG_TOD2,
+                                         cpcap_tm.tod2, TOD2_MASK);
+               ret |= cpcap_regacc_write(rtc->cpcap, CPCAP_REG_DAY,
+                                         cpcap_tm.day, DAY_MASK);
+       } else {
+               /* Clearing the upper lower 8 bits of the TOD guarantees that
+                * the upper half of TOD (TOD2) will not increment for 0xFF RTC
+                * ticks (255 seconds).  During this time we can safely write
+                * to DAY, TOD2, then TOD1 (in that order) and expect RTC to be
+                * synchronized to the exact time requested upon the final write
+                * to TOD1. */
+               ret |= cpcap_regacc_write(rtc->cpcap, CPCAP_REG_TOD1,
+                                         0, TOD1_MASK);
+               ret |= cpcap_regacc_write(rtc->cpcap, CPCAP_REG_DAY,
+                                         cpcap_tm.day, DAY_MASK);
+               ret |= cpcap_regacc_write(rtc->cpcap, CPCAP_REG_TOD2,
+                                         cpcap_tm.tod2, TOD2_MASK);
+               ret |= cpcap_regacc_write(rtc->cpcap, CPCAP_REG_TOD1,
+                                         cpcap_tm.tod1, TOD1_MASK);
+       }
+
+       if (!second_masked)
+               cpcap_irq_unmask(rtc->cpcap, CPCAP_IRQ_1HZ);
+       if (!alarm_masked)
+               cpcap_irq_unmask(rtc->cpcap, CPCAP_IRQ_TODA);
+
+#ifdef RTC_INTF_CPCAP_SECCLKD
+       mutex_lock(&rtc->lock);
+       rtc->data_pending = 1;
+       mutex_unlock(&rtc->lock);
+       wake_up_interruptible(&rtc->wait);
+#endif
+
+       return ret;
+}
+
+static int cpcap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+       struct cpcap_rtc *rtc;
+       struct cpcap_time cpcap_tm;
+       int ret;
+
+       rtc = dev_get_drvdata(dev);
+
+       alrm->enabled = rtc->alarm_enabled;
+
+       ret = cpcap_regacc_read(rtc->cpcap, CPCAP_REG_DAYA, &cpcap_tm.day);
+       ret |= cpcap_regacc_read(rtc->cpcap, CPCAP_REG_TODA2, &cpcap_tm.tod2);
+       ret |= cpcap_regacc_read(rtc->cpcap, CPCAP_REG_TODA1, &cpcap_tm.tod1);
+
+       if (ret) {
+               dev_err(dev, "Failed to read time\n");
+               return -EIO;
+       }
+
+       cpcap2rtc_time(&alrm->time, &cpcap_tm);
+       return rtc_valid_tm(&alrm->time);
+}
+
+static int cpcap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+       struct cpcap_rtc *rtc;
+       struct cpcap_time cpcap_tm;
+       int ret;
+
+       rtc = dev_get_drvdata(dev);
+
+       rtc2cpcap_time(&cpcap_tm, &alrm->time);
+
+       if (rtc->alarm_enabled)
+               cpcap_irq_mask(rtc->cpcap, CPCAP_IRQ_TODA);
+
+       ret = cpcap_regacc_write(rtc->cpcap, CPCAP_REG_DAYA, cpcap_tm.day,
+                                DAY_MASK);
+       ret |= cpcap_regacc_write(rtc->cpcap, CPCAP_REG_TODA2, cpcap_tm.tod2,
+                                 TOD2_MASK);
+       ret |= cpcap_regacc_write(rtc->cpcap, CPCAP_REG_TODA1, cpcap_tm.tod1,
+                                 TOD1_MASK);
+
+       ret |= cpcap_rtc_alarm_irq_enable(dev, alrm->enabled);
+
+       return ret;
+}
+
+static struct rtc_class_ops cpcap_rtc_ops = {
+       .read_time              = cpcap_rtc_read_time,
+       .set_time               = cpcap_rtc_set_time,
+       .read_alarm             = cpcap_rtc_read_alarm,
+       .set_alarm              = cpcap_rtc_set_alarm,
+       .alarm_irq_enable       = cpcap_rtc_alarm_irq_enable,
+       .update_irq_enable      = cpcap_rtc_update_irq_enable,
+};
+
+static void cpcap_rtc_irq(enum cpcap_irqs irq, void *data)
+{
+       struct cpcap_rtc *rtc = data;
+
+       switch (irq) {
+       case CPCAP_IRQ_TODA:
+               rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
+               break;
+       case CPCAP_IRQ_1HZ:
+               rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF);
+               break;
+       default:
+               break;
+       }
+}
+
+static int __devinit cpcap_rtc_probe(struct platform_device *pdev)
+{
+       struct cpcap_rtc *rtc;
+#ifdef RTC_INTF_CPCAP_SECCLKD
+       int ret = 0;
+#endif
+
+       rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
+       if (!rtc)
+               return -ENOMEM;
+
+       rtc->cpcap = pdev->dev.platform_data;
+       platform_set_drvdata(pdev, rtc);
+       rtc->rtc_dev = rtc_device_register("cpcap_rtc", &pdev->dev,
+                                          &cpcap_rtc_ops, THIS_MODULE);
+
+       if (IS_ERR(rtc->rtc_dev)) {
+               kfree(rtc);
+               return PTR_ERR(rtc->rtc_dev);
+       }
+
+#ifdef RTC_INTF_CPCAP_SECCLKD
+       rtc->dev = &pdev->dev;
+       ret = misc_register(&cpcap_rtc_dev);
+       if (ret != 0) {
+               rtc_device_unregister(rtc->rtc_dev);
+               kfree(rtc);
+               return ret;
+       }
+
+       mutex_init(&rtc->lock);
+       init_waitqueue_head(&rtc->wait);
+       rtc_ptr = rtc;
+#endif
+       cpcap_irq_register(rtc->cpcap, CPCAP_IRQ_TODA, cpcap_rtc_irq, rtc);
+       cpcap_irq_mask(rtc->cpcap, CPCAP_IRQ_TODA);
+
+       cpcap_irq_clear(rtc->cpcap, CPCAP_IRQ_1HZ);
+       cpcap_irq_register(rtc->cpcap, CPCAP_IRQ_1HZ, cpcap_rtc_irq, rtc);
+       cpcap_irq_mask(rtc->cpcap, CPCAP_IRQ_1HZ);
+
+       return 0;
+}
+
+static int __devexit cpcap_rtc_remove(struct platform_device *pdev)
+{
+       struct cpcap_rtc *rtc;
+
+       rtc = platform_get_drvdata(pdev);
+
+       cpcap_irq_free(rtc->cpcap, CPCAP_IRQ_TODA);
+       cpcap_irq_free(rtc->cpcap, CPCAP_IRQ_1HZ);
+
+#ifdef RTC_INTF_CPCAP_SECCLKD
+       misc_deregister(&cpcap_rtc_dev);
+#endif
+       rtc_device_unregister(rtc->rtc_dev);
+       kfree(rtc);
+
+       return 0;
+}
+
+static struct platform_driver cpcap_rtc_driver = {
+       .driver = {
+               .name = "cpcap_rtc",
+       },
+       .probe = cpcap_rtc_probe,
+       .remove = __devexit_p(cpcap_rtc_remove),
+};
+
+static int __init cpcap_rtc_init(void)
+{
+       return platform_driver_register(&cpcap_rtc_driver);
+}
+module_init(cpcap_rtc_init);
+
+static void __exit cpcap_rtc_exit(void)
+{
+       platform_driver_unregister(&cpcap_rtc_driver);
+}
+module_exit(cpcap_rtc_exit);
+
+MODULE_ALIAS("platform:cpcap_rtc");
+MODULE_DESCRIPTION("CPCAP RTC driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
index 9c2d19452d0bf08a4c3642c7596d6056a8456797..1d613fd604313709d9a790af3af7912e281f41d6 100644 (file)
@@ -138,6 +138,7 @@ fw-shipped-$(CONFIG_USB_SERIAL_XIRCOM) += keyspan_pda/xircom_pgs.fw
 fw-shipped-$(CONFIG_USB_VICAM) += vicam/firmware.fw
 fw-shipped-$(CONFIG_VIDEO_CPIA2) += cpia2/stv0672_vp4.bin
 fw-shipped-$(CONFIG_YAM) += yam/1200.bin yam/9600.bin
 fw-shipped-$(CONFIG_USB_VICAM) += vicam/firmware.fw
 fw-shipped-$(CONFIG_VIDEO_CPIA2) += cpia2/stv0672_vp4.bin
 fw-shipped-$(CONFIG_YAM) += yam/1200.bin yam/9600.bin
+fw-shipped-$(CONFIG_MFD_CPCAP) += cpcap/firmware_0_2x.fw cpcap/firmware_1_2x.fw
 
 fw-shipped-all := $(fw-shipped-y) $(fw-shipped-m) $(fw-shipped-)
 
 
 fw-shipped-all := $(fw-shipped-y) $(fw-shipped-m) $(fw-shipped-)
 
diff --git a/firmware/cpcap/firmware_0_2x.HEX b/firmware/cpcap/firmware_0_2x.HEX
new file mode 100644 (file)
index 0000000..70645a9
--- /dev/null
@@ -0,0 +1,67 @@
+:20001800017C0000000000000000000000000000000000000000000000000000000000004B
+:200038000000000000000000000000000000000001EC0000000000000000000000000000BB
+:20005800000000000000000000000000000000000000000000000000000000000000000088
+:20007800000000000000000000000000000000000000000000000000000000000000000068
+:20009800000000000000000000000000000000000000000000000000000000000000000048
+:0800B800000000000000000040
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFF00000000000000000000000000000000000000000000000000000000A3
+:2001600000000000000000000000000000000000000000000000000000000000030F00006D
+:2001800000000000000000000000000000000000000000000000000000000000000000005F
+:2001A00000000000000000000000000000000000000000000000000000000000000000003F
+:2001C00000000000000000000000000000000000000000000000000000000000000000001F
+:2001E000000000000000000000000000055805F304000100000000000000000000000000A5
+:080200000000000000000000F6
+:1002080000000000000000000000000000000200E4
+:200226009EA00C955BBFE2AB0997CD083F5BBFE2AB0597CD083F5BBFE2AB0197CD083FC60E
+:20024600450DCE450C6B0C72EF0B4F6B0A6B095BBFE2AB0997A610CD07F33FEB3FEA5BBFDB
+:20026600E2AB0997CD0748C6450BCE450ACD07675BBFE2AB0997CD07CA5BBFE2AB0997CDC8
+:20028600083F350200E2AE08CD081C5BBFE2AB0597CD07487B052A095BBFE2AB0597CD0728
+:2002A6009D350100E2AE28A610CD07F33FEB3FEA5BBFE2AB0197CD0748350100E2AE28CDC7
+:2002C600083FA610CD07393FE93FE85BBFE2AB0197CD07CA5BBFE2AB0597CD083F5BBFE28F
+:2002E600AB0197CD077025187B0CC7020B7B0BC7020A7B0AC702097B09C70208A60120010C
+:200306004F6B0C9EAB0B958481A640C7831B725F831AC78327725F832648C78329725F8370
+:2003260028A620C7832B725F832AC7832D725F832C4FC70218C70219C7021AC7021BC7023D
+:200346001CC7021DC7021EC7021FC70220C70221C70222C70223C70224C70225356901237F
+:20036600725F012235590125725F012435510127725F01263520012B35A1012A350701294B
+:2003860035000128720A410303CD04A3720C410322725C021B2604725C021AC6021BC0023A
+:2003A60011C6021AC20210250ACD04E94FC7021AC7021B720E410320725C021D2604725CA8
+:2003C600021CC6021DCA021C2715C64101CE4100AA80C74101CF41004FC7021CC7021D720A
+:2003E60000410224725C021F2604725C021EC6021FA00AC6021EA2002515CE4101C641001F
+:20040600AA01CF4101C741004FC7021EC7021F7202410224725C02212604725C0220C60246
+:2004260021A014C60220A2002515CE4101C64100AA02CF4101C741004FC70220C7022172AD
+:2004460004410224725C02232604725C0222C60223A03CC60222A2002515CE4101C6410078
+:20046600AA04CF4101C741004FC70222C702237206410217C6020F2712725F020FCE4101B5
+:20048600C64100AA08CF4101C74100720A410203CD0702A6CCAE0CCDF009CC038A88888809
+:2004A60088C640106B01C640116B02C640126B03C640136B047B01A50427067B03A50826F7
+:2004C60009350102128484848481C6021227F6725F0212C64101CE4100AA20C74101CF415C
+:2004E6000020E2C602132632C64807CE4806AA01C74807CF4806C64809CE4808AA01C748C3
+:2005060009CF4808C6480BCE480AAA01C7480BCF480A35010213A6032030C64807CE480619
+:20052600A4FEC74807CF4806C64809CE4808A4FEC74809CF4808C6480BCE480AA4FEC748E8
+:200546000BCF480A725F0213A612C70211725F021081A6CCAE0CCDF009725C02152604721A
+:200566005C0214C60215A058C60214A202253DCE4601C64600AA90CF4601C74600CE4009B7
+:20058600C64008A4FECF4009C74008C64603CE4602AA7341AAD1CF4603C74602A6CCAE0C2D
+:2005A600CDF0004FC70214C70215201CC60215A1572615CE0214A302260ECE4603C6460240
+:2005C600AA40CF4603C74602CD02264D27847207410203CC05583501020FCC0558000000BF
+:2005E6000000000000000000000000000088899EA010955BBFE2AB0197350500E590AEE382
+:20060600A610CD085ECE4009C64008AA01CF4009C74008CE4001C64000AA01CF4001C7401D
+:2006260000CE4603C64602A4BFCF4603C74602CE4605C64604A40372EF026B01CE4607C685
+:200646004606A40372EF046B03CE4609C64608A40372EF066B05CE460BC6460AA40372EFE2
+:20066600086B07CE460DC6460CA40372EF0A6B09CE460FC6460EA40372EF0C6B0BCE46114F
+:20068600C64610A40372EF0E6B0DCE4613C64612A40372EF106B0F7B02C7020E7B01C7023A
+:2006A6000DC60127C0020EC60126C2020D2504A6022014C60125C0020EC60124C2020D2509
+:2006C6000BC6020C26064CC7020C201BC60123C0020EC60122C2020D240DC6020C27087293
+:2006E6005F020C3501020FC6020C27043501020F7B12AE01CDF2B69EAB1295818888C640C2
+:2007060011C64010A402974F6B0272EF01C1021726069FC1021627187B02C702177B01C7F4
+:200726000216CE4101C64100AA20CF4101C741008484814D270B34E836E936EA36EB4A26AD
+:20074600F581BFE388B6E892C7E2AE01B6E992D7E25CB6EA92D7E25CB6EB92D7E284BEE3CD
+:2007660081B7EBBFEA3FE93FE8813FE3B6E892D1E226233CE3B6E992D1E226123CE3B6EA8F
+:2007860092D1E226093CE3B6EB92D1E227082404A6FF2002A60181BFE3723300E2AE01724A
+:2007A6006300E25C726300E25C726000E226125A726C00E2260B5A726C00E22604723C0056
+:2007C600E2BEE381CD07D0CC074ABFE3B6E892CAE2B7E8B6E9AE0192DAE2B7E9B6EA5C9267
+:2007E600DAE2B7EAB6EB5C92DAE2B7EB81CD083F2003CD080A4D270B38EB39EA39E939E80A
+:200806004A26F58188F6B7E8E601B7E9E602B7EAE603B7EB8481BFE3AE03B6EB92D0E2B740
+:20082600EBB6EA5A92D2E2B7EAB6E95A92D2E2B7E9B6E892C2E2B7E881BFE38892C6E2B7F8
+:20084600E8AE0192D6E2B7E95C92D6E2B7EA5C92D6E2B7EB84BEE381BFE390BFE6975A9282
+:0B086600D6E592D7E25D26F6BEE381E6
+:00000001FF
\ No newline at end of file
diff --git a/firmware/cpcap/firmware_1_2x.H16 b/firmware/cpcap/firmware_1_2x.H16
new file mode 100644 (file)
index 0000000..c6c2760
--- /dev/null
@@ -0,0 +1,3 @@
+:03E0913000917A918C2400000401000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012005030001E410F82A1000052A1000041301200503000B0410F82A1000052A10000413040B20040531A40B20040532640B20080532840B20020532A40B20020532C43829160438291624382916443829166438291684382916A4382916C40B2006990E640B2005990E840B2005190EA40B2A12090EC40B2000790EE43829170438291724382917443C2917843C291794382916E43C2917643D29177D0B201000110C0B201000108C0B2040000C0C0B2040000C640B20C80018440B200100180435C52A100004130120A435AB0B201009FA428041200503001623C0412005030000C4C4A4A4C413A52A100004130B0B2002011022C13B0B204000100280DB0B208000102280993C29176240843C29176D0B2002001C83C0243D29176B0B2004011022C22539291629292916E9162281C93C29177200AD3921806D3921808D392180A403F0003435E3C09C3921806C3921808C392180A403F0012434E4EC291774F82916E43829162B0B2008011022C06539291642405D0B2008001C843829164B0B2010011022C095392916690B2000A91662805D0B2010001C843829166B0B2020011022C095392916890B2001491682805D0B2020001C843829168B0B2040011022C095392916A90B2003C916A2805D0B2040001C84382916AB0B2100011022C295392916C90B20258916C280940B29000030040B2D17303024382916C3C0790B20257916C2003D0B240000302120050300102934C240243D29179B0B2080011022C0693C291792405D0B2080001C843C29179435C52A10000413080310020410C403E9140403D001012005030010CC0B240000302403E03FF4E0CF21C03044C810000438100024E0CF21C03064C810004438100064E0CF21C03084C8100084381000A4E0CF21C030A4C81000C4381000E4E0CF21C030C4C810010438100124E0CF21C030E4C810014438100164E0CF21C03104C8100184381001AF21E03124E81001C4381001E41A29174421F91749F8290EA280343E291783C16425E91789F8290E82805934E200343D291783C0C9F8290E62C07934E240543C2917843D291793C04934E240243D291795031002052A100004130421F150C421E150A4E0C4F0D821C9170721D9172930D3404E33CE33D531C630D921D90EE280C2003921C90EC28084E8291704F829172435C52A100004130434C52A100004130120C4EBC0000532C831D23FB413C52A1000041304C0F5D0F3C0343CC0000531C9F0C23FB52A100004130403C9160403D001A12005030FFDC00008A
+:0002901800913095
+:0000000001FF
diff --git a/include/linux/spi/cpcap-regbits.h b/include/linux/spi/cpcap-regbits.h
new file mode 100644 (file)
index 0000000..b5bda42
--- /dev/null
@@ -0,0 +1,952 @@
+#ifndef __CPCAP_REGBITS_H__
+#define __CPCAP_REGBITS_H__
+
+/*
+ * Copyright (C) 2007-2009 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+/*
+ * Register 0 - CPCAP_REG_INT_0 bits
+ */
+#define CPCAP_BIT_ID_GROUND_I          0x00008000
+#define CPCAP_BIT_ID_FLOAT_I           0x00004000
+#define CPCAP_BIT_CHRG_DET_I           0x00002000
+#define CPCAP_BIT_RVRS_CHRG_I          0x00001000
+#define CPCAP_BIT_VBUSOV_I             0x00000800
+#define CPCAP_BIT_MB2_I                        0x00000400
+#define CPCAP_BIT_HS_I                 0x00000200
+#define CPCAP_BIT_ADCDONE_I            0x00000100
+#define CPCAP_BIT_TS_I                 0x00000080
+#define CPCAP_BIT_EOL_I                        0x00000040
+#define CPCAP_BIT_LOWBPH_I             0x00000020
+#define CPCAP_BIT_SEC2PRI_I            0x00000010
+#define CPCAP_BIT_LOWBPL_I             0x00000008
+#define CPCAP_BIT_UNUSED_0_2_I         0x00000004
+#define CPCAP_BIT_PRIMAC_I             0x00000002
+#define CPCAP_BIT_HSCLK_I              0x00000001
+
+/*
+ * Register 1 - CPCAP_REG_INT_1 bits
+ */
+#define CPCAP_BIT_EXTMEMHD_I           0x00008000
+#define CPCAP_BIT_UART_ECHO_OVERRUN_I  0x00004000
+#define CPCAP_BIT_CHRG_SE1B_I          0x00002000
+#define CPCAP_BIT_SE0CONN_I            0x00001000
+#define CPCAP_BIT_PTT_I                        0x00000800
+#define CPCAP_BIT_1HZ_I                        0x00000400
+#define CPCAP_BIT_CLK_I                        0x00000200
+#define CPCAP_BIT_ON2_I                        0x00000100
+#define CPCAP_BIT_ON_I                 0x00000080
+#define CPCAP_BIT_RVRS_MODE_I          0x00000040
+#define CPCAP_BIT_CHRGCURR2_I          0x00000020
+#define CPCAP_BIT_CHRGCURR1_I          0x00000010
+#define CPCAP_BIT_VBUSVLD_I            0x00000008
+#define CPCAP_BIT_SESSVLD_I            0x00000004
+#define CPCAP_BIT_SESSEND_I            0x00000002
+#define CPCAP_BIT_SE1_I                        0x00000001
+
+/*
+ * Register 2  CPCAP_REG_INT_2 -  bits
+ */
+#define CPCAP_BIT_USBDPLLCLK_I         0x00008000
+#define CPCAP_BIT_PWRGOOD_I            0x00004000
+#define CPCAP_BIT_UCRESET_I            0x00002000
+#define CPCAP_BIT_ONEWIRE3_I           0x00001000
+#define CPCAP_BIT_ONEWIRE2_I           0x00000800
+#define CPCAP_BIT_ONEWIRE1_I           0x00000400
+#define CPCAP_BIT_OPT_SEL_STATE_I      0x00000200
+#define CPCAP_BIT_OPT_SEL_DTCH_I       0x00000100
+#define CPCAP_BIT_TODA_I               0x00000080
+#define CPCAP_BIT_OFLOWSW_I            0x00000040
+#define CPCAP_BIT_PC_I                 0x00000020
+#define CPCAP_BIT_DIETEMPH_I           0x00000010
+#define CPCAP_BIT_DIEPWRDWN_I          0x00000008
+#define CPCAP_BIT_SOFTRST_I            0x00000004
+#define CPCAP_BIT_SYSRSTRT_I           0x00000002
+#define CPCAP_BIT_WARM_I               0x00000001
+
+/*
+ * Register 3 - CPCAP_REG_INT_3  bits
+ */
+#define CPCAP_BIT_UNUSED_3_15_I                0x00008000
+#define CPCAP_BIT_UNUSED_3_14_I                0x00004000
+#define CPCAP_BIT_SPARE_3_13_I         0x00002000
+#define CPCAP_BIT_SPARE_3_12_I         0x00001000
+#define CPCAP_BIT_SPARE_3_11_I         0x00000800
+#define CPCAP_BIT_SPARE_3_10_I         0x00000400
+#define CPCAP_BIT_CC_CAL_I             0x00000200
+#define CPCAP_BIT_SECHALT_I            0x00000100
+#define CPCAP_BIT_PRIHALT_I            0x00000080
+#define CPCAP_BIT_BATTDETB_I           0x00000040
+#define CPCAP_BIT_SB_MAX_RETX_ERR_I    0x00000020
+#define CPCAP_BIT_GCAI_CURR2_I         0x00000010
+#define CPCAP_BIT_GCAI_CURR1_I         0x00000008
+#define CPCAP_BIT_UCBUSY_I             0x00000004
+#define CPCAP_BIT_DM_I                 0x00000002
+#define CPCAP_BIT_DP_I                 0x00000001
+
+/*
+ * Register 4 - CPCAP_REG_INTM1 bits
+ */
+#define CPCAP_BIT_ID_GROUND_M          0x00008000
+#define CPCAP_BIT_ID_FLOAT_M           0x00004000
+#define CPCAP_BIT_CHRG_DET_M           0x00002000
+#define CPCAP_BIT_RVRS_CHRG_M          0x00001000
+#define CPCAP_BIT_VBUSOV_M             0x00000800
+#define CPCAP_BIT_MB2_M                        0x00000400
+#define CPCAP_BIT_HS_M                 0x00000200
+#define CPCAP_BIT_ADCDONE_M            0x00000100
+#define CPCAP_BIT_TS_M                 0x00000080
+#define CPCAP_BIT_EOL_M                        0x00000040
+#define CPCAP_BIT_LOWBPH_M             0x00000020
+#define CPCAP_BIT_SEC2PRI_M            0x00000010
+#define CPCAP_BIT_LOWBPL_M             0x00000008
+#define CPCAP_BIT_UNUSED_4_2_M         0x00000004
+#define CPCAP_BIT_PRIMAC_M             0x00000002
+#define CPCAP_BIT_HSCLK_M              0x00000001
+
+/*
+ * Register 5 - CPCAP_REG_INTM2 bits
+ */
+#define CPCAP_BIT_EXTMEMHD_M           0x00008000
+#define CPCAP_BIT_UART_ECHO_OVERRUN_M  0x00004000
+#define CPCAP_BIT_CHRG_SE1B_M          0x00002000
+#define CPCAP_BIT_SE0CONN_M            0x00001000
+#define CPCAP_BIT_PTT_M                        0x00000800
+#define CPCAP_BIT_1HZ_M                        0x00000400
+#define CPCAP_BIT_CLK_M                        0x00000200
+#define CPCAP_BIT_ON2_M                        0x00000100
+#define CPCAP_BIT_ON_M                 0x00000080
+#define CPCAP_BIT_RVRS_MODE_M          0x00000040
+#define CPCAP_BIT_CHRGCURR2_M          0x00000020
+#define CPCAP_BIT_CHRGCURR1_M          0x00000010
+#define CPCAP_BIT_VBUSVLD_M            0x00000008
+#define CPCAP_BIT_SESSVLD_M            0x00000004
+#define CPCAP_BIT_SESSEND_M            0x00000002
+#define CPCAP_BIT_SE1_M                        0x00000001
+
+/*
+ * Register 6 - CPCAP_REG_INTM3 bits
+ */
+#define CPCAP_BIT_USBDPLLCLK_M         0x00008000
+#define CPCAP_BIT_PWRGOOD_M            0x00004000
+#define CPCAP_BIT_UCRESET_M            0x00002000
+#define CPCAP_BIT_ONEWIRE3_M           0x00001000
+#define CPCAP_BIT_ONEWIRE2_M           0x00000800
+#define CPCAP_BIT_ONEWIRE1_M           0x00000400
+#define CPCAP_BIT_OPT_SEL_STATE_M      0x00000200
+#define CPCAP_BIT_OPT_SEL_DTCH_M       0x00000100
+#define CPCAP_BIT_TODA_M               0x00000080
+#define CPCAP_BIT_OFLOWSW_M            0x00000040
+#define CPCAP_BIT_PC_M                 0x00000020
+#define CPCAP_BIT_DIETEMPH_M           0x00000010
+#define CPCAP_BIT_DIEPWRDWN_M          0x00000008
+#define CPCAP_BIT_SOFTRST_M            0x00000004
+#define CPCAP_BIT_SYSRSTRT_M           0x00000002
+#define CPCAP_BIT_WARM_M               0x00000001
+
+/*
+ * Register 7 - CPCAP_REG_INTM4 bits
+ */
+#define CPCAP_BIT_UNUSED_7_15_M                0x00008000
+#define CPCAP_BIT_UNUSED_7_14_M                0x00004000
+#define CPCAP_BIT_SPARE_7_13_M         0x00002000
+#define CPCAP_BIT_SPARE_7_12_M         0x00001000
+#define CPCAP_BIT_SPARE_7_11_M         0x00000800
+#define CPCAP_BIT_SPARE_7_10_M         0x00000400
+#define CPCAP_BIT_CC_CAL_M             0x00000200
+#define CPCAP_BIT_SECHALT_M            0x00000100
+#define CPCAP_BIT_PRIHALT_M            0x00000080
+#define CPCAP_BIT_BATTDETB_M           0x00000040
+#define CPCAP_BIT_SB_MAX_RETX_ERR_M    0x00000020
+#define CPCAP_BIT_GCAI_CURR2_M         0x00000010
+#define CPCAP_BIT_GCAI_CURR1_M         0x00000008
+#define CPCAP_BIT_UCBUSY_M             0x00000004
+#define CPCAP_BIT_DM_M                 0x00000002
+#define CPCAP_BIT_DP_M                 0x00000001
+
+/*
+ * Register 8 - CPCAP_REG_INTS1 bits
+ */
+#define CPCAP_BIT_ID_GROUND_S          0x00008000
+#define CPCAP_BIT_ID_FLOAT_S           0x00004000
+#define CPCAP_BIT_CHRG_DET_S           0x00002000
+#define CPCAP_BIT_RVRS_CHRG_S          0x00001000
+#define CPCAP_BIT_VBUSOV_S             0x00000800
+#define CPCAP_BIT_MB2_S                        0x00000400
+#define CPCAP_BIT_HS_S                 0x00000200
+#define CPCAP_BIT_ADCDONE_S            0x00000100
+#define CPCAP_BIT_TS_S                 0x00000080
+#define CPCAP_BIT_EOL_S                        0x00000040
+#define CPCAP_BIT_LOWBPH_S             0x00000020
+#define CPCAP_BIT_SEC2PRI_S            0x00000010
+#define CPCAP_BIT_LOWBPL_S             0x00000008
+#define CPCAP_BIT_UNUSED_8_2_S         0x00000004
+#define CPCAP_BIT_PRIMAC_S             0x00000002
+#define CPCAP_BIT_HSCLK_S              0x00000001
+
+/*
+ * Register 9 - CPCAP_REG_INTS2 bits
+ */
+#define CPCAP_BIT_EXTMEMHD_S           0x00008000
+#define CPCAP_BIT_UART_ECHO_OVERRUN_S  0x00004000
+#define CPCAP_BIT_CHRG_SE1B_S          0x00002000
+#define CPCAP_BIT_SE0CONN_S            0x00001000
+#define CPCAP_BIT_PTT_S                        0x00000800
+#define CPCAP_BIT_1HZ_S                        0x00000400
+#define CPCAP_BIT_CLK_S                        0x00000200
+#define CPCAP_BIT_ON2_S                        0x00000100
+#define CPCAP_BIT_ON_S                 0x00000080
+#define CPCAP_BIT_RVRS_MODE_S          0x00000040
+#define CPCAP_BIT_CHRGCURR2_S          0x00000020
+#define CPCAP_BIT_CHRGCURR1_S          0x00000010
+#define CPCAP_BIT_VBUSVLD_S            0x00000008
+#define CPCAP_BIT_SESSVLD_S            0x00000004
+#define CPCAP_BIT_SESSEND_S            0x00000002
+#define CPCAP_BIT_SE1_S                        0x00000001
+
+/*
+ * Register 10 - CPCAP_REG_INTS3 bits
+ */
+#define CPCAP_BIT_USBDPLLCLK_S         0x00008000
+#define CPCAP_BIT_PWRGOOD_S            0x00004000
+#define CPCAP_BIT_UCRESET_S            0x00002000
+#define CPCAP_BIT_ONEWIRE3_S           0x00001000
+#define CPCAP_BIT_ONEWIRE2_S           0x00000800
+#define CPCAP_BIT_ONEWIRE1_S           0x00000400
+#define CPCAP_BIT_OPT_SEL_STATE_S      0x00000200
+#define CPCAP_BIT_OPT_SEL_DTCH_S       0x00000100
+#define CPCAP_BIT_TODA_S               0x00000080
+#define CPCAP_BIT_OFLOWSW_S            0x00000040
+#define CPCAP_BIT_PC_S                 0x00000020
+#define CPCAP_BIT_DIETEMPH_S           0x00000010
+#define CPCAP_BIT_DIEPWRDWN_S          0x00000008
+#define CPCAP_BIT_SOFTRST_S            0x00000004
+#define CPCAP_BIT_SYSRSTRT_S           0x00000002
+#define CPCAP_BIT_WARM_S               0x00000001
+
+/*
+ * Register 11 - CPCAP_REG_INTS4 bits
+ */
+#define CPCAP_BIT_UNUSED_11_15_S       0x00008000
+#define CPCAP_BIT_UNUSED_11_14_S       0x00004000
+#define CPCAP_BIT_SPARE_11_13_S                0x00002000
+#define CPCAP_BIT_SPARE_11_12_S                0x00001000
+#define CPCAP_BIT_SPARE_11_11_S                0x00000800
+#define CPCAP_BIT_SPARE_11_10_S                0x00000400
+#define CPCAP_BIT_CC_CAL_S             0x00000200
+#define CPCAP_BIT_SECHALT_S            0x00000100
+#define CPCAP_BIT_PRIHALT_S            0x00000080
+#define CPCAP_BIT_BATTDETB_S           0x00000040
+#define CPCAP_BIT_SB_MAX_RETX_ERR_S    0x00000020
+#define CPCAP_BIT_GCAI_CURR2_S         0x00000010
+#define CPCAP_BIT_GCAI_CURR1_S         0x00000008
+#define CPCAP_BIT_UCBUSY_S             0x00000004
+#define CPCAP_BIT_DM_S                 0x00000002
+#define CPCAP_BIT_DP_S                 0x00000001
+
+/*
+ * Register 128 - CPCAP_REG_MI1 bits
+ */
+#define CPCAP_BIT_PRIMACRO_15_S                0x00008000
+#define CPCAP_BIT_PRIMACRO_14_S                0x00004000
+#define CPCAP_BIT_PRIMACRO_13_S                0x00002000
+#define CPCAP_BIT_PRIMACRO_12_S                0x00001000
+#define CPCAP_BIT_PRIMACRO_11_S                0x00000800
+#define CPCAP_BIT_PRIMACRO_10_S                0x00000400
+#define CPCAP_BIT_PRIMACRO_9_S         0x00000200
+#define CPCAP_BIT_PRIMACRO_8_S         0x00000100
+#define CPCAP_BIT_PRIMACRO_7_S         0x00000080
+#define CPCAP_BIT_PRIMACRO_6_S         0x00000040
+#define CPCAP_BIT_PRIMACRO_5_S         0x00000020
+#define CPCAP_BIT_PRIMACRO_4_S         0x00000010
+#define CPCAP_BIT_USEROFF_S            0x00000008
+#define CPCAP_BIT_PRIRAMR_S            0x00000004
+#define CPCAP_BIT_PRIRAMW_S            0x00000002
+#define CPCAP_BIT_PRIROMR_S            0x00000001
+
+/*
+ * Register 129 - CPCAP_REG_MIM1 bits
+ */
+#define CPCAP_BIT_PRIMACRO_15M         0x00008000
+#define CPCAP_BIT_PRIMACRO_14M         0x00004000
+#define CPCAP_BIT_PRIMACRO_13M         0x00002000
+#define CPCAP_BIT_PRIMACRO_12M         0x00001000
+#define CPCAP_BIT_PRIMACRO_11M         0x00000800
+#define CPCAP_BIT_PRIMACRO_10M         0x00000400
+#define CPCAP_BIT_PRIMACRO_9M          0x00000200
+#define CPCAP_BIT_PRIMACRO_8M          0x00000100
+#define CPCAP_BIT_PRIMACRO_7M          0x00000080
+#define CPCAP_BIT_PRIMACRO_6M          0x00000040
+#define CPCAP_BIT_PRIMACRO_5M          0x00000020
+#define CPCAP_BIT_PRIMACRO_4M          0x00000010
+#define CPCAP_BIT_USEROFFM             0x00000008
+#define CPCAP_BIT_PRIRAMRM             0x00000004
+#define CPCAP_BIT_PRIRAMWM             0x00000002
+#define CPCAP_BIT_PRIROMRM             0x00000001
+
+/*
+ * Register 130 - CPCAP_REG_MI2 bits
+ */
+#define CPCAP_BIT_PRIMACRO_15          0x00008000
+#define CPCAP_BIT_PRIMACRO_14          0x00004000
+#define CPCAP_BIT_PRIMACRO_13          0x00002000
+#define CPCAP_BIT_PRIMACRO_12          0x00001000
+#define CPCAP_BIT_PRIMACRO_11          0x00000800
+#define CPCAP_BIT_PRIMACRO_10          0x00000400
+#define CPCAP_BIT_PRIMACRO_9           0x00000200
+#define CPCAP_BIT_PRIMACRO_8           0x00000100
+#define CPCAP_BIT_PRIMACRO_7           0x00000080
+#define CPCAP_BIT_PRIMACRO_6           0x00000040
+#define CPCAP_BIT_PRIMACRO_5           0x00000020
+#define CPCAP_BIT_PRIMACRO_4           0x00000010
+#define CPCAP_BIT_USEROFF              0x00000008
+#define CPCAP_BIT_PRIRAMR              0x00000004
+#define CPCAP_BIT_PRIRAMW              0x00000002
+#define CPCAP_BIT_PRIROMR              0x00000001
+
+/*
+ * Register 131 - CPCAP_REG_MIM2 bits
+ */
+#define CPCAP_BIT_PRIMACRO_15S         0x00008000
+#define CPCAP_BIT_PRIMACRO_14S         0x00004000
+#define CPCAP_BIT_PRIMACRO_13S         0x00002000
+#define CPCAP_BIT_PRIMACRO_12S         0x00001000
+#define CPCAP_BIT_PRIMACRO_11S         0x00000800
+#define CPCAP_BIT_PRIMACRO_10S         0x00000400
+#define CPCAP_BIT_PRIMACRO_9S          0x00000200
+#define CPCAP_BIT_PRIMACRO_8S          0x00000100
+#define CPCAP_BIT_PRIMACRO_7S          0x00000080
+#define CPCAP_BIT_PRIMACRO_6S          0x00000040
+#define CPCAP_BIT_PRIMACRO_5S          0x00000020
+#define CPCAP_BIT_PRIMACRO_4S          0x00000010
+#define CPCAP_BIT_USEROFFS             0x00000008
+#define CPCAP_BIT_PRIRAMRS             0x00000004
+#define CPCAP_BIT_PRIRAMWS             0x00000002
+#define CPCAP_BIT_PRIROMRS             0x00000001
+
+/*
+ * Register 132 - CPCAP_REG_UCC1 bits
+ */
+#define CPCAP_BIT_UNUSED_132_15                0x00008000
+#define CPCAP_BIT_UNUSED_132_14                0x00004000
+#define CPCAP_BIT_UNUSED_132_13                0x00002000
+#define CPCAP_BIT_UNUSED_132_12                0x00001000
+#define CPCAP_BIT_PRI_GPIO6_2MAC10     0x00000800
+#define CPCAP_BIT_PRI_GPIO5_2MAC9      0x00000400
+#define CPCAP_BIT_PRI_GPIO4_2MAC8      0x00000200
+#define CPCAP_BIT_PRI_GPIO3_2MAC7      0x00000100
+#define CPCAP_BIT_PRI_GPIO2_2MAC6      0x00000080
+#define CPCAP_BIT_PRI_GPIO1_2MAC5      0x00000040
+#define CPCAP_BIT_PRI_GPIO0_2MAC4      0x00000020
+#define CPCAP_BIT_USEROFFCLK           0x00000010
+#define CPCAP_BIT_UO_MH_PFM_EN         0x00000008
+#define CPCAP_BIT_CNTRLSEC             0x00000004
+#define CPCAP_BIT_SCHDOVERRIDE         0x00000002
+#define CPCAP_BIT_PRIHALT              0x00000001
+
+/*
+ * Register 135 - CPCAP_REG_PC1 bits
+ */
+#define CPCAP_BIT_UNUSED_135_15                0x00008000
+#define CPCAP_BIT_UNUSED_135_14                0x00004000
+#define CPCAP_BIT_UNUSED_135_13                0x00002000
+#define CPCAP_BIT_UNUSED_135_12                0x00001000
+#define CPCAP_BIT_UNUSED_135_11                0x00000800
+#define CPCAP_BIT_UNUSED_135_10                0x00000400
+#define CPCAP_BIT_PC1_SC_SHTDWN_EN     0x00000200
+#define CPCAP_BIT_PC1_PCEN             0x00000100
+#define CPCAP_BIT_PC1_PCT7             0x00000080
+#define CPCAP_BIT_PC1_PCT6             0x00000040
+#define CPCAP_BIT_PC1_PCT5             0x00000020
+#define CPCAP_BIT_PC1_PCT4             0x00000010
+#define CPCAP_BIT_PC1_PCT3             0x00000008
+#define CPCAP_BIT_PC1_PCT2             0x00000004
+#define CPCAP_BIT_PC1_PCT1             0x00000002
+#define CPCAP_BIT_PC1_PCT0             0x00000001
+
+/*
+ * Register 138 - CPCAP_REG_PGC bits
+ */
+#define CPCAP_BIT_UNUSED_138_15                0x00008000
+#define CPCAP_BIT_UNUSED_138_14                0x00004000
+#define CPCAP_BIT_UNUSED_138_13                0x00002000
+#define CPCAP_BIT_UNUSED_138_12                0x00001000
+#define CPCAP_BIT_UNUSED_138_11                0x00000800
+#define CPCAP_BIT_UNUSED_138_10                0x00000400
+#define CPCAP_BIT_UNUSED_138_9         0x00000200
+#define CPCAP_BIT_REVENINV             0x00000100
+#define CPCAP_BIT_PRISTBYINV           0x00000080
+#define CPCAP_BIT_SYS_RST_MODE         0x00000040
+#define CPCAP_BIT_MAC_TIME_LONG                0x00000020
+#define CPCAP_BIT_PRI_UC_SUSPEND       0x00000010
+#define CPCAP_BIT_PRIWARMSTART         0x00000008
+#define CPCAP_BIT_PRIPRESVRAM          0x00000004
+#define CPCAP_BIT_SPI_PWRGT1EN         0x00000002
+#define CPCAP_BIT_SPI_PWRGT2EN         0x00000001
+
+/*
+ * Register 259 - CPCAP_REG_UCTM bits */
+#define CPCAP_BIT_UNUSED_259_15     0x00008000
+#define CPCAP_BIT_UNUSED_259_14     0x00004000
+#define CPCAP_BIT_UNUSED_259_13     0x00002000
+#define CPCAP_BIT_UNUSED_259_12     0x00001000
+#define CPCAP_BIT_UNUSED_259_11     0x00000800
+#define CPCAP_BIT_UNUSED_259_10     0x00000400
+#define CPCAP_BIT_UNUSED_259_9      0x00000200
+#define CPCAP_BIT_UNUSED_259_8      0x00000100
+#define CPCAP_BIT_UNUSED_259_7      0x00000080
+#define CPCAP_BIT_UNUSED_259_6      0x00000040
+#define CPCAP_BIT_UNUSED_259_5      0x00000020
+#define CPCAP_BIT_UNUSED_259_4      0x00000010
+#define CPCAP_BIT_UNUSED_259_3      0x00000008
+#define CPCAP_BIT_UNUSED_259_2      0x00000004
+#define CPCAP_BIT_UNUSED_259_1      0x00000002
+#define CPCAP_BIT_UCTM              0x00000001
+
+/*
+ * Register 266 - CPCAP_REG_VAL1 bits
+ */
+#define CPCAP_BIT_UNUSED_266_15                0x00008000
+#define CPCAP_BIT_UNUSED_266_14                0x00004000
+#define CPCAP_BIT_UNUSED_266_13                0x00002000
+#define CPCAP_BIT_UNUSED_266_12                0x00001000
+#define CPCAP_BIT_BOOT_MODE            0x00000800
+#define CPCAP_BIT_UNUSED_266_10                0x00000400
+#define CPCAP_BIT_OUT_CHARGE_ONLY      0x00000200
+#define CPCAP_BIT_USB_BATT_RECOVERY    0x00000100
+#define CPCAP_BIT_PANIC                        0x00000080
+#define CPCAP_BIT_BP_ONLY_FLASH                0x00000040
+#define CPCAP_BIT_WATCHDOG_RESET       0x00000020
+#define CPCAP_BIT_SOFT_RESET           0x00000010
+#define CPCAP_BIT_FLASH_FAIL           0x00000008
+#define CPCAP_BIT_FOTA_MODE            0x00000004
+#define CPCAP_BIT_AP_KERNEL_PANIC      0x00000002
+#define CPCAP_BIT_FLASH_MODE           0x00000001
+
+/*
+ * Register 385 - CPCAP_REG_SI2CC1
+ */
+#define CPCAP_BIT_CLK3M2_GATE_OVERRIDE 0x00000080
+
+/*
+ * Register 411 - CPCAP_REG_VUSB bits
+ */
+#define CPCAP_BIT_UNUSED_411_15                0x00008000
+#define CPCAP_BIT_UNUSED_411_14                0x00004000
+#define CPCAP_BIT_UNUSED_411_13                0x00002000
+#define CPCAP_BIT_UNUSED_411_12                0x00001000
+#define CPCAP_BIT_UNUSED_411_11                0x00000800
+#define CPCAP_BIT_UNUSED_411_10                0x00000400
+#define CPCAP_BIT_UNUSED_411_9         0x00000200
+#define CPCAP_BIT_VUSBSTBY             0x00000100
+#define CPCAP_BIT_UNUSED_411_7         0x00000080
+#define CPCAP_BIT_VUSB                 0x00000040
+#define CPCAP_BIT_UNUSED_411_5         0x00000020
+#define CPCAP_BIT_VUSB_MODE2           0x00000010
+#define CPCAP_BIT_VUSB_MODE1           0x00000008
+#define CPCAP_BIT_VUSB_MODE0           0x00000004
+#define CPCAP_BIT_SPARE_411_1          0x00000002
+#define CPCAP_BIT_VBUS_SWITCH          0x00000001
+/*
+ * Register 512 - Audio Regulator and Bias Voltage
+ */
+
+#define CPCAP_BIT_AUDIO_LOW_PWR           0x00000040
+#define CPCAP_BIT_AUD_LOWPWR_SPEED        0x00000020
+#define CPCAP_BIT_VAUDIOPRISTBY           0x00000010
+#define CPCAP_BIT_VAUDIO_MODE1            0x00000004
+#define CPCAP_BIT_VAUDIO_MODE0            0x00000002
+#define CPCAP_BIT_V_AUDIO_EN              0x00000001
+
+/*
+ * Register 513 CODEC
+ */
+
+#define CPCAP_BIT_CDC_CLK2                0x00008000
+#define CPCAP_BIT_CDC_CLK1                0x00004000
+#define CPCAP_BIT_CDC_CLK0                0x00002000
+#define CPCAP_BIT_CDC_SR3                 0x00001000
+#define CPCAP_BIT_CDC_SR2                 0x00000800
+#define CPCAP_BIT_CDC_SR1                 0x00000400
+#define CPCAP_BIT_CDC_SR0                 0x00000200
+#define CPCAP_BIT_CDC_CLOCK_TREE_RESET    0x00000100
+#define CPCAP_BIT_MIC2_CDC_EN             0x00000080
+#define CPCAP_BIT_CDC_EN_RX               0x00000040
+#define CPCAP_BIT_DF_RESET                0x00000020
+#define CPCAP_BIT_MIC1_CDC_EN             0x00000010
+#define CPCAP_BIT_AUDOHPF_1              0x00000008
+#define CPCAP_BIT_AUDOHPF_0              0x00000004
+#define CPCAP_BIT_AUDIHPF_1              0x00000002
+#define CPCAP_BIT_AUDIHPF_0              0x00000001
+
+/*
+ * Register 514 CODEC Digital Audio Interface
+ */
+
+#define CPCAP_BIT_CDC_PLL_SEL             0x00008000
+#define CPCAP_BIT_CLK_IN_SEL              0x00002000
+#define CPCAP_BIT_DIG_AUD_IN              0x00001000
+#define CPCAP_BIT_CDC_CLK_EN              0x00000800
+#define CPCAP_BIT_CDC_DIG_AUD_FS1         0x00000400
+#define CPCAP_BIT_CDC_DIG_AUD_FS0         0x00000200
+#define CPCAP_BIT_MIC2_TIMESLOT2          0x00000100
+#define CPCAP_BIT_MIC2_TIMESLOT1          0x00000080
+#define CPCAP_BIT_MIC2_TIMESLOT0          0x00000040
+#define CPCAP_BIT_MIC1_RX_TIMESLOT2       0x00000020
+#define CPCAP_BIT_MIC1_RX_TIMESLOT1       0x00000010
+#define CPCAP_BIT_MIC1_RX_TIMESLOT0       0x00000008
+#define CPCAP_BIT_FS_INV                  0x00000004
+#define CPCAP_BIT_CLK_INV                 0x00000002
+#define CPCAP_BIT_SMB_CDC                 0x00000001
+
+/*
+ * Register 515 Stereo DAC
+ */
+
+#define CPCAP_BIT_FSYNC_CLK_IN_COMMON     0x00000800
+#define CPCAP_BIT_SLAVE_PLL_CLK_INPUT     0x00000400
+#define CPCAP_BIT_ST_CLOCK_TREE_RESET     0x00000200
+#define CPCAP_BIT_DF_RESET_ST_DAC         0x00000100
+#define CPCAP_BIT_ST_SR3                  0x00000080
+#define CPCAP_BIT_ST_SR2                  0x00000040
+#define CPCAP_BIT_ST_SR1                  0x00000020
+#define CPCAP_BIT_ST_SR0                  0x00000010
+#define CPCAP_BIT_ST_DAC_CLK2             0x00000008
+#define CPCAP_BIT_ST_DAC_CLK1             0x00000004
+#define CPCAP_BIT_ST_DAC_CLK0             0x00000002
+#define CPCAP_BIT_ST_DAC_EN               0x00000001
+
+/*
+ * Register 516 Stereo DAC Digital Audio Interface
+ */
+
+#define CPCAP_BIT_ST_L_TIMESLOT2          0x00002000
+#define CPCAP_BIT_ST_L_TIMESLOT1          0x00001000
+#define CPCAP_BIT_ST_L_TIMESLOT0          0x00000800
+#define CPCAP_BIT_ST_R_TIMESLOT2          0x00000400
+#define CPCAP_BIT_ST_R_TIMESLOT1          0x00000200
+#define CPCAP_BIT_ST_R_TIMESLOT0          0x00000100
+#define CPCAP_BIT_ST_DAC_CLK_IN_SEL       0x00000080
+#define CPCAP_BIT_ST_FS_INV               0x00000040
+#define CPCAP_BIT_ST_CLK_INV              0x00000020
+#define CPCAP_BIT_ST_DIG_AUD_FS1          0x00000010
+#define CPCAP_BIT_ST_DIG_AUD_FS0          0x00000008
+#define CPCAP_BIT_DIG_AUD_IN_ST_DAC       0x00000004
+#define CPCAP_BIT_ST_CLK_EN               0x00000002
+#define CPCAP_BIT_SMB_ST_DAC              0x00000001
+
+/*
+ * Register 517 - CPCAP_REG_TXI bits
+ */
+#define CPCAP_BIT_PTT_TH               0x00008000
+#define CPCAP_BIT_PTT_CMP_EN           0x00004000
+#define CPCAP_BIT_HS_ID_TX             0x00002000
+#define CPCAP_BIT_MB_ON2               0x00001000
+#define CPCAP_BIT_MB_ON1L              0x00000800
+#define CPCAP_BIT_MB_ON1R              0x00000400
+#define CPCAP_BIT_RX_L_ENCODE          0x00000200
+#define CPCAP_BIT_RX_R_ENCODE          0x00000100
+#define CPCAP_BIT_MIC2_MUX             0x00000080
+#define CPCAP_BIT_MIC2_PGA_EN          0x00000040
+#define CPCAP_BIT_CDET_DIS             0x00000020
+#define CPCAP_BIT_EMU_MIC_MUX          0x00000010
+#define CPCAP_BIT_HS_MIC_MUX           0x00000008
+#define CPCAP_BIT_MIC1_MUX             0x00000004
+#define CPCAP_BIT_MIC1_PGA_EN          0x00000002
+#define CPCAP_BIT_DLM                  0x00000001
+
+/*
+ * Register 518 MIC PGA's
+ */
+#define CPCAP_BIT_MB_BIAS_R1              0x00000800
+#define CPCAP_BIT_MB_BIAS_R0              0x00000400
+#define CPCAP_BIT_MIC2_GAIN_4             0x00000200
+#define CPCAP_BIT_MIC2_GAIN_3             0x00000100
+#define CPCAP_BIT_MIC2_GAIN_2             0x00000080
+#define CPCAP_BIT_MIC2_GAIN_1             0x00000040
+#define CPCAP_BIT_MIC2_GAIN_0             0x00000020
+#define CPCAP_BIT_MIC1_GAIN_4             0x00000010
+#define CPCAP_BIT_MIC1_GAIN_3             0x00000008
+#define CPCAP_BIT_MIC1_GAIN_2             0x00000004
+#define CPCAP_BIT_MIC1_GAIN_1             0x00000002
+#define CPCAP_BIT_MIC1_GAIN_0             0x00000001
+
+/*
+ * Register 519 - CPCAP_REG_RXOA bits
+ */
+#define CPCAP_BIT_UNUSED_519_15                0x00008000
+#define CPCAP_BIT_UNUSED_519_14                0x00004000
+#define CPCAP_BIT_UNUSED_519_13                0x00002000
+#define CPCAP_BIT_STDAC_LOW_PWR_DISABLE        0x00001000
+#define CPCAP_BIT_HS_LOW_PWR           0x00000800
+#define CPCAP_BIT_HS_ID_RX             0x00000400
+#define CPCAP_BIT_ST_HS_CP_EN          0x00000200
+#define CPCAP_BIT_EMU_SPKR_R_EN                0x00000100
+#define CPCAP_BIT_EMU_SPKR_L_EN                0x00000080
+#define CPCAP_BIT_HS_L_EN              0x00000040
+#define CPCAP_BIT_HS_R_EN              0x00000020
+#define CPCAP_BIT_A4_LINEOUT_L_EN      0x00000010
+#define CPCAP_BIT_A4_LINEOUT_R_EN      0x00000008
+#define CPCAP_BIT_A2_LDSP_L_EN         0x00000004
+#define CPCAP_BIT_A2_LDSP_R_EN         0x00000002
+#define CPCAP_BIT_A1_EAR_EN            0x00000001
+
+/*
+ * Register 520 RX Volume Control
+ */
+#define CPCAP_BIT_VOL_EXT3                0x00008000
+#define CPCAP_BIT_VOL_EXT2                0x00004000
+#define CPCAP_BIT_VOL_EXT1                0x00002000
+#define CPCAP_BIT_VOL_EXT0                0x00001000
+#define CPCAP_BIT_VOL_DAC3                0x00000800
+#define CPCAP_BIT_VOL_DAC2                0x00000400
+#define CPCAP_BIT_VOL_DAC1                0x00000200
+#define CPCAP_BIT_VOL_DAC0                0x00000100
+#define CPCAP_BIT_VOL_DAC_LSB_1dB1        0x00000080
+#define CPCAP_BIT_VOL_DAC_LSB_1dB0        0x00000040
+#define CPCAP_BIT_VOL_CDC3                0x00000020
+#define CPCAP_BIT_VOL_CDC2                0x00000010
+#define CPCAP_BIT_VOL_CDC1                0x00000008
+#define CPCAP_BIT_VOL_CDC0                0x00000004
+#define CPCAP_BIT_VOL_CDC_LSB_1dB1        0x00000002
+#define CPCAP_BIT_VOL_CDC_LSB_1dB0        0x00000001
+
+/*
+ * Register 521 Codec to Output Amp Switches
+ */
+#define CPCAP_BIT_PGA_CDC_EN              0x00000400
+#define CPCAP_BIT_CDC_SW                  0x00000200
+#define CPCAP_BIT_PGA_OUTR_USBDP_CDC_SW   0x00000100
+#define CPCAP_BIT_PGA_OUTL_USBDN_CDC_SW   0x00000080
+#define CPCAP_BIT_ALEFT_HS_CDC_SW         0x00000040
+#define CPCAP_BIT_ARIGHT_HS_CDC_SW        0x00000020
+#define CPCAP_BIT_A4_LINEOUT_L_CDC_SW     0x00000010
+#define CPCAP_BIT_A4_LINEOUT_R_CDC_SW     0x00000008
+#define CPCAP_BIT_A2_LDSP_L_CDC_SW        0x00000004
+#define CPCAP_BIT_A2_LDSP_R_CDC_SW        0x00000002
+#define CPCAP_BIT_A1_EAR_CDC_SW           0x00000001
+
+/*
+ * Register 522 RX Stereo DAC to Output Amp Switches
+ */
+#define CPCAP_BIT_PGA_DAC_EN              0x00001000
+#define CPCAP_BIT_ST_DAC_SW               0x00000800
+#define CPCAP_BIT_MONO_DAC1               0x00000400
+#define CPCAP_BIT_MONO_DAC0               0x00000200
+#define CPCAP_BIT_PGA_OUTR_USBDP_DAC_SW   0x00000100
+#define CPCAP_BIT_PGA_OUTL_USBDN_DAC_SW   0x00000080
+#define CPCAP_BIT_ALEFT_HS_DAC_SW         0x00000040
+#define CPCAP_BIT_ARIGHT_HS_DAC_SW        0x00000020
+#define CPCAP_BIT_A4_LINEOUT_L_DAC_SW     0x00000010
+#define CPCAP_BIT_A4_LINEOUT_R_DAC_SW     0x00000008
+#define CPCAP_BIT_A2_LDSP_L_DAC_SW        0x00000004
+#define CPCAP_BIT_A2_LDSP_R_DAC_SW        0x00000002
+#define CPCAP_BIT_A1_EAR_DAC_SW           0x00000001
+
+/*
+ * Register 523 RX External PGA to Output Amp Switches
+ */
+#define CPCAP_BIT_PGA_EXT_L_EN            0x00004000
+#define CPCAP_BIT_PGA_EXT_R_EN            0x00002000
+#define CPCAP_BIT_PGA_IN_L_SW             0x00001000
+#define CPCAP_BIT_PGA_IN_R_SW             0x00000800
+#define CPCAP_BIT_MONO_EXT1               0x00000400
+#define CPCAP_BIT_MONO_EXT0               0x00000200
+#define CPCAP_BIT_PGA_OUTR_USBDP_EXT_SW   0x00000100
+#define CPCAP_BIT_PGA_OUTL_USBDN_EXT_SW   0x00000080
+#define CPCAP_BIT_ALEFT_HS_EXT_SW         0x00000040
+#define CPCAP_BIT_ARIGHT_HS_EXT_SW        0x00000020
+#define CPCAP_BIT_A4_LINEOUT_L_EXT_SW     0x00000010
+#define CPCAP_BIT_A4_LINEOUT_R_EXT_SW     0x00000008
+#define CPCAP_BIT_A2_LDSP_L_EXT_SW        0x00000004
+#define CPCAP_BIT_A2_LDSP_R_EXT_SW        0x00000002
+#define CPCAP_BIT_A1_EAR_EXT_SW           0x00000001
+
+/*
+ * Register 525 Loudspeaker Amplifier and Clock Configuration for Headset
+ */
+#define CPCAP_BIT_NCP_CLK_SYNC            0x00000080
+#define CPCAP_BIT_A2_CLK_SYNC             0x00000040
+#define CPCAP_BIT_A2_FREE_RUN             0x00000020
+#define CPCAP_BIT_A2_CLK2                 0x00000010
+#define CPCAP_BIT_A2_CLK1                 0x00000008
+#define CPCAP_BIT_A2_CLK0                 0x00000004
+#define CPCAP_BIT_A2_CLK_IN               0x00000002
+#define CPCAP_BIT_A2_CONFIG               0x00000001
+
+/*
+ * Register 641 - CPCAP_REG_CHRGR_1 bits
+ */
+#define CPCAP_BIT_UNUSED_641_15                0x00008000
+#define CPCAP_BIT_UNUSED_641_14                0x00004000
+#define CPCAP_BIT_CHRG_LED_EN          0x00002000
+#define CPCAP_BIT_RVRSMODE             0x00001000
+#define CPCAP_BIT_ICHRG_TR1            0x00000800
+#define CPCAP_BIT_ICHRG_TR0            0x00000400
+#define CPCAP_BIT_FET_OVRD             0x00000200
+#define CPCAP_BIT_FET_CTRL             0x00000100
+#define CPCAP_BIT_VCHRG3               0x00000080
+#define CPCAP_BIT_VCHRG2               0x00000040
+#define CPCAP_BIT_VCHRG1               0x00000020
+#define CPCAP_BIT_VCHRG0               0x00000010
+#define CPCAP_BIT_ICHRG3               0x00000008
+#define CPCAP_BIT_ICHRG2               0x00000004
+#define CPCAP_BIT_ICHRG1               0x00000002
+#define CPCAP_BIT_ICHRG0               0x00000001
+
+/*
+ * Register 768 - CPCAP_REG_ADCC1 bits
+ */
+#define CPCAP_BIT_ADEN_AUTO_CLR                0x00008000
+#define CPCAP_BIT_CAL_MODE             0x00004000
+#define CPCAP_BIT_ADC_CLK_SEL1         0x00002000
+#define CPCAP_BIT_ADC_CLK_SEL0         0x00001000
+#define CPCAP_BIT_ATOX                 0x00000800
+#define CPCAP_BIT_ATO3                 0x00000400
+#define CPCAP_BIT_ATO2                 0x00000200
+#define CPCAP_BIT_ATO1                 0x00000100
+#define CPCAP_BIT_ATO0                 0x00000080
+#define CPCAP_BIT_ADA2                 0x00000040
+#define CPCAP_BIT_ADA1                 0x00000020
+#define CPCAP_BIT_ADA0                 0x00000010
+#define CPCAP_BIT_AD_SEL1              0x00000008
+#define CPCAP_BIT_RAND1                        0x00000004
+#define CPCAP_BIT_RAND0                        0x00000002
+#define CPCAP_BIT_ADEN                 0x00000001
+
+/*
+ * Register 769 - CPCAP_REG_ADCC2 bits
+ */
+#define CPCAP_BIT_CAL_FACTOR_ENABLE    0x00008000
+#define CPCAP_BIT_BATDETB_EN           0x00004000
+#define CPCAP_BIT_ADTRIG_ONESHOT       0x00002000
+#define CPCAP_BIT_ASC                  0x00001000
+#define CPCAP_BIT_ATOX_PS_FACTOR        0x00000800
+#define CPCAP_BIT_ADC_PS_FACTOR1        0x00000400
+#define CPCAP_BIT_ADC_PS_FACTOR0        0x00000200
+#define CPCAP_BIT_AD4_SELECT           0x00000100
+#define CPCAP_BIT_ADC_BUSY             0x00000080
+#define CPCAP_BIT_THERMBIAS_EN         0x00000040
+#define CPCAP_BIT_ADTRIG_DIS           0x00000020
+#define CPCAP_BIT_LIADC                        0x00000010
+#define CPCAP_BIT_TS_REFEN             0x00000008
+#define CPCAP_BIT_TS_M2                        0x00000004
+#define CPCAP_BIT_TS_M1                        0x00000002
+#define CPCAP_BIT_TS_M0                        0x00000001
+
+/*
+ * Register 896 - CPCAP_REG_USBC1 bits
+ */
+#define CPCAP_BIT_IDPULSE              0x00008000
+#define CPCAP_BIT_ID100KPU             0x00004000
+#define CPCAP_BIT_IDPUCNTRL            0x00002000
+#define CPCAP_BIT_IDPU                 0x00001000
+#define CPCAP_BIT_IDPD                 0x00000800
+#define CPCAP_BIT_VBUSCHRGTMR3         0x00000400
+#define CPCAP_BIT_VBUSCHRGTMR2         0x00000200
+#define CPCAP_BIT_VBUSCHRGTMR1         0x00000100
+#define CPCAP_BIT_VBUSCHRGTMR0         0x00000080
+#define CPCAP_BIT_VBUSPU               0x00000040
+#define CPCAP_BIT_VBUSPD               0x00000020
+#define CPCAP_BIT_DMPD                 0x00000010
+#define CPCAP_BIT_DPPD                 0x00000008
+#define CPCAP_BIT_DM1K5PU              0x00000004
+#define CPCAP_BIT_DP1K5PU              0X00000002
+#define CPCAP_BIT_DP150KPU             0x00000001
+
+/*
+ * Register 897 - CPCAP_REG_USBC2 bits
+ */
+#define CPCAP_BIT_ZHSDRV1              0x00008000
+#define CPCAP_BIT_ZHSDRV0              0x00004000
+#define CPCAP_BIT_DPLLCLKREQ           0x00002000
+#define CPCAP_BIT_SE0CONN              0x00001000
+#define CPCAP_BIT_UARTTXTRI            0x00000800
+#define CPCAP_BIT_UARTSWAP             0x00000400
+#define CPCAP_BIT_UARTMUX1             0x00000200
+#define CPCAP_BIT_UARTMUX0             0x00000100
+#define CPCAP_BIT_ULPISTPLOW           0x00000080
+#define CPCAP_BIT_TXENPOL              0x00000040
+#define CPCAP_BIT_USBXCVREN            0x00000020
+#define CPCAP_BIT_USBCNTRL             0x00000010
+#define CPCAP_BIT_USBSUSPEND           0x00000008
+#define CPCAP_BIT_EMUMODE2             0x00000004
+#define CPCAP_BIT_EMUMODE1             0x00000002
+#define CPCAP_BIT_EMUMODE0             0x00000001
+
+/*
+ * Register 898 - CPCAP_REG_USBC3 bits
+ */
+#define CPCAP_BIT_SPARE_898_15         0x00008000
+#define CPCAP_BIT_IHSTX03              0x00004000
+#define CPCAP_BIT_IHSTX02              0x00002000
+#define CPCAP_BIT_IHSTX01              0x00001000
+#define CPCAP_BIT_IHSTX0               0x00000800
+#define CPCAP_BIT_IDPU_SPI             0x00000400
+#define CPCAP_BIT_UNUSED_898_9         0x00000200
+#define CPCAP_BIT_VBUSSTBY_EN          0x00000100
+#define CPCAP_BIT_VBUSEN_SPI           0x00000080
+#define CPCAP_BIT_VBUSPU_SPI           0x00000040
+#define CPCAP_BIT_VBUSPD_SPI           0x00000020
+#define CPCAP_BIT_DMPD_SPI             0x00000010
+#define CPCAP_BIT_DPPD_SPI             0x00000008
+#define CPCAP_BIT_SUSPEND_SPI          0x00000004
+#define CPCAP_BIT_PU_SPI               0x00000002
+#define CPCAP_BIT_ULPI_SPI_SEL         0x00000001
+
+/*
+ * Register 941 - CPCAP_REG_GPIO0 bits
+ */
+#define CPCAP_BIT_GPIO0MACROINITL      0x00008000
+#define CPCAP_BIT_GPIO0MACROINITH      0x00004000
+#define CPCAP_BIT_GPIO0MACROML         0x00002000
+#define CPCAP_BIT_GPIO0MACROMH         0x00001000
+#define CPCAP_BIT_UNUSED_941_11                0x00000800
+#define CPCAP_BIT_UNUSED_941_10                0x00000400
+#define CPCAP_BIT_GPIO0VLEV            0x00000200
+#define CPCAP_BIT_UNUSED_941_8         0x00000100
+#define CPCAP_BIT_GPIO0MUX1            0x00000080
+#define CPCAP_BIT_GPIO0MUX0            0x00000040
+#define CPCAP_BIT_GPIO0OT              0x00000020
+#define CPCAP_BIT_SPARE_941_4          0x00000010
+#define CPCAP_BIT_GPIO0PUEN            0x00000008
+#define CPCAP_BIT_GPIO0DIR             0x00000004
+#define CPCAP_BIT_GPIO0DRV             0x00000002
+#define CPCAP_BIT_GPIO0S               0x00000001
+
+/*
+ * Register 943 - CPCAP_REG_GPIO1 bits
+ */
+#define CPCAP_BIT_GPIO1MACROINITL      0x00008000
+#define CPCAP_BIT_GPIO1MACROINITH      0x00004000
+#define CPCAP_BIT_GPIO1MACROML         0x00002000
+#define CPCAP_BIT_GPIO1MACROMH         0x00001000
+#define CPCAP_BIT_UNUSED_943_11                0x00000800
+#define CPCAP_BIT_UNUSED_943_10                0x00000400
+#define CPCAP_BIT_GPIO1VLEV            0x00000200
+#define CPCAP_BIT_UNUSED_943_8         0x00000100
+#define CPCAP_BIT_GPIO1MUX1            0x00000080
+#define CPCAP_BIT_GPIO1MUX0            0x00000040
+#define CPCAP_BIT_GPIO1OT              0x00000020
+#define CPCAP_BIT_SPARE_943_4          0x00000010
+#define CPCAP_BIT_GPIO1PUEN            0x00000008
+#define CPCAP_BIT_GPIO1DIR             0x00000004
+#define CPCAP_BIT_GPIO1DRV             0x00000002
+#define CPCAP_BIT_GPIO1S               0x00000001
+
+/*
+ * Register 945 - CPCAP_REG_GPIO2 bits
+ */
+#define CPCAP_BIT_GPIO2MACROINITL      0x00008000
+#define CPCAP_BIT_GPIO2MACROINITH      0x00004000
+#define CPCAP_BIT_GPIO2MACROML         0x00002000
+#define CPCAP_BIT_GPIO2MACROMH         0x00001000
+#define CPCAP_BIT_UNUSED_945_11                0x00000800
+#define CPCAP_BIT_UNUSED_945_10                0x00000400
+#define CPCAP_BIT_GPIO2VLEV            0x00000200
+#define CPCAP_BIT_UNUSED_945_8         0x00000100
+#define CPCAP_BIT_GPIO2MUX1            0x00000080
+#define CPCAP_BIT_GPIO2MUX0            0x00000040
+#define CPCAP_BIT_GPIO2OT              0x00000020
+#define CPCAP_BIT_SPARE_945_4          0x00000010
+#define CPCAP_BIT_GPIO2PUEN            0x00000008
+#define CPCAP_BIT_GPIO2DIR             0x00000004
+#define CPCAP_BIT_GPIO2DRV             0x00000002
+#define CPCAP_BIT_GPIO2S               0x00000001
+
+/*
+ * Register 947 - CPCAP_REG_GPIO3 bits
+ */
+#define CPCAP_BIT_GPIO3MACROINITL      0x00008000
+#define CPCAP_BIT_GPIO3MACROINITH      0x00004000
+#define CPCAP_BIT_GPIO3MACROML         0x00002000
+#define CPCAP_BIT_GPIO3MACROMH         0x00001000
+#define CPCAP_BIT_UNUSED_947_11                0x00000800
+#define CPCAP_BIT_UNUSED_947_10                0x00000400
+#define CPCAP_BIT_GPIO3VLEV            0x00000200
+#define CPCAP_BIT_UNUSED_947_8         0x00000100
+#define CPCAP_BIT_GPIO3MUX1            0x00000080
+#define CPCAP_BIT_GPIO3MUX0            0x00000040
+#define CPCAP_BIT_GPIO3OT              0x00000020
+#define CPCAP_BIT_SPARE_947_4          0x00000010
+#define CPCAP_BIT_GPIO3PUEN            0x00000008
+#define CPCAP_BIT_GPIO3DIR             0x00000004
+#define CPCAP_BIT_GPIO3DRV             0x00000002
+#define CPCAP_BIT_GPIO3S               0x00000001
+
+/*
+ * Register 949 - CPCAP_REG_GPIO4 bits
+ */
+#define CPCAP_BIT_GPIO4MACROINITL      0x00008000
+#define CPCAP_BIT_GPIO4MACROINITH      0x00004000
+#define CPCAP_BIT_GPIO4MACROML         0x00002000
+#define CPCAP_BIT_GPIO4MACROMH         0x00001000
+#define CPCAP_BIT_UNUSED_949_11                0x00000800
+#define CPCAP_BIT_UNUSED_949_10                0x00000400
+#define CPCAP_BIT_GPIO4VLEV            0x00000200
+#define CPCAP_BIT_UNUSED_949_8         0x00000100
+#define CPCAP_BIT_GPIO4MUX1            0x00000080
+#define CPCAP_BIT_GPIO4MUX0            0x00000040
+#define CPCAP_BIT_GPIO4OT              0x00000020
+#define CPCAP_BIT_SPARE_949_4          0x00000010
+#define CPCAP_BIT_GPIO4PUEN            0x00000008
+#define CPCAP_BIT_GPIO4DIR             0x00000004
+#define CPCAP_BIT_GPIO4DRV             0x00000002
+#define CPCAP_BIT_GPIO4S               0x00000001
+
+/*
+ * Register 951 - CPCAP_REG_GPIO5 bits
+ */
+#define CPCAP_BIT_GPIO5MACROINITL      0x00008000
+#define CPCAP_BIT_GPIO5MACROINITH      0x00004000
+#define CPCAP_BIT_GPIO5MACROML         0x00002000
+#define CPCAP_BIT_GPIO5MACROMH         0x00001000
+#define CPCAP_BIT_UNUSED_951_11                0x00000800
+#define CPCAP_BIT_UNUSED_951_10                0x00000400
+#define CPCAP_BIT_GPIO5VLEV            0x00000200
+#define CPCAP_BIT_GPIO5MUX2            0x00000100
+#define CPCAP_BIT_GPIO5MUX1            0x00000080
+#define CPCAP_BIT_GPIO5MUX0            0x00000040
+#define CPCAP_BIT_GPIO5OT              0x00000020
+#define CPCAP_BIT_SPARE_951_4          0x00000010
+#define CPCAP_BIT_GPIO5PUEN            0x00000008
+#define CPCAP_BIT_GPIO5DIR             0x00000004
+#define CPCAP_BIT_GPIO5DRV             0x00000002
+#define CPCAP_BIT_GPIO5S               0x00000001
+
+/*
+ * Register 953 - CPCAP_REG_GPIO6 bits
+ */
+#define CPCAP_BIT_GPIO6MACROINITL      0x00008000
+#define CPCAP_BIT_GPIO6MACROINITH      0x00004000
+#define CPCAP_BIT_GPIO6MACROML         0x00002000
+#define CPCAP_BIT_GPIO6MACROMH         0x00001000
+#define CPCAP_BIT_UNUSED_953_11                0x00000800
+#define CPCAP_BIT_UNUSED_953_10                0x00000400
+#define CPCAP_BIT_GPIO6VLEV            0x00000200
+#define CPCAP_BIT_GPIO6MUX2            0x00000100
+#define CPCAP_BIT_GPIO6MUX1            0x00000080
+#define CPCAP_BIT_GPIO6MUX0            0x00000040
+#define CPCAP_BIT_GPIO6OT              0x00000020
+#define CPCAP_BIT_SPARE_953_4          0x00000010
+#define CPCAP_BIT_GPIO6PUEN            0x00000008
+#define CPCAP_BIT_GPIO6DIR             0x00000004
+#define CPCAP_BIT_GPIO6DRV             0x00000002
+#define CPCAP_BIT_GPIO6S               0x00000001
+
+#endif /* __CPCAP_REGBITS_H__ */
diff --git a/include/linux/spi/cpcap.h b/include/linux/spi/cpcap.h
new file mode 100644 (file)
index 0000000..fc80685
--- /dev/null
@@ -0,0 +1,791 @@
+#ifndef _LINUX_SPI_CPCAP_H
+#define _LINUX_SPI_CPCAP_H
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ *
+ */
+
+#include <linux/ioctl.h>
+#ifdef __KERNEL__
+#include <linux/workqueue.h>
+#include <linux/completion.h>
+#include <linux/power_supply.h>
+#include <linux/platform_device.h>
+#endif
+
+#ifdef CONFIG_RTC_INTF_SECCLKD
+#include <linux/rtc.h>
+#endif
+
+#define CPCAP_DEV_NAME "cpcap"
+#define CPCAP_NUM_REG_CPCAP (CPCAP_REG_END - CPCAP_REG_START + 1)
+
+#define CPCAP_IRQ_INT1_INDEX 0
+#define CPCAP_IRQ_INT2_INDEX 16
+#define CPCAP_IRQ_INT3_INDEX 32
+#define CPCAP_IRQ_INT4_INDEX 48
+#define CPCAP_IRQ_INT5_INDEX 64
+
+#define CPCAP_WHISPER_MODE_PU 0x00000001
+
+enum cpcap_regulator_id {
+       CPCAP_SW5,
+       CPCAP_VCAM,
+       CPCAP_VCSI,
+       CPCAP_VDAC,
+       CPCAP_VDIG,
+       CPCAP_VFUSE,
+       CPCAP_VHVIO,
+       CPCAP_VSDIO,
+       CPCAP_VPLL,
+       CPCAP_VRF1,
+       CPCAP_VRF2,
+       CPCAP_VRFREF,
+       CPCAP_VWLAN1,
+       CPCAP_VWLAN2,
+       CPCAP_VSIM,
+       CPCAP_VSIMCARD,
+       CPCAP_VVIB,
+       CPCAP_VUSB,
+       CPCAP_VAUDIO,
+
+       CPCAP_NUM_REGULATORS
+};
+
+/*
+ * Enumeration of all registers in the cpcap. Note that the register
+ * numbers on the CPCAP IC are not contiguous. The values of the enums below
+ * are not the actual register numbers.
+ */
+enum cpcap_reg {
+       CPCAP_REG_START,        /* Start of CPCAP registers. */
+
+       CPCAP_REG_INT1 = CPCAP_REG_START, /* Interrupt 1 */
+       CPCAP_REG_INT2,         /* Interrupt 2 */
+       CPCAP_REG_INT3,         /* Interrupt 3 */
+       CPCAP_REG_INT4,         /* Interrupt 4 */
+       CPCAP_REG_INTM1,        /* Interrupt Mask 1 */
+       CPCAP_REG_INTM2,        /* Interrupt Mask 2 */
+       CPCAP_REG_INTM3,        /* Interrupt Mask 3 */
+       CPCAP_REG_INTM4,        /* Interrupt Mask 4 */
+       CPCAP_REG_INTS1,        /* Interrupt Sense 1 */
+       CPCAP_REG_INTS2,        /* Interrupt Sense 2 */
+       CPCAP_REG_INTS3,        /* Interrupt Sense 3 */
+       CPCAP_REG_INTS4,        /* Interrupt Sense 4 */
+       CPCAP_REG_ASSIGN1,      /* Resource Assignment 1 */
+       CPCAP_REG_ASSIGN2,      /* Resource Assignment 2 */
+       CPCAP_REG_ASSIGN3,      /* Resource Assignment 3 */
+       CPCAP_REG_ASSIGN4,      /* Resource Assignment 4 */
+       CPCAP_REG_ASSIGN5,      /* Resource Assignment 5 */
+       CPCAP_REG_ASSIGN6,      /* Resource Assignment 6 */
+       CPCAP_REG_VERSC1,       /* Version Control 1 */
+       CPCAP_REG_VERSC2,       /* Version Control 2 */
+
+       CPCAP_REG_MI1,          /* Macro Interrupt 1 */
+       CPCAP_REG_MIM1,         /* Macro Interrupt Mask 1 */
+       CPCAP_REG_MI2,          /* Macro Interrupt 2 */
+       CPCAP_REG_MIM2,         /* Macro Interrupt Mask 2 */
+       CPCAP_REG_UCC1,         /* UC Control 1 */
+       CPCAP_REG_UCC2,         /* UC Control 2 */
+       CPCAP_REG_PC1,          /* Power Cut 1 */
+       CPCAP_REG_PC2,          /* Power Cut 2 */
+       CPCAP_REG_BPEOL,        /* BP and EOL */
+       CPCAP_REG_PGC,          /* Power Gate and Control */
+       CPCAP_REG_MT1,          /* Memory Transfer 1 */
+       CPCAP_REG_MT2,          /* Memory Transfer 2 */
+       CPCAP_REG_MT3,          /* Memory Transfer 3 */
+       CPCAP_REG_PF,           /* Print Format */
+
+       CPCAP_REG_SCC,          /* System Clock Control */
+       CPCAP_REG_SW1,          /* Stop Watch 1 */
+       CPCAP_REG_SW2,          /* Stop Watch 2 */
+       CPCAP_REG_UCTM,         /* UC Turbo Mode */
+       CPCAP_REG_TOD1,         /* Time of Day 1 */
+       CPCAP_REG_TOD2,         /* Time of Day 2 */
+       CPCAP_REG_TODA1,        /* Time of Day Alarm 1 */
+       CPCAP_REG_TODA2,        /* Time of Day Alarm 2 */
+       CPCAP_REG_DAY,          /* Day */
+       CPCAP_REG_DAYA,         /* Day Alarm */
+       CPCAP_REG_VAL1,         /* Validity 1 */
+       CPCAP_REG_VAL2,         /* Validity 2 */
+
+       CPCAP_REG_SDVSPLL,      /* Switcher DVS and PLL */
+       CPCAP_REG_SI2CC1,       /* Switcher I2C Control 1 */
+       CPCAP_REG_Si2CC2,       /* Switcher I2C Control 2 */
+       CPCAP_REG_S1C1,         /* Switcher 1 Control 1 */
+       CPCAP_REG_S1C2,         /* Switcher 1 Control 2 */
+       CPCAP_REG_S2C1,         /* Switcher 2 Control 1 */
+       CPCAP_REG_S2C2,         /* Switcher 2 Control 2 */
+       CPCAP_REG_S3C,          /* Switcher 3 Control */
+       CPCAP_REG_S4C1,         /* Switcher 4 Control 1 */
+       CPCAP_REG_S4C2,         /* Switcher 4 Control 2 */
+       CPCAP_REG_S5C,          /* Switcher 5 Control */
+       CPCAP_REG_S6C,          /* Switcher 6 Control */
+       CPCAP_REG_VCAMC,        /* VCAM Control */
+       CPCAP_REG_VCSIC,        /* VCSI Control */
+       CPCAP_REG_VDACC,        /* VDAC Control */
+       CPCAP_REG_VDIGC,        /* VDIG Control */
+       CPCAP_REG_VFUSEC,       /* VFUSE Control */
+       CPCAP_REG_VHVIOC,       /* VHVIO Control */
+       CPCAP_REG_VSDIOC,       /* VSDIO Control */
+       CPCAP_REG_VPLLC,        /* VPLL Control */
+       CPCAP_REG_VRF1C,        /* VRF1 Control */
+       CPCAP_REG_VRF2C,        /* VRF2 Control */
+       CPCAP_REG_VRFREFC,      /* VRFREF Control */
+       CPCAP_REG_VWLAN1C,      /* VWLAN1 Control */
+       CPCAP_REG_VWLAN2C,      /* VWLAN2 Control */
+       CPCAP_REG_VSIMC,        /* VSIM Control */
+       CPCAP_REG_VVIBC,        /* VVIB Control */
+       CPCAP_REG_VUSBC,        /* VUSB Control */
+       CPCAP_REG_VUSBINT1C,    /* VUSBINT1 Control */
+       CPCAP_REG_VUSBINT2C,    /* VUSBINT2 Control */
+       CPCAP_REG_URT,          /* Useroff Regulator Trigger */
+       CPCAP_REG_URM1,         /* Useroff Regulator Mask 1 */
+       CPCAP_REG_URM2,         /* Useroff Regulator Mask 2 */
+
+       CPCAP_REG_VAUDIOC,      /* VAUDIO Control */
+       CPCAP_REG_CC,           /* Codec Control */
+       CPCAP_REG_CDI,          /* Codec Digital Interface */
+       CPCAP_REG_SDAC,         /* Stereo DAC */
+       CPCAP_REG_SDACDI,       /* Stereo DAC Digital Interface */
+       CPCAP_REG_TXI,          /* TX Inputs */
+       CPCAP_REG_TXMP,         /* TX MIC PGA's */
+       CPCAP_REG_RXOA,         /* RX Output Amplifiers */
+       CPCAP_REG_RXVC,         /* RX Volume Control */
+       CPCAP_REG_RXCOA,        /* RX Codec to Output Amps */
+       CPCAP_REG_RXSDOA,       /* RX Stereo DAC to Output Amps */
+       CPCAP_REG_RXEPOA,       /* RX External PGA to Output Amps */
+       CPCAP_REG_RXLL,         /* RX Low Latency */
+       CPCAP_REG_A2LA,         /* A2 Loudspeaker Amplifier */
+       CPCAP_REG_MIPIS1,       /* MIPI Slimbus 1 */
+       CPCAP_REG_MIPIS2,       /* MIPI Slimbus 2 */
+       CPCAP_REG_MIPIS3,       /* MIPI Slimbus 3. */
+       CPCAP_REG_LVAB,         /* LMR Volume and A4 Balanced. */
+
+       CPCAP_REG_CCC1,         /* Coulomb Counter Control 1 */
+       CPCAP_REG_CRM,          /* Charger and Reverse Mode */
+       CPCAP_REG_CCCC2,        /* Coincell and Coulomb Ctr Ctrl 2 */
+       CPCAP_REG_CCS1,         /* Coulomb Counter Sample 1 */
+       CPCAP_REG_CCS2,         /* Coulomb Counter Sample 2 */
+       CPCAP_REG_CCA1,         /* Coulomb Counter Accumulator 1 */
+       CPCAP_REG_CCA2,         /* Coulomb Counter Accumulator 2 */
+       CPCAP_REG_CCM,          /* Coulomb Counter Mode */
+       CPCAP_REG_CCO,          /* Coulomb Counter Offset */
+       CPCAP_REG_CCI,          /* Coulomb Counter Integrator */
+
+       CPCAP_REG_ADCC1,        /* A/D Converter Configuration 1 */
+       CPCAP_REG_ADCC2,        /* A/D Converter Configuration 2 */
+       CPCAP_REG_ADCD0,        /* A/D Converter Data 0 */
+       CPCAP_REG_ADCD1,        /* A/D Converter Data 1 */
+       CPCAP_REG_ADCD2,        /* A/D Converter Data 2 */
+       CPCAP_REG_ADCD3,        /* A/D Converter Data 3 */
+       CPCAP_REG_ADCD4,        /* A/D Converter Data 4 */
+       CPCAP_REG_ADCD5,        /* A/D Converter Data 5 */
+       CPCAP_REG_ADCD6,        /* A/D Converter Data 6 */
+       CPCAP_REG_ADCD7,        /* A/D Converter Data 7 */
+       CPCAP_REG_ADCAL1,       /* A/D Converter Calibration 1 */
+       CPCAP_REG_ADCAL2,       /* A/D Converter Calibration 2 */
+
+       CPCAP_REG_USBC1,        /* USB Control 1 */
+       CPCAP_REG_USBC2,        /* USB Control 2 */
+       CPCAP_REG_USBC3,        /* USB Control 3 */
+       CPCAP_REG_UVIDL,        /* ULPI Vendor ID Low */
+       CPCAP_REG_UVIDH,        /* ULPI Vendor ID High */
+       CPCAP_REG_UPIDL,        /* ULPI Product ID Low */
+       CPCAP_REG_UPIDH,        /* ULPI Product ID High */
+       CPCAP_REG_UFC1,         /* ULPI Function Control 1 */
+       CPCAP_REG_UFC2,         /* ULPI Function Control 2 */
+       CPCAP_REG_UFC3,         /* ULPI Function Control 3 */
+       CPCAP_REG_UIC1,         /* ULPI Interface Control 1 */
+       CPCAP_REG_UIC2,         /* ULPI Interface Control 2 */
+       CPCAP_REG_UIC3,         /* ULPI Interface Control 3 */
+       CPCAP_REG_USBOTG1,      /* USB OTG Control 1 */
+       CPCAP_REG_USBOTG2,      /* USB OTG Control 2 */
+       CPCAP_REG_USBOTG3,      /* USB OTG Control 3 */
+       CPCAP_REG_UIER1,        /* USB Interrupt Enable Rising 1 */
+       CPCAP_REG_UIER2,        /* USB Interrupt Enable Rising 2 */
+       CPCAP_REG_UIER3,        /* USB Interrupt Enable Rising 3 */
+       CPCAP_REG_UIEF1,        /* USB Interrupt Enable Falling 1 */
+       CPCAP_REG_UIEF2,        /* USB Interrupt Enable Falling 1 */
+       CPCAP_REG_UIEF3,        /* USB Interrupt Enable Falling 1 */
+       CPCAP_REG_UIS,          /* USB Interrupt Status */
+       CPCAP_REG_UIL,          /* USB Interrupt Latch */
+       CPCAP_REG_USBD,         /* USB Debug */
+       CPCAP_REG_SCR1,         /* Scratch 1 */
+       CPCAP_REG_SCR2,         /* Scratch 2 */
+       CPCAP_REG_SCR3,         /* Scratch 3 */
+       CPCAP_REG_VMC,          /* Video Mux Control */
+       CPCAP_REG_OWDC,         /* One Wire Device Control */
+       CPCAP_REG_GPIO0,        /* GPIO 0 Control */
+       CPCAP_REG_GPIO1,        /* GPIO 1 Control */
+       CPCAP_REG_GPIO2,        /* GPIO 2 Control */
+       CPCAP_REG_GPIO3,        /* GPIO 3 Control */
+       CPCAP_REG_GPIO4,        /* GPIO 4 Control */
+       CPCAP_REG_GPIO5,        /* GPIO 5 Control */
+       CPCAP_REG_GPIO6,        /* GPIO 6 Control */
+
+       CPCAP_REG_MDLC,         /* Main Display Lighting Control */
+       CPCAP_REG_KLC,          /* Keypad Lighting Control */
+       CPCAP_REG_ADLC,         /* Aux Display Lighting Control */
+       CPCAP_REG_REDC,         /* Red Triode Control */
+       CPCAP_REG_GREENC,       /* Green Triode Control */
+       CPCAP_REG_BLUEC,        /* Blue Triode Control */
+       CPCAP_REG_CFC,          /* Camera Flash Control */
+       CPCAP_REG_ABC,          /* Adaptive Boost Control */
+       CPCAP_REG_BLEDC,        /* Bluetooth LED Control */
+       CPCAP_REG_CLEDC,        /* Camera Privacy LED Control */
+
+       CPCAP_REG_OW1C,         /* One Wire 1 Command */
+       CPCAP_REG_OW1D,         /* One Wire 1 Data */
+       CPCAP_REG_OW1I,         /* One Wire 1 Interrupt */
+       CPCAP_REG_OW1IE,        /* One Wire 1 Interrupt Enable */
+       CPCAP_REG_OW1,          /* One Wire 1 Control */
+       CPCAP_REG_OW2C,         /* One Wire 2 Command */
+       CPCAP_REG_OW2D,         /* One Wire 2 Data */
+       CPCAP_REG_OW2I,         /* One Wire 2 Interrupt */
+       CPCAP_REG_OW2IE,        /* One Wire 2 Interrupt Enable */
+       CPCAP_REG_OW2,          /* One Wire 2 Control */
+       CPCAP_REG_OW3C,         /* One Wire 3 Command */
+       CPCAP_REG_OW3D,         /* One Wire 3 Data */
+       CPCAP_REG_OW3I,         /* One Wire 3 Interrupt */
+       CPCAP_REG_OW3IE,        /* One Wire 3 Interrupt Enable */
+       CPCAP_REG_OW3,          /* One Wire 3 Control */
+       CPCAP_REG_GCAIC,        /* GCAI Clock Control */
+       CPCAP_REG_GCAIM,        /* GCAI GPIO Mode */
+       CPCAP_REG_LGDIR,        /* LMR GCAI GPIO Direction */
+       CPCAP_REG_LGPU,         /* LMR GCAI GPIO Pull-up */
+       CPCAP_REG_LGPIN,        /* LMR GCAI GPIO Pin */
+       CPCAP_REG_LGMASK,       /* LMR GCAI GPIO Mask */
+       CPCAP_REG_LDEB,         /* LMR Debounce Settings */
+       CPCAP_REG_LGDET,        /* LMR GCAI Detach Detect */
+       CPCAP_REG_LMISC,        /* LMR Misc Bits */
+       CPCAP_REG_LMACE,        /* LMR Mace IC Support */
+
+       CPCAP_REG_END = CPCAP_REG_LMACE, /* End of CPCAP registers. */
+
+       CPCAP_REG_MAX           /* The largest valid register value. */
+       = CPCAP_REG_END,
+
+       CPCAP_REG_SIZE = CPCAP_REG_MAX + 1,
+       CPCAP_REG_UNUSED = CPCAP_REG_MAX + 2,
+};
+
+enum {
+       CPCAP_IOCTL_NUM_TEST__START,
+       CPCAP_IOCTL_NUM_TEST_READ_REG,
+       CPCAP_IOCTL_NUM_TEST_WRITE_REG,
+       CPCAP_IOCTL_NUM_TEST__END,
+
+       CPCAP_IOCTL_NUM_ADC__START,
+       CPCAP_IOCTL_NUM_ADC_PHASE,
+       CPCAP_IOCTL_NUM_ADC__END,
+
+       CPCAP_IOCTL_NUM_BATT__START,
+       CPCAP_IOCTL_NUM_BATT_DISPLAY_UPDATE,
+       CPCAP_IOCTL_NUM_BATT_ATOD_ASYNC,
+       CPCAP_IOCTL_NUM_BATT_ATOD_SYNC,
+       CPCAP_IOCTL_NUM_BATT_ATOD_READ,
+       CPCAP_IOCTL_NUM_BATT__END,
+
+       CPCAP_IOCTL_NUM_UC__START,
+       CPCAP_IOCTL_NUM_UC_MACRO_START,
+       CPCAP_IOCTL_NUM_UC_MACRO_STOP,
+       CPCAP_IOCTL_NUM_UC_GET_VENDOR,
+       CPCAP_IOCTL_NUM_UC_SET_TURBO_MODE,
+       CPCAP_IOCTL_NUM_UC__END,
+
+#ifdef CONFIG_RTC_INTF_SECCLKD
+       CPCAP_IOCTL_NUM_RTC__START,
+       CPCAP_IOCTL_NUM_RTC_COUNT,
+       CPCAP_IOCTL_NUM_RTC__END,
+#endif
+
+       CPCAP_IOCTL_NUM_ACCY__START,
+       CPCAP_IOCTL_NUM_ACCY_WHISPER,
+       CPCAP_IOCTL_NUM_ACCY__END,
+};
+
+enum cpcap_irqs {
+       CPCAP_IRQ__START,               /* 1st supported interrupt event */
+       CPCAP_IRQ_HSCLK = CPCAP_IRQ_INT1_INDEX, /* High Speed Clock */
+       CPCAP_IRQ_PRIMAC,               /* Primary Macro */
+       CPCAP_IRQ_SECMAC,               /* Secondary Macro */
+       CPCAP_IRQ_LOWBPL,               /* Low Battery Low Threshold */
+       CPCAP_IRQ_SEC2PRI,              /* 2nd Macro to Primary Processor */
+       CPCAP_IRQ_LOWBPH,               /* Low Battery High Threshold  */
+       CPCAP_IRQ_EOL,                  /* End of Life */
+       CPCAP_IRQ_TS,                   /* Touchscreen */
+       CPCAP_IRQ_ADCDONE,              /* ADC Conversion Complete */
+       CPCAP_IRQ_HS,                   /* Headset */
+       CPCAP_IRQ_MB2,                  /* Mic Bias2 */
+       CPCAP_IRQ_VBUSOV,               /* Overvoltage Detected */
+       CPCAP_IRQ_RVRS_CHRG,            /* Reverse Charge */
+       CPCAP_IRQ_CHRG_DET,             /* Charger Detected */
+       CPCAP_IRQ_IDFLOAT,              /* ID Float */
+       CPCAP_IRQ_IDGND,                /* ID Ground */
+
+       CPCAP_IRQ_SE1 = CPCAP_IRQ_INT2_INDEX, /* SE1 Detector */
+       CPCAP_IRQ_SESSEND,              /* Session End */
+       CPCAP_IRQ_SESSVLD,              /* Session Valid */
+       CPCAP_IRQ_VBUSVLD,              /* VBUS Valid */
+       CPCAP_IRQ_CHRG_CURR1,           /* Charge Current Monitor (20mA) */
+       CPCAP_IRQ_CHRG_CURR2,           /* Charge Current Monitor (250mA) */
+       CPCAP_IRQ_RVRS_MODE,            /* Reverse Current Limit */
+       CPCAP_IRQ_ON,                   /* On Signal */
+       CPCAP_IRQ_ON2,                  /* On 2 Signal */
+       CPCAP_IRQ_CLK,                  /* 32k Clock Transition */
+       CPCAP_IRQ_1HZ,                  /* 1Hz Tick */
+       CPCAP_IRQ_PTT,                  /* Push To Talk */
+       CPCAP_IRQ_SE0CONN,              /* SE0 Condition */
+       CPCAP_IRQ_CHRG_SE1B,            /* CHRG_SE1B Pin */
+       CPCAP_IRQ_UART_ECHO_OVERRUN,    /* UART Buffer Overflow */
+       CPCAP_IRQ_EXTMEMHD,             /* External MEMHOLD */
+
+       CPCAP_IRQ_WARM = CPCAP_IRQ_INT3_INDEX, /* Warm Start */
+       CPCAP_IRQ_SYSRSTR,              /* System Restart */
+       CPCAP_IRQ_SOFTRST,              /* Soft Reset */
+       CPCAP_IRQ_DIEPWRDWN,            /* Die Temperature Powerdown */
+       CPCAP_IRQ_DIETEMPH,             /* Die Temperature High */
+       CPCAP_IRQ_PC,                   /* Power Cut */
+       CPCAP_IRQ_OFLOWSW,              /* Stopwatch Overflow */
+       CPCAP_IRQ_TODA,                 /* TOD Alarm */
+       CPCAP_IRQ_OPT_SEL_DTCH,         /* Detach Detect */
+       CPCAP_IRQ_OPT_SEL_STATE,        /* State Change */
+       CPCAP_IRQ_ONEWIRE1,             /* Onewire 1 Block */
+       CPCAP_IRQ_ONEWIRE2,             /* Onewire 2 Block */
+       CPCAP_IRQ_ONEWIRE3,             /* Onewire 3 Block */
+       CPCAP_IRQ_UCRESET,              /* Microcontroller Reset */
+       CPCAP_IRQ_PWRGOOD,              /* BP Turn On */
+       CPCAP_IRQ_USBDPLLCLK,           /* USB DPLL Status */
+
+       CPCAP_IRQ_DPI = CPCAP_IRQ_INT4_INDEX, /* DP Line */
+       CPCAP_IRQ_DMI,                  /* DM Line */
+       CPCAP_IRQ_UCBUSY,               /* Microcontroller Busy */
+       CPCAP_IRQ_GCAI_CURR1,           /* Charge Current Monitor (65mA) */
+       CPCAP_IRQ_GCAI_CURR2,           /* Charge Current Monitor (600mA) */
+       CPCAP_IRQ_SB_MAX_RETRANSMIT_ERR,/* SLIMbus Retransmit Error */
+       CPCAP_IRQ_BATTDETB,             /* Battery Presence Detected */
+       CPCAP_IRQ_PRIHALT,              /* Primary Microcontroller Halt */
+       CPCAP_IRQ_SECHALT,              /* Secondary Microcontroller Halt */
+       CPCAP_IRQ_CC_CAL,               /* CC Calibration */
+
+       CPCAP_IRQ_UC_PRIROMR = CPCAP_IRQ_INT5_INDEX, /* Prim ROM Rd Macro Int */
+       CPCAP_IRQ_UC_PRIRAMW,           /* Primary RAM Write Macro Int */
+       CPCAP_IRQ_UC_PRIRAMR,           /* Primary RAM Read Macro Int */
+       CPCAP_IRQ_UC_USEROFF,           /* USEROFF Macro Interrupt */
+       CPCAP_IRQ_UC_PRIMACRO_4,        /* Primary Macro 4 Interrupt */
+       CPCAP_IRQ_UC_PRIMACRO_5,        /* Primary Macro 5 Interrupt */
+       CPCAP_IRQ_UC_PRIMACRO_6,        /* Primary Macro 6 Interrupt */
+       CPCAP_IRQ_UC_PRIMACRO_7,        /* Primary Macro 7 Interrupt */
+       CPCAP_IRQ_UC_PRIMACRO_8,        /* Primary Macro 8 Interrupt */
+       CPCAP_IRQ_UC_PRIMACRO_9,        /* Primary Macro 9 Interrupt */
+       CPCAP_IRQ_UC_PRIMACRO_10,       /* Primary Macro 10 Interrupt */
+       CPCAP_IRQ_UC_PRIMACRO_11,       /* Primary Macro 11 Interrupt */
+       CPCAP_IRQ_UC_PRIMACRO_12,       /* Primary Macro 12 Interrupt */
+       CPCAP_IRQ_UC_PRIMACRO_13,       /* Primary Macro 13 Interrupt */
+       CPCAP_IRQ_UC_PRIMACRO_14,       /* Primary Macro 14 Interrupt */
+       CPCAP_IRQ_UC_PRIMACRO_15,       /* Primary Macro 15 Interrupt */
+       CPCAP_IRQ__NUM                  /* Number of allocated events */
+};
+
+enum cpcap_adc_bank0 {
+       CPCAP_ADC_AD0_BATTDETB,
+       CPCAP_ADC_BATTP,
+       CPCAP_ADC_VBUS,
+       CPCAP_ADC_AD3,
+       CPCAP_ADC_BPLUS_AD4,
+       CPCAP_ADC_CHG_ISENSE,
+       CPCAP_ADC_BATTI_ADC,
+       CPCAP_ADC_USB_ID,
+
+       CPCAP_ADC_BANK0_NUM,
+};
+
+enum cpcap_adc_bank1 {
+       CPCAP_ADC_AD8,
+       CPCAP_ADC_AD9,
+       CPCAP_ADC_LICELL,
+       CPCAP_ADC_HV_BATTP,
+       CPCAP_ADC_TSX1_AD12,
+       CPCAP_ADC_TSX2_AD13,
+       CPCAP_ADC_TSY1_AD14,
+       CPCAP_ADC_TSY2_AD15,
+
+       CPCAP_ADC_BANK1_NUM,
+};
+
+enum cpcap_adc_format {
+       CPCAP_ADC_FORMAT_RAW,
+       CPCAP_ADC_FORMAT_PHASED,
+       CPCAP_ADC_FORMAT_CONVERTED,
+};
+
+enum cpcap_adc_timing {
+       CPCAP_ADC_TIMING_IMM,
+       CPCAP_ADC_TIMING_IN,
+       CPCAP_ADC_TIMING_OUT,
+};
+
+enum cpcap_adc_type {
+       CPCAP_ADC_TYPE_BANK_0,
+       CPCAP_ADC_TYPE_BANK_1,
+       CPCAP_ADC_TYPE_BATT_PI,
+};
+
+enum cpcap_macro {
+       CPCAP_MACRO_ROMR,
+       CPCAP_MACRO_RAMW,
+       CPCAP_MACRO_RAMR,
+       CPCAP_MACRO_USEROFF,
+       CPCAP_MACRO_4,
+       CPCAP_MACRO_5,
+       CPCAP_MACRO_6,
+       CPCAP_MACRO_7,
+       CPCAP_MACRO_8,
+       CPCAP_MACRO_9,
+       CPCAP_MACRO_10,
+       CPCAP_MACRO_11,
+       CPCAP_MACRO_12,
+       CPCAP_MACRO_13,
+       CPCAP_MACRO_14,
+       CPCAP_MACRO_15,
+
+       CPCAP_MACRO__END,
+};
+
+enum cpcap_vendor {
+       CPCAP_VENDOR_ST,
+       CPCAP_VENDOR_TI,
+};
+
+enum cpcap_revision {
+       CPCAP_REVISION_1_0 = 0x08,
+       CPCAP_REVISION_1_1 = 0x09,
+       CPCAP_REVISION_2_0 = 0x10,
+       CPCAP_REVISION_2_1 = 0x11,
+};
+
+enum cpcap_batt_usb_model {
+       CPCAP_BATT_USB_MODEL_NONE,
+       CPCAP_BATT_USB_MODEL_USB,
+       CPCAP_BATT_USB_MODEL_FACTORY,
+};
+
+struct cpcap_spi_init_data {
+       enum cpcap_reg reg;
+       unsigned short data;
+};
+
+struct cpcap_adc_ato {
+       unsigned short ato_in;
+       unsigned short atox_in;
+       unsigned short adc_ps_factor_in;
+       unsigned short atox_ps_factor_in;
+       unsigned short ato_out;
+       unsigned short atox_out;
+       unsigned short adc_ps_factor_out;
+       unsigned short atox_ps_factor_out;
+};
+
+struct cpcap_display_led {
+       unsigned int display_reg;
+       unsigned int display_mask;
+       unsigned int display_on;
+       unsigned int display_off;
+       unsigned int display_init;
+       unsigned int poll_intvl;
+       unsigned int zone0;
+       unsigned int zone1;
+       unsigned int zone2;
+       unsigned int zone3;
+       unsigned int zone4;
+};
+
+struct cpcap_button_led {
+       unsigned int button_reg;
+       unsigned int button_mask;
+       unsigned int button_on;
+       unsigned int button_off;
+};
+
+struct cpcap_kpad_led {
+       unsigned int kpad_reg;
+       unsigned int kpad_mask;
+       unsigned int kpad_on;
+       unsigned int kpad_off;
+};
+
+struct cpcap_rgb_led {
+       unsigned int rgb_reg;
+       unsigned int rgb_mask;
+       unsigned int rgb_on;
+       unsigned int rgb_off;
+};
+
+struct cpcap_leds {
+       struct cpcap_display_led display_led;
+       struct cpcap_button_led button_led;
+       struct cpcap_kpad_led kpad_led;
+       struct cpcap_rgb_led rgb_led;
+};
+
+struct cpcap_batt_data {
+       int status;
+       int health;
+       int present;
+       int capacity;
+       int batt_volt;
+       int batt_temp;
+};
+
+struct cpcap_batt_ac_data {
+       int online;
+};
+
+struct cpcap_batt_usb_data {
+       int online;
+       int current_now;
+       enum cpcap_batt_usb_model model;
+};
+
+#ifdef CONFIG_RTC_INTF_SECCLKD
+struct cpcap_rtc_time_cnt {
+       struct rtc_time time;
+       unsigned short count;
+};
+#endif
+struct cpcap_device;
+
+#ifdef __KERNEL__
+struct cpcap_platform_data {
+       struct cpcap_spi_init_data *init;
+       int init_len;
+       unsigned short *regulator_mode_values;
+       unsigned short *regulator_off_mode_values;
+       struct regulator_init_data *regulator_init;
+       struct cpcap_adc_ato *adc_ato;
+       struct cpcap_leds *leds;
+       void (*ac_changed)(struct power_supply *,
+                          struct cpcap_batt_ac_data *);
+       void (*batt_changed)(struct power_supply *,
+                            struct cpcap_batt_data *);
+       void (*usb_changed)(struct power_supply *,
+                           struct cpcap_batt_usb_data *);
+};
+
+struct cpcap_whisper_pdata {
+       unsigned int gpio;
+       unsigned char uartmux;
+};
+
+struct cpcap_adc_request {
+       enum cpcap_adc_format format;
+       enum cpcap_adc_timing timing;
+       enum cpcap_adc_type type;
+       int status;
+       int result[CPCAP_ADC_BANK0_NUM];
+       void (*callback)(struct cpcap_device *, void *);
+       void *callback_param;
+
+       /* Used in case of sync requests */
+       struct completion completion;
+};
+#endif
+
+struct cpcap_adc_us_request {
+       enum cpcap_adc_format format;
+       enum cpcap_adc_timing timing;
+       enum cpcap_adc_type type;
+       int status;
+       int result[CPCAP_ADC_BANK0_NUM];
+};
+
+struct cpcap_adc_phase {
+       signed char offset_batti;
+       unsigned char slope_batti;
+       signed char offset_chrgi;
+       unsigned char slope_chrgi;
+       signed char offset_battp;
+       unsigned char slope_battp;
+       signed char offset_bp;
+       unsigned char slope_bp;
+       signed char offset_battt;
+       unsigned char slope_battt;
+       signed char offset_chrgv;
+       unsigned char slope_chrgv;
+};
+
+struct cpcap_regacc {
+       unsigned short reg;
+       unsigned short value;
+       unsigned short mask;
+};
+
+/*
+ * Gets the contents of the specified cpcap register.
+ *
+ * INPUTS: The register number in the cpcap driver's format.
+ *
+ * OUTPUTS: The command writes the register data back to user space at the
+ * location specified, or it may return an error code.
+ */
+#ifdef CONFIG_RTC_INTF_SECCLKD
+#define CPCAP_IOCTL_GET_RTC_TIME_COUNTER \
+       _IOR(0, CPCAP_IOCTL_NUM_RTC_COUNT, struct cpcap_rtc_time_cnt)
+#endif
+
+#define CPCAP_IOCTL_TEST_READ_REG \
+       _IOWR(0, CPCAP_IOCTL_NUM_TEST_READ_REG, struct cpcap_regacc*)
+
+/*
+ * Writes the specifed cpcap register.
+ *
+ * This function writes the specified cpcap register with the specified
+ * data.
+ *
+ * INPUTS: The register number in the cpcap driver's format and the data to
+ * write to that register.
+ *
+ * OUTPUTS: The command has no output other than the returned error code for
+ * the ioctl() call.
+ */
+#define CPCAP_IOCTL_TEST_WRITE_REG \
+       _IOWR(0, CPCAP_IOCTL_NUM_TEST_WRITE_REG, struct cpcap_regacc*)
+
+#define CPCAP_IOCTL_ADC_PHASE \
+       _IOWR(0, CPCAP_IOCTL_NUM_ADC_PHASE, struct cpcap_adc_phase*)
+
+#define CPCAP_IOCTL_BATT_DISPLAY_UPDATE \
+       _IOW(0, CPCAP_IOCTL_NUM_BATT_DISPLAY_UPDATE, struct cpcap_batt_data*)
+
+#define CPCAP_IOCTL_BATT_ATOD_ASYNC \
+       _IOW(0, CPCAP_IOCTL_NUM_BATT_ATOD_ASYNC, struct cpcap_adc_us_request*)
+
+#define CPCAP_IOCTL_BATT_ATOD_SYNC \
+       _IOWR(0, CPCAP_IOCTL_NUM_BATT_ATOD_SYNC, struct cpcap_adc_us_request*)
+
+#define CPCAP_IOCTL_BATT_ATOD_READ \
+       _IOWR(0, CPCAP_IOCTL_NUM_BATT_ATOD_READ, struct cpcap_adc_us_request*)
+
+
+#define CPCAP_IOCTL_UC_MACRO_START \
+       _IOWR(0, CPCAP_IOCTL_NUM_UC_MACRO_START, enum cpcap_macro)
+
+#define CPCAP_IOCTL_UC_MACRO_STOP \
+       _IOWR(0, CPCAP_IOCTL_NUM_UC_MACRO_STOP, enum cpcap_macro)
+
+#define CPCAP_IOCTL_UC_GET_VENDOR \
+       _IOWR(0, CPCAP_IOCTL_NUM_UC_GET_VENDOR, enum cpcap_vendor)
+
+#define CPCAP_IOCTL_UC_SET_TURBO_MODE \
+       _IOW(0, CPCAP_IOCTL_NUM_UC_SET_TURBO_MODE, unsigned short)
+
+#define CPCAP_IOCTL_ACCY_WHISPER \
+       _IOW(0, CPCAP_IOCTL_NUM_ACCY_WHISPER, unsigned long)
+
+#ifdef __KERNEL__
+struct cpcap_device {
+       struct spi_device       *spi;
+       enum cpcap_vendor       vendor;
+       enum cpcap_revision     revision;
+       void                    *keydata;
+       struct platform_device  *regulator_pdev[CPCAP_NUM_REGULATORS];
+       void                    *irqdata;
+       void                    *adcdata;
+       void                    *battdata;
+       void                    *ucdata;
+       void                    *accydata;
+       void                    (*h2w_new_state)(int);
+};
+
+static inline void cpcap_set_keydata(struct cpcap_device *cpcap, void *data)
+{
+       cpcap->keydata = data;
+}
+
+static inline void *cpcap_get_keydata(struct cpcap_device *cpcap)
+{
+       return cpcap->keydata;
+}
+
+int cpcap_regacc_write(struct cpcap_device *cpcap, enum cpcap_reg reg,
+                      unsigned short value, unsigned short mask);
+
+int cpcap_regacc_read(struct cpcap_device *cpcap, enum cpcap_reg reg,
+                     unsigned short *value_ptr);
+
+int cpcap_regacc_init(struct cpcap_device *cpcap);
+
+void cpcap_broadcast_key_event(struct cpcap_device *cpcap,
+                              unsigned int code, int value);
+
+int cpcap_irq_init(struct cpcap_device *cpcap);
+
+void cpcap_irq_shutdown(struct cpcap_device *cpcap);
+
+int cpcap_irq_register(struct cpcap_device *cpcap, enum cpcap_irqs irq,
+                      void (*cb_func) (enum cpcap_irqs, void *), void *data);
+
+int cpcap_irq_free(struct cpcap_device *cpcap, enum cpcap_irqs irq);
+
+int cpcap_irq_get_data(struct cpcap_device *cpcap, enum cpcap_irqs irq,
+                      void **data);
+
+int cpcap_irq_clear(struct cpcap_device *cpcap, enum cpcap_irqs int_event);
+
+int cpcap_irq_mask(struct cpcap_device *cpcap, enum cpcap_irqs int_event);
+
+int cpcap_irq_unmask(struct cpcap_device *cpcap, enum cpcap_irqs int_event);
+
+int cpcap_irq_mask_get(struct cpcap_device *cpcap, enum cpcap_irqs int_event);
+
+int cpcap_irq_sense(struct cpcap_device *cpcap, enum cpcap_irqs int_event,
+                   unsigned char clear);
+
+int cpcap_adc_sync_read(struct cpcap_device *cpcap,
+                       struct cpcap_adc_request *request);
+
+int cpcap_adc_async_read(struct cpcap_device *cpcap,
+                        struct cpcap_adc_request *request);
+
+void cpcap_adc_phase(struct cpcap_device *cpcap, struct cpcap_adc_phase *phase);
+
+void cpcap_batt_set_ac_prop(struct cpcap_device *cpcap, int online);
+
+void cpcap_batt_set_usb_prop_online(struct cpcap_device *cpcap, int online,
+                                   enum cpcap_batt_usb_model model);
+
+void cpcap_batt_set_usb_prop_curr(struct cpcap_device *cpcap,
+                                 unsigned int curr);
+
+int cpcap_uc_start(struct cpcap_device *cpcap, enum cpcap_macro macro);
+
+int cpcap_uc_stop(struct cpcap_device *cpcap, enum cpcap_macro macro);
+
+unsigned char cpcap_uc_status(struct cpcap_device *cpcap,
+                             enum cpcap_macro macro);
+
+int cpcap_accy_whisper(struct cpcap_device *cpcap, unsigned long cmd);
+
+#define  cpcap_driver_register platform_driver_register
+#define  cpcap_driver_unregister platform_driver_unregister
+
+int cpcap_device_register(struct platform_device *pdev);
+int cpcap_device_unregister(struct platform_device *pdev);
+
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_SPI_CPCAP_H */