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.
 
+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
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
+
+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.
 
+config REGULATOR_CPCAP
+       tristate "CPCAP regulator driver"
+       depends on MFD_CPCAP
+       help
+         Say Y here to support the voltage regulators on CPCAP
+
 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_CPCAP) += cpcap-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.
 
+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"
@@ -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.
 
+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
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_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_MFD_CPCAP) += cpcap/firmware_0_2x.fw cpcap/firmware_1_2x.fw
 
 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 */