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
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
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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(®_access);
+
+ retval = cpcap_config_for_read(spi, register_info_tbl
+ [reg].address, value_ptr);
+
+ mutex_unlock(®_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(®_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(®_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;
+}
--- /dev/null
+/*
+ * 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, ®val);
+ 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, ®val);
+
+ if (regval & (1 << macro))
+ retval = 1;
+ } else {
+ cpcap_regacc_read(cpcap, CPCAP_REG_MIM1, ®val);
+
+ 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");
--- /dev/null
+/*
+ * 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");
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
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
--- /dev/null
+/*
+ * 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(®ulators[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");
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"
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
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
--- /dev/null
+/*
+ * 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");
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-)
--- /dev/null
+: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
--- /dev/null
+:03E0913000917A918C2400000401000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012005030001E410F82A1000052A1000041301200503000B0410F82A1000052A10000413040B20040531A40B20040532640B20080532840B20020532A40B20020532C43829160438291624382916443829166438291684382916A4382916C40B2006990E640B2005990E840B2005190EA40B2A12090EC40B2000790EE43829170438291724382917443C2917843C291794382916E43C2917643D29177D0B201000110C0B201000108C0B2040000C0C0B2040000C640B20C80018440B200100180435C52A100004130120A435AB0B201009FA428041200503001623C0412005030000C4C4A4A4C413A52A100004130B0B2002011022C13B0B204000100280DB0B208000102280993C29176240843C29176D0B2002001C83C0243D29176B0B2004011022C22539291629292916E9162281C93C29177200AD3921806D3921808D392180A403F0003435E3C09C3921806C3921808C392180A403F0012434E4EC291774F82916E43829162B0B2008011022C06539291642405D0B2008001C843829164B0B2010011022C095392916690B2000A91662805D0B2010001C843829166B0B2020011022C095392916890B2001491682805D0B2020001C843829168B0B2040011022C095392916A90B2003C916A2805D0B2040001C84382916AB0B2100011022C295392916C90B20258916C280940B29000030040B2D17303024382916C3C0790B20257916C2003D0B240000302120050300102934C240243D29179B0B2080011022C0693C291792405D0B2080001C843C29179435C52A10000413080310020410C403E9140403D001012005030010CC0B240000302403E03FF4E0CF21C03044C810000438100024E0CF21C03064C810004438100064E0CF21C03084C8100084381000A4E0CF21C030A4C81000C4381000E4E0CF21C030C4C810010438100124E0CF21C030E4C810014438100164E0CF21C03104C8100184381001AF21E03124E81001C4381001E41A29174421F91749F8290EA280343E291783C16425E91789F8290E82805934E200343D291783C0C9F8290E62C07934E240543C2917843D291793C04934E240243D291795031002052A100004130421F150C421E150A4E0C4F0D821C9170721D9172930D3404E33CE33D531C630D921D90EE280C2003921C90EC28084E8291704F829172435C52A100004130434C52A100004130120C4EBC0000532C831D23FB413C52A1000041304C0F5D0F3C0343CC0000531C9F0C23FB52A100004130403C9160403D001A12005030FFDC00008A
+:0002901800913095
+:0000000001FF
--- /dev/null
+#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__ */
--- /dev/null
+#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 */