From c88d0874a5bbe611be6c0823a0e6fb2b4dc30b1e Mon Sep 17 00:00:00 2001 From: Greg Meiste Date: Thu, 13 May 2010 17:04:35 -0500 Subject: [PATCH] mfd: cpcap: Add CPCAP drivers Add driver for motorola's CPCAP PMIC. This includes the core in mfd, the rtc driver, and regulator drivers. Change-Id: I96e3a97673002f3264ae8a71f5c8db1fcb7846e3 Signed-off-by: Greg Meiste --- drivers/mfd/Kconfig | 6 + drivers/mfd/Makefile | 11 + drivers/mfd/cpcap-3mm5.c | 331 ++++++++++ drivers/mfd/cpcap-adc.c | 654 +++++++++++++++++++ drivers/mfd/cpcap-core.c | 469 ++++++++++++++ drivers/mfd/cpcap-irq.c | 507 +++++++++++++++ drivers/mfd/cpcap-key.c | 128 ++++ drivers/mfd/cpcap-regacc.c | 383 +++++++++++ drivers/mfd/cpcap-uc.c | 880 +++++++++++++++++++++++++ drivers/mfd/cpcap-whisper.c | 551 ++++++++++++++++ drivers/regulator/Kconfig | 6 + drivers/regulator/Makefile | 1 + drivers/regulator/cpcap-regulator.c | 574 +++++++++++++++++ drivers/rtc/Kconfig | 14 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-cpcap.c | 483 ++++++++++++++ firmware/Makefile | 1 + firmware/cpcap/firmware_0_2x.HEX | 67 ++ firmware/cpcap/firmware_1_2x.H16 | 3 + include/linux/spi/cpcap-regbits.h | 952 ++++++++++++++++++++++++++++ include/linux/spi/cpcap.h | 791 +++++++++++++++++++++++ 21 files changed, 6813 insertions(+) create mode 100644 drivers/mfd/cpcap-3mm5.c create mode 100644 drivers/mfd/cpcap-adc.c create mode 100644 drivers/mfd/cpcap-core.c create mode 100644 drivers/mfd/cpcap-irq.c create mode 100644 drivers/mfd/cpcap-key.c create mode 100644 drivers/mfd/cpcap-regacc.c create mode 100644 drivers/mfd/cpcap-uc.c create mode 100644 drivers/mfd/cpcap-whisper.c create mode 100644 drivers/regulator/cpcap-regulator.c create mode 100644 drivers/rtc/rtc-cpcap.c create mode 100644 firmware/cpcap/firmware_0_2x.HEX create mode 100644 firmware/cpcap/firmware_1_2x.H16 create mode 100644 include/linux/spi/cpcap-regbits.h create mode 100644 include/linux/spi/cpcap.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 0d762688effe..d2924a2cf3cd 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -523,6 +523,12 @@ config MFD_RDC321X southbridge which provides access to GPIOs and Watchdog using the southbridge PCI device configuration space. +config MFD_CPCAP + tristate "Support for CPCAP" + depends on SPI && FIRMWARE_IN_KERNEL + help + Say yes here if you want to include drivers for the CPCAP chip. + config MFD_JANZ_CMODIO tristate "Support for Janz CMOD-IO PCI MODULbus Carrier Board" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index feaeeaeeddb7..713f8176d506 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -76,3 +76,14 @@ obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o obj-$(CONFIG_MFD_JZ4740_ADC) += jz4740-adc.o obj-$(CONFIG_MFD_TPS6586X) += tps6586x.o + +cpcap-objs := cpcap-core.o \ + cpcap-irq.o \ + cpcap-regacc.o \ + cpcap-key.o \ + cpcap-whisper.o \ + cpcap-adc.o \ + cpcap-uc.o \ + cpcap-3mm5.o + +obj-$(CONFIG_MFD_CPCAP) += cpcap.o diff --git a/drivers/mfd/cpcap-3mm5.c b/drivers/mfd/cpcap-3mm5.c new file mode 100644 index 000000000000..68923cf6ebe3 --- /dev/null +++ b/drivers/mfd/cpcap-3mm5.c @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2009 Motorola, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +enum { + NO_DEVICE, + HEADSET_WITH_MIC, + HEADSET_WITHOUT_MIC, +}; + +struct cpcap_3mm5_data { + struct cpcap_device *cpcap; + struct switch_dev sdev; + unsigned int key_state; + struct regulator *regulator; + unsigned char audio_low_pwr_det; + unsigned char audio_low_pwr_mac13; + struct delayed_work work; +}; + +static ssize_t print_name(struct switch_dev *sdev, char *buf) +{ + switch (switch_get_state(sdev)) { + case NO_DEVICE: + return sprintf(buf, "No Device\n"); + case HEADSET_WITH_MIC: + return sprintf(buf, "Headset with mic\n"); + case HEADSET_WITHOUT_MIC: + return sprintf(buf, "Headset without mic\n"); + } + + return -EINVAL; +} + +static void audio_low_power_set(struct cpcap_3mm5_data *data, + unsigned char *flag) +{ + if (!(*flag)) { + regulator_set_mode(data->regulator, REGULATOR_MODE_STANDBY); + *flag = 1; + } +} + +static void audio_low_power_clear(struct cpcap_3mm5_data *data, + unsigned char *flag) +{ + if (*flag) { + regulator_set_mode(data->regulator, REGULATOR_MODE_NORMAL); + *flag = 0; + } +} + +static void send_key_event(struct cpcap_3mm5_data *data, unsigned int state) +{ + dev_info(&data->cpcap->spi->dev, "Headset key event: old=%d, new=%d\n", + data->key_state, state); + + if (data->key_state != state) { + data->key_state = state; + cpcap_broadcast_key_event(data->cpcap, KEY_MEDIA, state); + } +} + +static void hs_handler(enum cpcap_irqs irq, void *data) +{ + struct cpcap_3mm5_data *data_3mm5 = data; + int new_state = NO_DEVICE; + + if (irq != CPCAP_IRQ_HS) + return; + + /* HS sense of 1 means no headset present, 0 means headset attached. */ + if (cpcap_irq_sense(data_3mm5->cpcap, CPCAP_IRQ_HS, 1) == 1) { + cpcap_regacc_write(data_3mm5->cpcap, CPCAP_REG_TXI, 0, + (CPCAP_BIT_MB_ON2 | CPCAP_BIT_PTT_CMP_EN)); + cpcap_regacc_write(data_3mm5->cpcap, CPCAP_REG_RXOA, 0, + CPCAP_BIT_ST_HS_CP_EN); + audio_low_power_set(data_3mm5, &data_3mm5->audio_low_pwr_det); + + cpcap_irq_mask(data_3mm5->cpcap, CPCAP_IRQ_MB2); + cpcap_irq_mask(data_3mm5->cpcap, CPCAP_IRQ_UC_PRIMACRO_5); + + cpcap_irq_clear(data_3mm5->cpcap, CPCAP_IRQ_MB2); + cpcap_irq_clear(data_3mm5->cpcap, CPCAP_IRQ_UC_PRIMACRO_5); + + cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_HS); + + send_key_event(data_3mm5, 0); + + cpcap_uc_stop(data_3mm5->cpcap, CPCAP_MACRO_5); + } else { + cpcap_regacc_write(data_3mm5->cpcap, CPCAP_REG_TXI, + (CPCAP_BIT_MB_ON2 | CPCAP_BIT_PTT_CMP_EN), + (CPCAP_BIT_MB_ON2 | CPCAP_BIT_PTT_CMP_EN)); + cpcap_regacc_write(data_3mm5->cpcap, CPCAP_REG_RXOA, + CPCAP_BIT_ST_HS_CP_EN, + CPCAP_BIT_ST_HS_CP_EN); + audio_low_power_clear(data_3mm5, &data_3mm5->audio_low_pwr_det); + + /* Give PTTS time to settle */ + mdelay(2); + + if (cpcap_irq_sense(data_3mm5->cpcap, CPCAP_IRQ_PTT, 1) <= 0) { + /* Headset without mic and MFB is detected. (May also + * be a headset with the MFB pressed.) */ + new_state = HEADSET_WITHOUT_MIC; + } else + new_state = HEADSET_WITH_MIC; + + cpcap_irq_clear(data_3mm5->cpcap, CPCAP_IRQ_MB2); + cpcap_irq_clear(data_3mm5->cpcap, CPCAP_IRQ_UC_PRIMACRO_5); + + cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_HS); + cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_MB2); + cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_UC_PRIMACRO_5); + + cpcap_uc_start(data_3mm5->cpcap, CPCAP_MACRO_5); + } + + switch_set_state(&data_3mm5->sdev, new_state); + if (data_3mm5->cpcap->h2w_new_state) + data_3mm5->cpcap->h2w_new_state(new_state); + + dev_info(&data_3mm5->cpcap->spi->dev, "New headset state: %d\n", + new_state); +} + +static void key_handler(enum cpcap_irqs irq, void *data) +{ + struct cpcap_3mm5_data *data_3mm5 = data; + + if ((irq != CPCAP_IRQ_MB2) && (irq != CPCAP_IRQ_UC_PRIMACRO_5)) + return; + + if ((cpcap_irq_sense(data_3mm5->cpcap, CPCAP_IRQ_HS, 1) == 1) || + (switch_get_state(&data_3mm5->sdev) != HEADSET_WITH_MIC)) { + hs_handler(CPCAP_IRQ_HS, data_3mm5); + return; + } + + if ((cpcap_irq_sense(data_3mm5->cpcap, CPCAP_IRQ_MB2, 0) == 0) || + (cpcap_irq_sense(data_3mm5->cpcap, CPCAP_IRQ_PTT, 0) == 0)) { + send_key_event(data_3mm5, 1); + + /* If macro not available, only short presses are supported */ + if (!cpcap_uc_status(data_3mm5->cpcap, CPCAP_MACRO_5)) { + send_key_event(data_3mm5, 0); + + /* Attempt to restart the macro for next time. */ + cpcap_uc_start(data_3mm5->cpcap, CPCAP_MACRO_5); + } + } else + send_key_event(data_3mm5, 0); + + cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_MB2); + cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_UC_PRIMACRO_5); +} + +static void mac13_work(struct work_struct *work) +{ + struct cpcap_3mm5_data *data_3mm5 = + container_of(work, struct cpcap_3mm5_data, work.work); + + audio_low_power_set(data_3mm5, &data_3mm5->audio_low_pwr_mac13); + cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_UC_PRIMACRO_13); +} + +static void mac13_handler(enum cpcap_irqs irq, void *data) +{ + struct cpcap_3mm5_data *data_3mm5 = data; + + if (irq != CPCAP_IRQ_UC_PRIMACRO_13) + return; + + audio_low_power_clear(data_3mm5, &data_3mm5->audio_low_pwr_mac13); + schedule_delayed_work(&data_3mm5->work, msecs_to_jiffies(200)); +} + +static int __init cpcap_3mm5_probe(struct platform_device *pdev) +{ + int retval = 0; + struct cpcap_3mm5_data *data; + + if (pdev->dev.platform_data == NULL) { + dev_err(&pdev->dev, "no platform_data\n"); + return -EINVAL; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->cpcap = pdev->dev.platform_data; + data->audio_low_pwr_det = 1; + data->audio_low_pwr_mac13 = 1; + data->sdev.name = "h2w"; + data->sdev.print_name = print_name; + switch_dev_register(&data->sdev); + INIT_DELAYED_WORK(&data->work, mac13_work); + platform_set_drvdata(pdev, data); + + data->regulator = regulator_get(NULL, "vaudio"); + if (IS_ERR(data->regulator)) { + dev_err(&pdev->dev, "Could not get regulator for cpcap_3mm5\n"); + retval = PTR_ERR(data->regulator); + goto free_mem; + } + + regulator_set_voltage(data->regulator, 2775000, 2775000); + + retval = cpcap_irq_clear(data->cpcap, CPCAP_IRQ_HS); + retval |= cpcap_irq_clear(data->cpcap, CPCAP_IRQ_MB2); + retval |= cpcap_irq_clear(data->cpcap, CPCAP_IRQ_UC_PRIMACRO_5); + retval |= cpcap_irq_clear(data->cpcap, CPCAP_IRQ_UC_PRIMACRO_13); + if (retval) + goto reg_put; + + retval = cpcap_irq_register(data->cpcap, CPCAP_IRQ_HS, hs_handler, + data); + if (retval) + goto reg_put; + + retval = cpcap_irq_register(data->cpcap, CPCAP_IRQ_MB2, key_handler, + data); + if (retval) + goto free_hs; + + retval = cpcap_irq_register(data->cpcap, CPCAP_IRQ_UC_PRIMACRO_5, + key_handler, data); + if (retval) + goto free_mb2; + + if (data->cpcap->vendor == CPCAP_VENDOR_ST) { + retval = cpcap_irq_register(data->cpcap, + CPCAP_IRQ_UC_PRIMACRO_13, + mac13_handler, data); + if (retval) + goto free_mac5; + + cpcap_uc_start(data->cpcap, CPCAP_MACRO_13); + } + + hs_handler(CPCAP_IRQ_HS, data); + + return 0; + +free_mac5: + cpcap_irq_free(data->cpcap, CPCAP_IRQ_UC_PRIMACRO_5); +free_mb2: + cpcap_irq_free(data->cpcap, CPCAP_IRQ_MB2); +free_hs: + cpcap_irq_free(data->cpcap, CPCAP_IRQ_HS); +reg_put: + regulator_put(data->regulator); +free_mem: + kfree(data); + + return retval; +} + +static int __exit cpcap_3mm5_remove(struct platform_device *pdev) +{ + struct cpcap_3mm5_data *data = platform_get_drvdata(pdev); + + cancel_delayed_work_sync(&data->work); + + cpcap_irq_free(data->cpcap, CPCAP_IRQ_MB2); + cpcap_irq_free(data->cpcap, CPCAP_IRQ_HS); + cpcap_irq_free(data->cpcap, CPCAP_IRQ_UC_PRIMACRO_5); + cpcap_irq_free(data->cpcap, CPCAP_IRQ_UC_PRIMACRO_13); + + switch_dev_unregister(&data->sdev); + regulator_put(data->regulator); + + kfree(data); + return 0; +} + +static struct platform_driver cpcap_3mm5_driver = { + .probe = cpcap_3mm5_probe, + .remove = __exit_p(cpcap_3mm5_remove), + .driver = { + .name = "cpcap_3mm5", + .owner = THIS_MODULE, + }, +}; + +static int __init cpcap_3mm5_init(void) +{ + return platform_driver_register(&cpcap_3mm5_driver); +} +module_init(cpcap_3mm5_init); + +static void __exit cpcap_3mm5_exit(void) +{ + platform_driver_unregister(&cpcap_3mm5_driver); +} +module_exit(cpcap_3mm5_exit); + +MODULE_ALIAS("platform:cpcap_3mm5"); +MODULE_DESCRIPTION("CPCAP USB detection driver"); +MODULE_AUTHOR("Motorola"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/cpcap-adc.c b/drivers/mfd/cpcap-adc.c new file mode 100644 index 000000000000..98b5f3c4cc89 --- /dev/null +++ b/drivers/mfd/cpcap-adc.c @@ -0,0 +1,654 @@ +/* + * Copyright (C) 2009 Motorola, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +#define MAX_ADC_FIFO_DEPTH 8 /* this must be a power of 2 */ +#define MAX_TEMP_LVL 27 + +struct cpcap_adc { + struct cpcap_device *cpcap; + + /* Private stuff */ + struct cpcap_adc_request *queue[MAX_ADC_FIFO_DEPTH]; + int queue_head; + int queue_tail; + struct mutex queue_mutex; + struct delayed_work work; +}; + +struct phasing_tbl { + short offset; + unsigned short multiplier; + unsigned short divider; + short min; + short max; +}; + +static struct phasing_tbl bank0_phasing[CPCAP_ADC_BANK0_NUM] = { + [CPCAP_ADC_AD0_BATTDETB] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_BATTP] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_VBUS] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_AD3] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_BPLUS_AD4] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_CHG_ISENSE] = {0, 0x80, 0x80, -512, 511}, + [CPCAP_ADC_BATTI_ADC] = {0, 0x80, 0x80, -512, 511}, + [CPCAP_ADC_USB_ID] = {0, 0x80, 0x80, 0, 1023}, +}; + +static struct phasing_tbl bank1_phasing[CPCAP_ADC_BANK1_NUM] = { + [CPCAP_ADC_AD8] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_AD9] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_LICELL] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_HV_BATTP] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_TSX1_AD12] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_TSX2_AD13] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_TSY1_AD14] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_TSY2_AD15] = {0, 0x80, 0x80, 0, 1023}, +}; + +enum conv_type { + CONV_TYPE_NONE, + CONV_TYPE_DIRECT, + CONV_TYPE_MAPPING, +}; + +struct conversion_tbl { + enum conv_type conv_type; + int align_offset; + int conv_offset; + int multiplier; + int divider; +}; + +static struct conversion_tbl bank0_conversion[CPCAP_ADC_BANK0_NUM] = { + [CPCAP_ADC_AD0_BATTDETB] = {CONV_TYPE_MAPPING, 0, 0, 1, 1}, + [CPCAP_ADC_BATTP] = {CONV_TYPE_DIRECT, 0, 2400, 2300, 1023}, + [CPCAP_ADC_VBUS] = {CONV_TYPE_DIRECT, 0, 0, 10000, 1023}, + [CPCAP_ADC_AD3] = {CONV_TYPE_MAPPING, 0, 0, 1, 1}, + [CPCAP_ADC_BPLUS_AD4] = {CONV_TYPE_DIRECT, 0, 2400, 2300, 1023}, + [CPCAP_ADC_CHG_ISENSE] = {CONV_TYPE_DIRECT, -512, 2, 5000, 1023}, + [CPCAP_ADC_BATTI_ADC] = {CONV_TYPE_DIRECT, -512, 2, 5000, 1023}, + [CPCAP_ADC_USB_ID] = {CONV_TYPE_NONE, 0, 0, 1, 1}, +}; + +static struct conversion_tbl bank1_conversion[CPCAP_ADC_BANK1_NUM] = { + [CPCAP_ADC_AD8] = {CONV_TYPE_NONE, 0, 0, 1, 1}, + [CPCAP_ADC_AD9] = {CONV_TYPE_NONE, 0, 0, 1, 1}, + [CPCAP_ADC_LICELL] = {CONV_TYPE_DIRECT, 0, 0, 3400, 1023}, + [CPCAP_ADC_HV_BATTP] = {CONV_TYPE_NONE, 0, 0, 1, 1}, + [CPCAP_ADC_TSX1_AD12] = {CONV_TYPE_NONE, 0, 0, 1, 1}, + [CPCAP_ADC_TSX2_AD13] = {CONV_TYPE_NONE, 0, 0, 1, 1}, + [CPCAP_ADC_TSY1_AD14] = {CONV_TYPE_NONE, 0, 0, 1, 1}, + [CPCAP_ADC_TSY2_AD15] = {CONV_TYPE_NONE, 0, 0, 1, 1}, +}; + +static const unsigned short temp_map[MAX_TEMP_LVL][2] = { + {0x03ff, 233}, /* -40C */ + {0x03ff, 238}, /* -35C */ + {0x03ef, 243}, /* -30C */ + {0x03b2, 248}, /* -25C */ + {0x036c, 253}, /* -20C */ + {0x0320, 258}, /* -15C */ + {0x02d0, 263}, /* -10C */ + {0x027f, 268}, /* -5C */ + {0x022f, 273}, /* 0C */ + {0x01e4, 278}, /* 5C */ + {0x019f, 283}, /* 10C */ + {0x0161, 288}, /* 15C */ + {0x012b, 293}, /* 20C */ + {0x00fc, 298}, /* 25C */ + {0x00d4, 303}, /* 30C */ + {0x00b2, 308}, /* 35C */ + {0x0095, 313}, /* 40C */ + {0x007d, 318}, /* 45C */ + {0x0069, 323}, /* 50C */ + {0x0059, 328}, /* 55C */ + {0x004b, 333}, /* 60C */ + {0x003f, 338}, /* 65C */ + {0x0036, 343}, /* 70C */ + {0x002e, 348}, /* 75C */ + {0x0027, 353}, /* 80C */ + {0x0022, 358}, /* 85C */ + {0x001d, 363}, /* 90C */ +}; + +static unsigned short convert_to_kelvins(unsigned short value) +{ + int i; + unsigned short result = 0; + signed short alpha = 0; + + if (value <= temp_map[MAX_TEMP_LVL - 1][0]) + return temp_map[MAX_TEMP_LVL - 1][1]; + + if (value >= temp_map[0][0]) + return temp_map[0][1]; + + for (i = 0; i < MAX_TEMP_LVL - 1; i++) { + if ((value <= temp_map[i][0]) && + (value >= temp_map[i+1][0])) { + if (value == temp_map[i][0]) + result = temp_map[i][1]; + else if (value == temp_map[i+1][0]) + result = temp_map[i+1][1]; + else { + alpha = ((value - temp_map[i][0])*1000)/ + (temp_map[i+1][0] - temp_map[i][0]); + + result = temp_map[i][1] + + ((alpha*(temp_map[i+1][1] - + temp_map[i][1]))/1000); + } + break; + } + } + return result; +} + +static void adc_setup(struct cpcap_device *cpcap, + struct cpcap_adc_request *req) +{ + struct cpcap_adc_ato *ato; + struct cpcap_platform_data *data; + unsigned short value1 = 0; + unsigned short value2 = 0; + + data = cpcap->spi->controller_data; + ato = data->adc_ato; + + if (req->type == CPCAP_ADC_TYPE_BANK_1) + value1 |= CPCAP_BIT_AD_SEL1; + else if (req->type == CPCAP_ADC_TYPE_BATT_PI) + value1 |= CPCAP_BIT_RAND1; + + switch (req->timing) { + case CPCAP_ADC_TIMING_IN: + value1 |= ato->ato_in; + value1 |= ato->atox_in; + value2 |= ato->adc_ps_factor_in; + value2 |= ato->atox_ps_factor_in; + break; + + case CPCAP_ADC_TIMING_OUT: + value1 |= ato->ato_out; + value1 |= ato->atox_out; + value2 |= ato->adc_ps_factor_out; + value2 |= ato->atox_ps_factor_out; + break; + + case CPCAP_ADC_TIMING_IMM: + default: + break; + } + + cpcap_regacc_write(cpcap, CPCAP_REG_ADCC1, value1, + (CPCAP_BIT_CAL_MODE | CPCAP_BIT_ATOX | + CPCAP_BIT_ATO3 | CPCAP_BIT_ATO2 | + CPCAP_BIT_ATO1 | CPCAP_BIT_ATO0 | + CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 | + CPCAP_BIT_ADA0 | CPCAP_BIT_AD_SEL1 | + CPCAP_BIT_RAND1 | CPCAP_BIT_RAND0)); + + cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2, value2, + (CPCAP_BIT_ATOX_PS_FACTOR | + CPCAP_BIT_ADC_PS_FACTOR1 | + CPCAP_BIT_ADC_PS_FACTOR0)); + + if (req->timing == CPCAP_ADC_TIMING_IMM) { + cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_DIS, + CPCAP_BIT_ADTRIG_DIS); + cpcap_irq_clear(cpcap, CPCAP_IRQ_ADCDONE); + cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2, + CPCAP_BIT_ASC, + CPCAP_BIT_ASC); + } else { + cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_ONESHOT, + CPCAP_BIT_ADTRIG_ONESHOT); + cpcap_irq_clear(cpcap, CPCAP_IRQ_ADCDONE); + cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2, + 0, + CPCAP_BIT_ADTRIG_DIS); + } + + schedule_delayed_work(&((struct cpcap_adc *)(cpcap->adcdata))->work, + msecs_to_jiffies(500)); + + cpcap_irq_unmask(cpcap, CPCAP_IRQ_ADCDONE); +} + +static void adc_setup_calibrate(struct cpcap_device *cpcap, + enum cpcap_adc_bank0 chan) +{ + unsigned short value = 0; + unsigned long timeout = jiffies + msecs_to_jiffies(11); + + if ((chan != CPCAP_ADC_CHG_ISENSE) && + (chan != CPCAP_ADC_BATTI_ADC)) + return; + + value |= CPCAP_BIT_CAL_MODE | CPCAP_BIT_RAND0; + value |= ((chan << 4) & + (CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 | CPCAP_BIT_ADA0)); + + cpcap_regacc_write(cpcap, CPCAP_REG_ADCC1, value, + (CPCAP_BIT_CAL_MODE | CPCAP_BIT_ATOX | + CPCAP_BIT_ATO3 | CPCAP_BIT_ATO2 | + CPCAP_BIT_ATO1 | CPCAP_BIT_ATO0 | + CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 | + CPCAP_BIT_ADA0 | CPCAP_BIT_AD_SEL1 | + CPCAP_BIT_RAND1 | CPCAP_BIT_RAND0)); + + cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2, 0, + (CPCAP_BIT_ATOX_PS_FACTOR | + CPCAP_BIT_ADC_PS_FACTOR1 | + CPCAP_BIT_ADC_PS_FACTOR0)); + + cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_DIS, + CPCAP_BIT_ADTRIG_DIS); + + cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2, + CPCAP_BIT_ASC, + CPCAP_BIT_ASC); + + do { + schedule_timeout_uninterruptible(1); + cpcap_regacc_read(cpcap, CPCAP_REG_ADCC2, &value); + } while ((value & CPCAP_BIT_ASC) && time_before(jiffies, timeout)); + + if (value & CPCAP_BIT_ASC) + dev_err(&(cpcap->spi->dev), + "Timeout waiting for calibration to complete\n"); + + cpcap_irq_clear(cpcap, CPCAP_IRQ_ADCDONE); + + cpcap_regacc_write(cpcap, CPCAP_REG_ADCC1, 0, CPCAP_BIT_CAL_MODE); +} + +static void trigger_next_adc_job_if_any(struct cpcap_device *cpcap) +{ + struct cpcap_adc *adc = cpcap->adcdata; + int head; + + mutex_lock(&adc->queue_mutex); + + head = adc->queue_head; + + if (!adc->queue[head]) { + mutex_unlock(&adc->queue_mutex); + return; + } + mutex_unlock(&adc->queue_mutex); + + adc_setup(cpcap, adc->queue[head]); +} + +static int +adc_enqueue_request(struct cpcap_device *cpcap, struct cpcap_adc_request *req) +{ + struct cpcap_adc *adc = cpcap->adcdata; + int head; + int tail; + int running; + + mutex_lock(&adc->queue_mutex); + + head = adc->queue_head; + tail = adc->queue_tail; + running = (head != tail); + + if (adc->queue[tail]) { + mutex_unlock(&adc->queue_mutex); + return -EBUSY; + } + + adc->queue[tail] = req; + adc->queue_tail = (tail + 1) & (MAX_ADC_FIFO_DEPTH - 1); + + mutex_unlock(&adc->queue_mutex); + + if (!running) + trigger_next_adc_job_if_any(cpcap); + + return 0; +} + +static void +cpcap_adc_sync_read_callback(struct cpcap_device *cpcap, void *param) +{ + struct cpcap_adc_request *req = param; + + complete(&req->completion); +} + +int cpcap_adc_sync_read(struct cpcap_device *cpcap, + struct cpcap_adc_request *request) +{ + int ret; + + request->callback = cpcap_adc_sync_read_callback; + request->callback_param = request; + init_completion(&request->completion); + ret = adc_enqueue_request(cpcap, request); + if (ret) + return ret; + wait_for_completion(&request->completion); + + return 0; +} +EXPORT_SYMBOL_GPL(cpcap_adc_sync_read); + +int cpcap_adc_async_read(struct cpcap_device *cpcap, + struct cpcap_adc_request *request) +{ + return adc_enqueue_request(cpcap, request); +} +EXPORT_SYMBOL_GPL(cpcap_adc_async_read); + +void cpcap_adc_phase(struct cpcap_device *cpcap, struct cpcap_adc_phase *phase) +{ + bank0_phasing[CPCAP_ADC_BATTI_ADC].offset = phase->offset_batti; + bank0_phasing[CPCAP_ADC_BATTI_ADC].multiplier = phase->slope_batti; + + bank0_phasing[CPCAP_ADC_CHG_ISENSE].offset = phase->offset_chrgi; + bank0_phasing[CPCAP_ADC_CHG_ISENSE].multiplier = phase->slope_chrgi; + + bank0_phasing[CPCAP_ADC_BATTP].offset = phase->offset_battp; + bank0_phasing[CPCAP_ADC_BATTP].multiplier = phase->slope_battp; + + bank0_phasing[CPCAP_ADC_BPLUS_AD4].offset = phase->offset_bp; + bank0_phasing[CPCAP_ADC_BPLUS_AD4].multiplier = phase->slope_bp; + + bank0_phasing[CPCAP_ADC_AD0_BATTDETB].offset = phase->offset_battt; + bank0_phasing[CPCAP_ADC_AD0_BATTDETB].multiplier = phase->slope_battt; + + bank0_phasing[CPCAP_ADC_VBUS].offset = phase->offset_chrgv; + bank0_phasing[CPCAP_ADC_VBUS].multiplier = phase->slope_chrgv; +} +EXPORT_SYMBOL_GPL(cpcap_adc_phase); + +static void adc_phase(struct cpcap_adc_request *req, int index) +{ + struct conversion_tbl *conv_tbl = bank0_conversion; + struct phasing_tbl *phase_tbl = bank0_phasing; + int tbl_index = index; + + if (req->type == CPCAP_ADC_TYPE_BANK_1) { + conv_tbl = bank1_conversion; + phase_tbl = bank1_phasing; + } + + if (req->type == CPCAP_ADC_TYPE_BATT_PI) + tbl_index = (tbl_index % 2) ? CPCAP_ADC_BATTI_ADC : + CPCAP_ADC_BATTP; + + req->result[index] += conv_tbl[tbl_index].align_offset; + req->result[index] *= phase_tbl[tbl_index].multiplier; + req->result[index] /= phase_tbl[tbl_index].divider; + req->result[index] += phase_tbl[tbl_index].offset; + + if (req->result[index] < phase_tbl[tbl_index].min) + req->result[index] = phase_tbl[tbl_index].min; + else if (req->result[index] > phase_tbl[tbl_index].max) + req->result[index] = phase_tbl[tbl_index].max; +} + +static void adc_convert(struct cpcap_adc_request *req, int index) +{ + struct conversion_tbl *conv_tbl = bank0_conversion; + int tbl_index = index; + + if (req->type == CPCAP_ADC_TYPE_BANK_1) + conv_tbl = bank1_conversion; + + if (req->type == CPCAP_ADC_TYPE_BATT_PI) + tbl_index = (tbl_index % 2) ? CPCAP_ADC_BATTI_ADC : + CPCAP_ADC_BATTP; + + if (conv_tbl[tbl_index].conv_type == CONV_TYPE_DIRECT) { + req->result[index] *= conv_tbl[tbl_index].multiplier; + req->result[index] /= conv_tbl[tbl_index].divider; + req->result[index] += conv_tbl[tbl_index].conv_offset; + } else if (conv_tbl[tbl_index].conv_type == CONV_TYPE_MAPPING) + req->result[index] = convert_to_kelvins(req->result[tbl_index]); +} + +static void adc_raw(struct cpcap_adc_request *req, int index) +{ + struct conversion_tbl *conv_tbl = bank0_conversion; + struct phasing_tbl *phase_tbl = bank0_phasing; + int tbl_index = index; + + if (req->type == CPCAP_ADC_TYPE_BANK_1) + return; + + if (req->type == CPCAP_ADC_TYPE_BATT_PI) + tbl_index = (tbl_index % 2) ? CPCAP_ADC_BATTI_ADC : + CPCAP_ADC_BATTP; + + req->result[index] += conv_tbl[tbl_index].align_offset; + + if (req->result[index] < phase_tbl[tbl_index].min) + req->result[index] = phase_tbl[tbl_index].min; + else if (req->result[index] > phase_tbl[tbl_index].max) + req->result[index] = phase_tbl[tbl_index].max; +} + +static void adc_result(struct cpcap_device *cpcap, + struct cpcap_adc_request *req) +{ + int i; + int j; + + for (i = CPCAP_REG_ADCD0; i <= CPCAP_REG_ADCD7; i++) { + j = i - CPCAP_REG_ADCD0; + cpcap_regacc_read(cpcap, i, (unsigned short *)&req->result[j]); + req->result[j] &= 0x3FF; + + switch (req->format) { + case CPCAP_ADC_FORMAT_PHASED: + adc_phase(req, j); + break; + + case CPCAP_ADC_FORMAT_CONVERTED: + adc_phase(req, j); + adc_convert(req, j); + break; + + case CPCAP_ADC_FORMAT_RAW: + adc_raw(req, j); + break; + + default: + break; + } + } +} + +static void cpcap_adc_irq(enum cpcap_irqs irq, void *data) +{ + struct cpcap_adc *adc = data; + struct cpcap_device *cpcap = adc->cpcap; + struct cpcap_adc_request *req; + int head; + + cancel_delayed_work_sync(&adc->work); + + cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_DIS, + CPCAP_BIT_ADTRIG_DIS); + + mutex_lock(&adc->queue_mutex); + head = adc->queue_head; + + req = adc->queue[head]; + if (!req) { + dev_info(&(cpcap->spi->dev), + "cpcap_adc_irq: ADC queue empty!\n"); + mutex_unlock(&adc->queue_mutex); + return; + } + adc->queue[head] = NULL; + adc->queue_head = (head + 1) & (MAX_ADC_FIFO_DEPTH - 1); + + mutex_unlock(&adc->queue_mutex); + + adc_result(cpcap, req); + + trigger_next_adc_job_if_any(cpcap); + + req->status = 0; + + req->callback(cpcap, req->callback_param); +} + +static void cpcap_adc_cancel(struct work_struct *work) +{ + int head; + struct cpcap_adc_request *req; + struct cpcap_adc *adc = + container_of(work, struct cpcap_adc, work.work); + + cpcap_irq_mask(adc->cpcap, CPCAP_IRQ_ADCDONE); + + cpcap_regacc_write(adc->cpcap, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_DIS, + CPCAP_BIT_ADTRIG_DIS); + + mutex_lock(&adc->queue_mutex); + head = adc->queue_head; + + req = adc->queue[head]; + if (!req) { + dev_info(&(adc->cpcap->spi->dev), + "cpcap_adc_cancel: ADC queue empty!\n"); + mutex_unlock(&adc->queue_mutex); + return; + } + adc->queue[head] = NULL; + adc->queue_head = (head + 1) & (MAX_ADC_FIFO_DEPTH - 1); + + mutex_unlock(&adc->queue_mutex); + + req->status = -ETIMEDOUT; + + req->callback(adc->cpcap, req->callback_param); + + trigger_next_adc_job_if_any(adc->cpcap); +} + +static int __devinit cpcap_adc_probe(struct platform_device *pdev) +{ + struct cpcap_adc *adc; + unsigned short cal_data; + + if (pdev->dev.platform_data == NULL) { + dev_err(&pdev->dev, "no platform_data\n"); + return -EINVAL; + } + + adc = kzalloc(sizeof(*adc), GFP_KERNEL); + if (!adc) + return -ENOMEM; + + adc->cpcap = pdev->dev.platform_data; + + platform_set_drvdata(pdev, adc); + adc->cpcap->adcdata = adc; + + mutex_init(&adc->queue_mutex); + + adc_setup_calibrate(adc->cpcap, CPCAP_ADC_CHG_ISENSE); + adc_setup_calibrate(adc->cpcap, CPCAP_ADC_BATTI_ADC); + + cal_data = 0; + cpcap_regacc_read(adc->cpcap, CPCAP_REG_ADCAL1, &cal_data); + bank0_conversion[CPCAP_ADC_CHG_ISENSE].align_offset = + ((short)cal_data * -1); + cal_data = 0; + cpcap_regacc_read(adc->cpcap, CPCAP_REG_ADCAL2, &cal_data); + bank0_conversion[CPCAP_ADC_BATTI_ADC].align_offset = + ((short)cal_data * -1); + + INIT_DELAYED_WORK(&adc->work, cpcap_adc_cancel); + + cpcap_irq_register(adc->cpcap, CPCAP_IRQ_ADCDONE, + cpcap_adc_irq, adc); + + return 0; +} + +static int __devexit cpcap_adc_remove(struct platform_device *pdev) +{ + struct cpcap_adc *adc = platform_get_drvdata(pdev); + int head; + + cancel_delayed_work_sync(&adc->work); + + cpcap_irq_free(adc->cpcap, CPCAP_IRQ_ADCDONE); + + mutex_lock(&adc->queue_mutex); + head = adc->queue_head; + + if (WARN_ON(adc->queue[head])) + dev_err(&pdev->dev, + "adc driver removed with request pending\n"); + + mutex_unlock(&adc->queue_mutex); + kfree(adc); + + return 0; +} + +static struct platform_driver cpcap_adc_driver = { + .driver = { + .name = "cpcap_adc", + }, + .probe = cpcap_adc_probe, + .remove = __devexit_p(cpcap_adc_remove), +}; + +static int __init cpcap_adc_init(void) +{ + return platform_driver_register(&cpcap_adc_driver); +} +module_init(cpcap_adc_init); + +static void __exit cpcap_adc_exit(void) +{ + platform_driver_unregister(&cpcap_adc_driver); +} +module_exit(cpcap_adc_exit); + +MODULE_ALIAS("platform:cpcap_adc"); +MODULE_DESCRIPTION("CPCAP ADC driver"); +MODULE_AUTHOR("Motorola"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/cpcap-core.c b/drivers/mfd/cpcap-core.c new file mode 100644 index 000000000000..858c0cf33c8c --- /dev/null +++ b/drivers/mfd/cpcap-core.c @@ -0,0 +1,469 @@ +/* + * Copyright (C) 2007-2010 Motorola, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct cpcap_driver_info { + struct list_head list; + struct platform_device *pdev; +}; + +static int ioctl(struct inode *inode, + struct file *file, unsigned int cmd, unsigned long arg); +static int __devinit cpcap_probe(struct spi_device *spi); +static int __devexit cpcap_remove(struct spi_device *spi); + +const static struct file_operations cpcap_fops = { + .owner = THIS_MODULE, + .ioctl = ioctl, +}; + +static struct miscdevice cpcap_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = CPCAP_DEV_NAME, + .fops = &cpcap_fops, +}; + +static struct spi_driver cpcap_driver = { + .driver = { + .name = "cpcap", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = cpcap_probe, + .remove = __devexit_p(cpcap_remove), +}; + +static struct platform_device cpcap_adc_device = { + .name = "cpcap_adc", + .id = -1, + .dev.platform_data = NULL, +}; + + +static struct platform_device cpcap_key_device = { + .name = "cpcap_key", + .id = -1, + .dev.platform_data = NULL, +}; + +static struct platform_device cpcap_uc_device = { + .name = "cpcap_uc", + .id = -1, + .dev.platform_data = NULL, +}; + +static struct platform_device cpcap_rtc_device = { + .name = "cpcap_rtc", + .id = -1, + .dev.platform_data = NULL, +}; + +/* List of required CPCAP devices that will ALWAYS be present. + * + * DO NOT ADD NEW DEVICES TO THIS LIST! You must use cpcap_driver_register() + * for any new drivers for non-core functionality of CPCAP. + */ +static struct platform_device *cpcap_devices[] = { + &cpcap_uc_device, + &cpcap_adc_device, + &cpcap_key_device, + &cpcap_rtc_device, +}; + +static struct cpcap_device *misc_cpcap; + +static LIST_HEAD(cpcap_device_list); +static DEFINE_MUTEX(cpcap_driver_lock); + +static int cpcap_reboot(struct notifier_block *this, unsigned long code, + void *cmd) +{ + int ret = -1; + int result = NOTIFY_DONE; + + /* Disable the USB transceiver */ + ret = cpcap_regacc_write(misc_cpcap, CPCAP_REG_USBC2, 0, + CPCAP_BIT_USBXCVREN); + + if (ret) { + dev_err(&(misc_cpcap->spi->dev), + "Disable Transciever failure.\n"); + result = NOTIFY_BAD; + } + + if (code == SYS_RESTART) + cpcap_regacc_write(misc_cpcap, CPCAP_REG_MI2, 0, 0xFFFF); + + /* Always clear the power cut bit on SW Shutdown*/ + ret = cpcap_regacc_write(misc_cpcap, CPCAP_REG_PC1, + 0, CPCAP_BIT_PC1_PCEN); + if (ret) { + dev_err(&(misc_cpcap->spi->dev), + "Clear Power Cut bit failure.\n"); + result = NOTIFY_BAD; + } + + /* Clear the charger and charge path settings to avoid a false turn on + * event in caused by CPCAP. After clearing these settings, 100ms is + * needed to before SYSRSTRTB is pulled low to avoid the false turn on + * event. + */ + cpcap_regacc_write(misc_cpcap, CPCAP_REG_CRM, 0, 0x3FFF); + mdelay(100); + + return result; +} +static struct notifier_block cpcap_reboot_notifier = { + .notifier_call = cpcap_reboot, +}; + +static int __init cpcap_init(void) +{ + return spi_register_driver(&cpcap_driver); +} + +static void cpcap_vendor_read(struct cpcap_device *cpcap) +{ + unsigned short value; + + (void)cpcap_regacc_read(cpcap, CPCAP_REG_VERSC1, &value); + + cpcap->vendor = (enum cpcap_vendor)((value >> 6) & 0x0007); + cpcap->revision = (enum cpcap_revision)(((value >> 3) & 0x0007) | + ((value << 3) & 0x0038)); +} + + +int cpcap_device_unregister(struct platform_device *pdev) +{ + struct cpcap_driver_info *info; + struct cpcap_driver_info *tmp; + int found; + + + found = 0; + mutex_lock(&cpcap_driver_lock); + + list_for_each_entry_safe(info, tmp, &cpcap_device_list, list) { + if (info->pdev == pdev) { + list_del(&info->list); + + /* + * misc_cpcap != NULL suggests pdev + * already registered + */ + if (misc_cpcap) { + printk(KERN_INFO "CPCAP: unregister %s\n", + pdev->name); + platform_device_unregister(pdev); + } + info->pdev = NULL; + kfree(info); + found = 1; + } + } + + mutex_unlock(&cpcap_driver_lock); + + BUG_ON(!found); + return 0; +} + +int cpcap_device_register(struct platform_device *pdev) +{ + int retval; + struct cpcap_driver_info *info; + + retval = 0; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + printk(KERN_ERR "Cannot save device %s\n", pdev->name); + return -ENOMEM; + } + + mutex_lock(&cpcap_driver_lock); + + info->pdev = pdev; + list_add_tail(&info->list, &cpcap_device_list); + + /* If misc_cpcap is valid, the CPCAP driver has already been probed. + * Therefore, call platform_device_register() to probe the device. + */ + if (misc_cpcap) { + dev_info(&(misc_cpcap->spi->dev), + "Probing CPCAP device %s\n", pdev->name); + + /* + * platform_data is non-empty indicates + * CPCAP client devices need to pass their own data + * In that case we put cpcap data in driver_data + */ + if (pdev->dev.platform_data != NULL) + platform_set_drvdata(pdev, misc_cpcap); + else + pdev->dev.platform_data = misc_cpcap; + retval = platform_device_register(pdev); + } else + printk(KERN_INFO "CPCAP: delaying %s probe\n", + pdev->name); + mutex_unlock(&cpcap_driver_lock); + + return retval; +} + +static int __devinit cpcap_probe(struct spi_device *spi) +{ + int retval = -EINVAL; + struct cpcap_device *cpcap; + struct cpcap_platform_data *data; + int i; + struct cpcap_driver_info *info; + + cpcap = kzalloc(sizeof(*cpcap), GFP_KERNEL); + if (cpcap == NULL) + return -ENOMEM; + + cpcap->spi = spi; + data = spi->controller_data; + spi_set_drvdata(spi, cpcap); + + retval = cpcap_regacc_init(cpcap); + if (retval < 0) + goto free_mem; + retval = cpcap_irq_init(cpcap); + if (retval < 0) + goto free_cpcap_irq; + + cpcap_vendor_read(cpcap); + + for (i = 0; i < ARRAY_SIZE(cpcap_devices); i++) + cpcap_devices[i]->dev.platform_data = cpcap; + + retval = misc_register(&cpcap_dev); + if (retval < 0) + goto free_cpcap_irq; + + /* loop twice becuase cpcap_regulator_probe may refer to other devices + * in this list to handle dependencies between regulators. Create them + * all and then add them */ + for (i = 0; i < CPCAP_NUM_REGULATORS; i++) { + struct platform_device *pdev; + + pdev = platform_device_alloc("cpcap-regltr", i); + if (!pdev) { + dev_err(&(spi->dev), "Cannot create regulator\n"); + continue; + } + + pdev->dev.parent = &(spi->dev); + pdev->dev.platform_data = &data->regulator_init[i]; + platform_set_drvdata(pdev, cpcap); + cpcap->regulator_pdev[i] = pdev; + } + + for (i = 0; i < CPCAP_NUM_REGULATORS; i++) { + /* vusb has to be added after sw5 so skip it for now, + * it will be added from probe of sw5 */ + if (i == CPCAP_VUSB) + continue; + platform_device_add(cpcap->regulator_pdev[i]); + } + + platform_add_devices(cpcap_devices, ARRAY_SIZE(cpcap_devices)); + + mutex_lock(&cpcap_driver_lock); + misc_cpcap = cpcap; /* kept for misc device */ + + list_for_each_entry(info, &cpcap_device_list, list) { + dev_info(&(spi->dev), "Probing CPCAP device %s\n", + info->pdev->name); + if (info->pdev->dev.platform_data != NULL) + platform_set_drvdata(info->pdev, cpcap); + else + info->pdev->dev.platform_data = cpcap; + platform_device_register(info->pdev); + } + mutex_unlock(&cpcap_driver_lock); + + register_reboot_notifier(&cpcap_reboot_notifier); + + return 0; + +free_cpcap_irq: + cpcap_irq_shutdown(cpcap); +free_mem: + kfree(cpcap); + return retval; +} + +static int __devexit cpcap_remove(struct spi_device *spi) +{ + struct cpcap_device *cpcap = spi_get_drvdata(spi); + struct cpcap_driver_info *info; + int i; + + unregister_reboot_notifier(&cpcap_reboot_notifier); + + mutex_lock(&cpcap_driver_lock); + list_for_each_entry(info, &cpcap_device_list, list) { + dev_info(&(spi->dev), "Removing CPCAP device %s\n", + info->pdev->name); + platform_device_unregister(info->pdev); + } + misc_cpcap = NULL; + mutex_unlock(&cpcap_driver_lock); + + for (i = ARRAY_SIZE(cpcap_devices); i > 0; i--) + platform_device_unregister(cpcap_devices[i-1]); + + for (i = 0; i < CPCAP_NUM_REGULATORS; i++) + platform_device_unregister(cpcap->regulator_pdev[i]); + + misc_deregister(&cpcap_dev); + cpcap_irq_shutdown(cpcap); + kfree(cpcap); + return 0; +} + + +static int test_ioctl(unsigned int cmd, unsigned long arg) +{ + int retval = -EINVAL; + struct cpcap_regacc read_data; + struct cpcap_regacc write_data; + + switch (cmd) { + case CPCAP_IOCTL_TEST_READ_REG: + if (copy_from_user((void *)&read_data, (void *)arg, + sizeof(read_data))) + return -EFAULT; + retval = cpcap_regacc_read(misc_cpcap, read_data.reg, + &read_data.value); + if (retval < 0) + return retval; + if (copy_to_user((void *)arg, (void *)&read_data, + sizeof(read_data))) + return -EFAULT; + return 0; + break; + + case CPCAP_IOCTL_TEST_WRITE_REG: + if (copy_from_user((void *) &write_data, + (void *) arg, + sizeof(write_data))) + return -EFAULT; + retval = cpcap_regacc_write(misc_cpcap, write_data.reg, + write_data.value, write_data.mask); + break; + + default: + retval = -ENOTTY; + break; + } + + return retval; +} + +static int adc_ioctl(unsigned int cmd, unsigned long arg) +{ + int retval = -EINVAL; + struct cpcap_adc_phase phase; + + switch (cmd) { + case CPCAP_IOCTL_ADC_PHASE: + if (copy_from_user((void *) &phase, (void *) arg, + sizeof(phase))) + return -EFAULT; + + cpcap_adc_phase(misc_cpcap, &phase); + retval = 0; + break; + + default: + retval = -ENOTTY; + break; + } + + return retval; +} + +static int accy_ioctl(unsigned int cmd, unsigned long arg) +{ + int retval = -EINVAL; + + switch (cmd) { + case CPCAP_IOCTL_ACCY_WHISPER: + retval = cpcap_accy_whisper(misc_cpcap, arg); + break; + + default: + retval = -ENOTTY; + break; + } + + return retval; +} + +static int ioctl(struct inode *inode, + struct file *file, unsigned int cmd, unsigned long arg) +{ + int retval = -ENOTTY; + unsigned int cmd_num; + + cmd_num = _IOC_NR(cmd); + + if ((cmd_num > CPCAP_IOCTL_NUM_TEST__START) && + (cmd_num < CPCAP_IOCTL_NUM_TEST__END)) { + retval = test_ioctl(cmd, arg); + } + if ((cmd_num > CPCAP_IOCTL_NUM_ADC__START) && + (cmd_num < CPCAP_IOCTL_NUM_ADC__END)) { + retval = adc_ioctl(cmd, arg); + } + if ((cmd_num > CPCAP_IOCTL_NUM_ACCY__START) && + (cmd_num < CPCAP_IOCTL_NUM_ACCY__END)) { + retval = accy_ioctl(cmd, arg); + } + + return retval; +} + +static void cpcap_shutdown(void) +{ + spi_unregister_driver(&cpcap_driver); +} + +subsys_initcall(cpcap_init); +module_exit(cpcap_shutdown); + +MODULE_ALIAS("platform:cpcap"); +MODULE_DESCRIPTION("CPCAP driver"); +MODULE_AUTHOR("Motorola"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/cpcap-irq.c b/drivers/mfd/cpcap-irq.c new file mode 100644 index 000000000000..b0726ca4a8db --- /dev/null +++ b/drivers/mfd/cpcap-irq.c @@ -0,0 +1,507 @@ +/* + * Copyright (C) 2009, Motorola, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define NUM_INT_REGS 5 +#define NUM_INTS_PER_REG 16 + +#define CPCAP_INT1_VALID_BITS 0xFFFB +#define CPCAP_INT2_VALID_BITS 0xFFFF +#define CPCAP_INT3_VALID_BITS 0xFFFF +#define CPCAP_INT4_VALID_BITS 0x03FF +#define CPCAP_INT5_VALID_BITS 0xFFFF + +struct cpcap_event_handler { + void (*func)(enum cpcap_irqs, void *); + void *data; +}; + +struct cpcap_irqdata { + struct mutex lock; + struct work_struct work; + struct workqueue_struct *workqueue; + struct cpcap_device *cpcap; + struct cpcap_event_handler event_handler[CPCAP_IRQ__NUM]; + uint64_t registered; + uint64_t enabled; + struct wake_lock wake_lock; +}; + +#define EVENT_MASK(event) (1 << ((event) % NUM_INTS_PER_REG)) + +enum pwrkey_states { + PWRKEY_RELEASE, /* Power key released state. */ + PWRKEY_PRESS, /* Power key pressed state. */ + PWRKEY_UNKNOWN, /* Unknown power key state. */ +}; + +static irqreturn_t event_isr(int irq, void *data) +{ + struct cpcap_irqdata *irq_data = data; + disable_irq_nosync(irq); + wake_lock(&irq_data->wake_lock); + queue_work(irq_data->workqueue, &irq_data->work); + + return IRQ_HANDLED; +} + +static unsigned short get_int_reg(enum cpcap_irqs event) +{ + unsigned short ret; + + if ((event) >= CPCAP_IRQ_INT5_INDEX) + ret = CPCAP_REG_MI1; + else if ((event) >= CPCAP_IRQ_INT4_INDEX) + ret = CPCAP_REG_INT4; + else if ((event) >= CPCAP_IRQ_INT3_INDEX) + ret = CPCAP_REG_INT3; + else if ((event) >= CPCAP_IRQ_INT2_INDEX) + ret = CPCAP_REG_INT2; + else + ret = CPCAP_REG_INT1; + + return ret; +} + +static unsigned short get_mask_reg(enum cpcap_irqs event) +{ + unsigned short ret; + + if (event >= CPCAP_IRQ_INT5_INDEX) + ret = CPCAP_REG_MIM1; + else if (event >= CPCAP_IRQ_INT4_INDEX) + ret = CPCAP_REG_INTM4; + else if (event >= CPCAP_IRQ_INT3_INDEX) + ret = CPCAP_REG_INTM3; + else if (event >= CPCAP_IRQ_INT2_INDEX) + ret = CPCAP_REG_INTM2; + else + ret = CPCAP_REG_INTM1; + + return ret; +} + +static unsigned short get_sense_reg(enum cpcap_irqs event) +{ + unsigned short ret; + + if (event >= CPCAP_IRQ_INT5_INDEX) + ret = CPCAP_REG_MI2; + else if (event >= CPCAP_IRQ_INT4_INDEX) + ret = CPCAP_REG_INTS4; + else if (event >= CPCAP_IRQ_INT3_INDEX) + ret = CPCAP_REG_INTS3; + else if (event >= CPCAP_IRQ_INT2_INDEX) + ret = CPCAP_REG_INTS2; + else + ret = CPCAP_REG_INTS1; + + return ret; +} + +void cpcap_irq_mask_all(struct cpcap_device *cpcap) +{ + int i; + + static const struct { + unsigned short mask_reg; + unsigned short valid; + } int_reg[NUM_INT_REGS] = { + {CPCAP_REG_INTM1, CPCAP_INT1_VALID_BITS}, + {CPCAP_REG_INTM2, CPCAP_INT2_VALID_BITS}, + {CPCAP_REG_INTM3, CPCAP_INT3_VALID_BITS}, + {CPCAP_REG_INTM4, CPCAP_INT4_VALID_BITS}, + {CPCAP_REG_MIM1, CPCAP_INT5_VALID_BITS} + }; + + for (i = 0; i < NUM_INT_REGS; i++) { + cpcap_regacc_write(cpcap, int_reg[i].mask_reg, + int_reg[i].valid, + int_reg[i].valid); + } +} + +struct pwrkey_data { + struct cpcap_device *cpcap; + enum pwrkey_states state; + struct wake_lock wake_lock; +}; + +static void pwrkey_handler(enum cpcap_irqs irq, void *data) +{ + struct pwrkey_data *pwrkey_data = data; + enum pwrkey_states new_state, last_state = pwrkey_data->state; + struct cpcap_device *cpcap = pwrkey_data->cpcap; + + new_state = (enum pwrkey_states) cpcap_irq_sense(cpcap, irq, 0); + + + if ((new_state < PWRKEY_UNKNOWN) && (new_state != last_state)) { + wake_lock_timeout(&pwrkey_data->wake_lock, 20); + cpcap_broadcast_key_event(cpcap, KEY_END, new_state); + pwrkey_data->state = new_state; + } + cpcap_irq_unmask(cpcap, CPCAP_IRQ_ON); +} + +static int pwrkey_init(struct cpcap_device *cpcap) +{ + struct pwrkey_data *data = kmalloc(sizeof(struct pwrkey_data), + GFP_KERNEL); + int retval; + + if (!data) + return -ENOMEM; + data->cpcap = cpcap; + data->state = PWRKEY_RELEASE; + retval = cpcap_irq_register(cpcap, CPCAP_IRQ_ON, pwrkey_handler, data); + if (retval) + kfree(data); + wake_lock_init(&data->wake_lock, WAKE_LOCK_SUSPEND, "pwrkey"); + return retval; +} + +static void pwrkey_remove(struct cpcap_device *cpcap) +{ + struct pwrkey_data *data; + + cpcap_irq_get_data(cpcap, CPCAP_IRQ_ON, (void **)&data); + if (!data) + return; + cpcap_irq_free(cpcap, CPCAP_IRQ_ON); + wake_lock_destroy(&data->wake_lock); + kfree(data); +} + +static int int_read_and_clear(struct cpcap_device *cpcap, + unsigned short status_reg, + unsigned short mask_reg, + unsigned short valid_mask, + unsigned short *en) +{ + unsigned short ireg_val, mreg_val; + int ret; + ret = cpcap_regacc_read(cpcap, status_reg, &ireg_val); + if (ret) + return ret; + ret = cpcap_regacc_read(cpcap, mask_reg, &mreg_val); + if (ret) + return ret; + *en |= ireg_val & ~mreg_val; + *en &= valid_mask; + ret = cpcap_regacc_write(cpcap, mask_reg, *en, *en); + if (ret) + return ret; + ret = cpcap_regacc_write(cpcap, status_reg, *en, *en); + if (ret) + return ret; + return 0; +} + + +static void irq_work_func(struct work_struct *work) +{ + int retval = 0; + unsigned short en_ints[NUM_INT_REGS]; + int i; + struct cpcap_irqdata *data; + struct cpcap_device *cpcap; + struct spi_device *spi; + + static const struct { + unsigned short status_reg; + unsigned short mask_reg; + unsigned short valid; + } int_reg[NUM_INT_REGS] = { + {CPCAP_REG_INT1, CPCAP_REG_INTM1, CPCAP_INT1_VALID_BITS}, + {CPCAP_REG_INT2, CPCAP_REG_INTM2, CPCAP_INT2_VALID_BITS}, + {CPCAP_REG_INT3, CPCAP_REG_INTM3, CPCAP_INT3_VALID_BITS}, + {CPCAP_REG_INT4, CPCAP_REG_INTM4, CPCAP_INT4_VALID_BITS}, + {CPCAP_REG_MI1, CPCAP_REG_MIM1, CPCAP_INT5_VALID_BITS} + }; + + for (i = 0; i < NUM_INT_REGS; ++i) + en_ints[i] = 0; + + data = container_of(work, struct cpcap_irqdata, work); + cpcap = data->cpcap; + spi = cpcap->spi; + + for (i = 0; i < NUM_INT_REGS; ++i) { + retval = int_read_and_clear(cpcap, + int_reg[i].status_reg, + int_reg[i].mask_reg, + int_reg[i].valid, + &en_ints[i]); + if (retval < 0) { + dev_err(&spi->dev, "Error reading interrupts\n"); + break; + } + } + enable_irq(spi->irq); + + /* lock protects event handlers and data */ + mutex_lock(&data->lock); + for (i = 0; i < NUM_INT_REGS; ++i) { + unsigned char index; + + while (en_ints[i] > 0) { + struct cpcap_event_handler *event_handler; + + /* find the first set bit */ + index = (unsigned char)(ffs(en_ints[i]) - 1); + if (index >= CPCAP_IRQ__NUM) + goto error; + /* clear the bit */ + en_ints[i] &= ~(1 << index); + /* find the event that occurred */ + index += CPCAP_IRQ__START + (i * NUM_INTS_PER_REG); + event_handler = &data->event_handler[index]; + + if (event_handler->func) + event_handler->func(index, event_handler->data); + } + } +error: + mutex_unlock(&data->lock); + wake_unlock(&data->wake_lock); +} + +int cpcap_irq_init(struct cpcap_device *cpcap) +{ + int retval; + struct spi_device *spi = cpcap->spi; + struct cpcap_irqdata *data; + struct dentry *debug_dir; + + data = kzalloc(sizeof(struct cpcap_irqdata), GFP_KERNEL); + if (!data) + return -ENOMEM; + + cpcap_irq_mask_all(cpcap); + + data->workqueue = create_workqueue("cpcap_irq"); + INIT_WORK(&data->work, irq_work_func); + mutex_init(&data->lock); + wake_lock_init(&data->wake_lock, WAKE_LOCK_SUSPEND, "cpcap-irq"); + data->cpcap = cpcap; + + retval = request_irq(spi->irq, event_isr, IRQF_DISABLED | + IRQF_TRIGGER_HIGH, "cpcap-irq", data); + if (retval) { + printk(KERN_ERR "cpcap_irq: Failed requesting irq.\n"); + goto error; + } + + enable_irq_wake(spi->irq); + + cpcap->irqdata = data; + retval = pwrkey_init(cpcap); + if (retval) { + printk(KERN_ERR "cpcap_irq: Failed initializing pwrkey.\n"); + goto error; + } + + debug_dir = debugfs_create_dir("cpcap-irq", NULL); + debugfs_create_u64("registered", S_IRUGO, debug_dir, + &data->registered); + debugfs_create_u64("enabled", S_IRUGO, debug_dir, + &data->enabled); + + return 0; + +error: + free_irq(spi->irq, data); + kfree(data); + printk(KERN_ERR "cpcap_irq: Error registering cpcap irq.\n"); + return retval; +} + +void cpcap_irq_shutdown(struct cpcap_device *cpcap) +{ + struct spi_device *spi = cpcap->spi; + struct cpcap_irqdata *data = cpcap->irqdata; + + pwrkey_remove(cpcap); + cancel_work_sync(&data->work); + destroy_workqueue(data->workqueue); + free_irq(spi->irq, data); + kfree(data); +} + +int cpcap_irq_register(struct cpcap_device *cpcap, + enum cpcap_irqs irq, + void (*cb_func) (enum cpcap_irqs, void *), + void *data) +{ + struct cpcap_irqdata *irqdata = cpcap->irqdata; + int retval = 0; + + if ((irq >= CPCAP_IRQ__NUM) || (!cb_func)) + return -EINVAL; + + mutex_lock(&irqdata->lock); + + if (irqdata->event_handler[irq].func == NULL) { + irqdata->registered |= 1 << irq; + cpcap_irq_unmask(cpcap, irq); + irqdata->event_handler[irq].func = cb_func; + irqdata->event_handler[irq].data = data; + } else + retval = -EPERM; + + mutex_unlock(&irqdata->lock); + + return retval; +} +EXPORT_SYMBOL_GPL(cpcap_irq_register); + +int cpcap_irq_free(struct cpcap_device *cpcap, enum cpcap_irqs irq) +{ + struct cpcap_irqdata *data = cpcap->irqdata; + int retval; + + if (irq >= CPCAP_IRQ__NUM) + return -EINVAL; + + mutex_lock(&data->lock); + retval = cpcap_irq_mask(cpcap, irq); + data->event_handler[irq].func = NULL; + data->event_handler[irq].data = NULL; + data->registered &= ~(1 << irq); + mutex_unlock(&data->lock); + + return retval; +} +EXPORT_SYMBOL_GPL(cpcap_irq_free); + +int cpcap_irq_get_data(struct cpcap_device *cpcap, + enum cpcap_irqs irq, + void **data) +{ + struct cpcap_irqdata *irqdata = cpcap->irqdata; + + if (irq >= CPCAP_IRQ__NUM) + return -EINVAL; + + mutex_lock(&irqdata->lock); + *data = irqdata->event_handler[irq].data; + mutex_unlock(&irqdata->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(cpcap_irq_get_data); + +int cpcap_irq_clear(struct cpcap_device *cpcap, + enum cpcap_irqs irq) +{ + int retval = -EINVAL; + + if ((irq < CPCAP_IRQ__NUM) && (irq != CPCAP_IRQ_SECMAC)) { + retval = cpcap_regacc_write(cpcap, + get_int_reg(irq), + EVENT_MASK(irq), + EVENT_MASK(irq)); + } + + return retval; +} +EXPORT_SYMBOL_GPL(cpcap_irq_clear); + +int cpcap_irq_mask(struct cpcap_device *cpcap, + enum cpcap_irqs irq) +{ + struct cpcap_irqdata *data = cpcap->irqdata; + int retval = -EINVAL; + + if ((irq < CPCAP_IRQ__NUM) && (irq != CPCAP_IRQ_SECMAC)) { + data->enabled &= ~(1 << irq); + retval = cpcap_regacc_write(cpcap, + get_mask_reg(irq), + EVENT_MASK(irq), + EVENT_MASK(irq)); + } + + return retval; +} +EXPORT_SYMBOL_GPL(cpcap_irq_mask); + +int cpcap_irq_unmask(struct cpcap_device *cpcap, + enum cpcap_irqs irq) +{ + struct cpcap_irqdata *data = cpcap->irqdata; + int retval = -EINVAL; + + if ((irq < CPCAP_IRQ__NUM) && (irq != CPCAP_IRQ_SECMAC)) { + data->enabled |= 1 << irq; + retval = cpcap_regacc_write(cpcap, + get_mask_reg(irq), + 0, + EVENT_MASK(irq)); + } + + return retval; +} +EXPORT_SYMBOL_GPL(cpcap_irq_unmask); + +int cpcap_irq_mask_get(struct cpcap_device *cpcap, + enum cpcap_irqs irq) +{ + struct cpcap_irqdata *data = cpcap->irqdata; + int retval = -EINVAL; + + if ((irq < CPCAP_IRQ__NUM) && (irq != CPCAP_IRQ_SECMAC)) + return (data->enabled & (1 << irq)) ? 0 : 1; + + return retval; +} +EXPORT_SYMBOL_GPL(cpcap_irq_mask_get); + +int cpcap_irq_sense(struct cpcap_device *cpcap, + enum cpcap_irqs irq, + unsigned char clear) +{ + unsigned short val; + int retval; + + if (irq >= CPCAP_IRQ__NUM) + return -EINVAL; + + retval = cpcap_regacc_read(cpcap, get_sense_reg(irq), &val); + if (retval) + return retval; + + if (clear) + retval = cpcap_irq_clear(cpcap, irq); + if (retval) + return retval; + + return ((val & EVENT_MASK(irq)) != 0) ? 1 : 0; +} +EXPORT_SYMBOL_GPL(cpcap_irq_sense); diff --git a/drivers/mfd/cpcap-key.c b/drivers/mfd/cpcap-key.c new file mode 100644 index 000000000000..633e6ded2255 --- /dev/null +++ b/drivers/mfd/cpcap-key.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2009 Motorola, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include + + + +struct cpcap_key_data { + struct input_dev *input_dev; + struct cpcap_device *cpcap; +}; + +static int __init cpcap_key_probe(struct platform_device *pdev) +{ + int err; + struct cpcap_key_data *key; + + if (pdev->dev.platform_data == NULL) { + dev_err(&pdev->dev, "no platform_data\n"); + return -EINVAL; + } + + key = kzalloc(sizeof(*key), GFP_KERNEL); + if (!key) + return -ENOMEM; + + key->cpcap = pdev->dev.platform_data; + + key->input_dev = input_allocate_device(); + if (key->input_dev == NULL) { + dev_err(&pdev->dev, "can't allocate input device\n"); + err = -ENOMEM; + goto err0; + } + + set_bit(EV_KEY, key->input_dev->evbit); + set_bit(KEY_MEDIA, key->input_dev->keybit); + set_bit(KEY_END, key->input_dev->keybit); + + key->input_dev->name = "cpcap-key"; + + err = input_register_device(key->input_dev); + if (err < 0) { + dev_err(&pdev->dev, "could not register input device.\n"); + goto err1; + } + + platform_set_drvdata(pdev, key); + cpcap_set_keydata(key->cpcap, key); + + dev_info(&pdev->dev, "CPCAP key device probed\n"); + + return 0; + +err1: + input_free_device(key->input_dev); +err0: + kfree(key); + return err; +} + +static int __exit cpcap_key_remove(struct platform_device *pdev) +{ + struct cpcap_key_data *key = platform_get_drvdata(pdev); + + input_unregister_device(key->input_dev); + input_free_device(key->input_dev); + kfree(key); + + return 0; +} + +void cpcap_broadcast_key_event(struct cpcap_device *cpcap, + unsigned int code, int value) +{ + struct cpcap_key_data *key = cpcap_get_keydata(cpcap); + + if (key && key->input_dev) + input_report_key(key->input_dev, code, value); +} +EXPORT_SYMBOL(cpcap_broadcast_key_event); + +static struct platform_driver cpcap_key_driver = { + .probe = cpcap_key_probe, + .remove = __exit_p(cpcap_key_remove), + .driver = { + .name = "cpcap_key", + .owner = THIS_MODULE, + }, +}; + +static int __init cpcap_key_init(void) +{ + return platform_driver_register(&cpcap_key_driver); +} +module_init(cpcap_key_init); + +static void __exit cpcap_key_exit(void) +{ + platform_driver_unregister(&cpcap_key_driver); +} +module_exit(cpcap_key_exit); + +MODULE_ALIAS("platform:cpcap_key"); +MODULE_DESCRIPTION("CPCAP KEY driver"); +MODULE_AUTHOR("Motorola"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/cpcap-regacc.c b/drivers/mfd/cpcap-regacc.c new file mode 100644 index 000000000000..2b8de54a8dab --- /dev/null +++ b/drivers/mfd/cpcap-regacc.c @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2007-2009 Motorola, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include + +#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; +} diff --git a/drivers/mfd/cpcap-uc.c b/drivers/mfd/cpcap-uc.c new file mode 100644 index 000000000000..d014f04552fa --- /dev/null +++ b/drivers/mfd/cpcap-uc.c @@ -0,0 +1,880 @@ +/* + * Copyright (C) 2008-2010 Motorola, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +#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"); diff --git a/drivers/mfd/cpcap-whisper.c b/drivers/mfd/cpcap-whisper.c new file mode 100644 index 000000000000..68bd873d4c17 --- /dev/null +++ b/drivers/mfd/cpcap-whisper.c @@ -0,0 +1,551 @@ +/* + * Copyright (C) 2010 Motorola, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + + +#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"); diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 172951bf23a4..d520bc0fd23a 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -235,5 +235,11 @@ config REGULATOR_TPS6586X help This driver supports TPS6586X voltage regulator chips. +config REGULATOR_CPCAP + tristate "CPCAP regulator driver" + depends on MFD_CPCAP + help + Say Y here to support the voltage regulators on CPCAP + endif diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 8285fd832e16..085384e7e27e 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o +obj-$(CONFIG_REGULATOR_CPCAP) += cpcap-regulator.o obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o diff --git a/drivers/regulator/cpcap-regulator.c b/drivers/regulator/cpcap-regulator.c new file mode 100644 index 000000000000..d3049ad26f93 --- /dev/null +++ b/drivers/regulator/cpcap-regulator.c @@ -0,0 +1,574 @@ +/* + * Copyright (C) 2009 Motorola, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#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"); diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index a86401ea8e71..9c9228fbab61 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -126,6 +126,13 @@ config RTC_INTF_ALARM_DEV help Exports the alarm interface to user-space. +config RTC_INTF_CPCAP_SECCLKD + bool "Secure Clock Daemon support" + depends on RTC_DRV_CPCAP + default n + help + CPCAP RTC driver support for secure clock daemon by maintaining a + counter to keep track of changes in RTC time. config RTC_DRV_TEST tristate "Test driver/device" @@ -679,6 +686,13 @@ config RTC_DRV_NUC900 If you say yes here you get support for the RTC subsystem of the NUC910/NUC920 used in embedded systems. +config RTC_DRV_CPCAP + depends on MFD_CPCAP + tristate "CPCAP RTC" + help + If you say yes here you get support for the RTC subsystem of the + CPCAP used in embedded systems. + comment "on-CPU RTC drivers" config RTC_DRV_DAVINCI diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 5520733748d2..1849ff0b5963 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -101,3 +101,4 @@ obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o +obj-$(CONFIG_RTC_DRV_CPCAP) += rtc-cpcap.o diff --git a/drivers/rtc/rtc-cpcap.c b/drivers/rtc/rtc-cpcap.c new file mode 100644 index 000000000000..7e9c7051fce1 --- /dev/null +++ b/drivers/rtc/rtc-cpcap.c @@ -0,0 +1,483 @@ +/* + * Copyright (C) 2009 Motorola, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef RTC_INTF_CPCAP_SECCLKD +#include + +#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"); diff --git a/firmware/Makefile b/firmware/Makefile index 9c2d19452d0b..1d613fd60431 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -138,6 +138,7 @@ fw-shipped-$(CONFIG_USB_SERIAL_XIRCOM) += keyspan_pda/xircom_pgs.fw fw-shipped-$(CONFIG_USB_VICAM) += vicam/firmware.fw fw-shipped-$(CONFIG_VIDEO_CPIA2) += cpia2/stv0672_vp4.bin fw-shipped-$(CONFIG_YAM) += yam/1200.bin yam/9600.bin +fw-shipped-$(CONFIG_MFD_CPCAP) += cpcap/firmware_0_2x.fw cpcap/firmware_1_2x.fw fw-shipped-all := $(fw-shipped-y) $(fw-shipped-m) $(fw-shipped-) diff --git a/firmware/cpcap/firmware_0_2x.HEX b/firmware/cpcap/firmware_0_2x.HEX new file mode 100644 index 000000000000..70645a9ed56f --- /dev/null +++ b/firmware/cpcap/firmware_0_2x.HEX @@ -0,0 +1,67 @@ +:20001800017C0000000000000000000000000000000000000000000000000000000000004B +:200038000000000000000000000000000000000001EC0000000000000000000000000000BB +:20005800000000000000000000000000000000000000000000000000000000000000000088 +:20007800000000000000000000000000000000000000000000000000000000000000000068 +:20009800000000000000000000000000000000000000000000000000000000000000000048 +:0800B800000000000000000040 +:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF +:20014000FFFFFFFF00000000000000000000000000000000000000000000000000000000A3 +:2001600000000000000000000000000000000000000000000000000000000000030F00006D +:2001800000000000000000000000000000000000000000000000000000000000000000005F +:2001A00000000000000000000000000000000000000000000000000000000000000000003F +:2001C00000000000000000000000000000000000000000000000000000000000000000001F +:2001E000000000000000000000000000055805F304000100000000000000000000000000A5 +:080200000000000000000000F6 +:1002080000000000000000000000000000000200E4 +:200226009EA00C955BBFE2AB0997CD083F5BBFE2AB0597CD083F5BBFE2AB0197CD083FC60E +:20024600450DCE450C6B0C72EF0B4F6B0A6B095BBFE2AB0997A610CD07F33FEB3FEA5BBFDB +:20026600E2AB0997CD0748C6450BCE450ACD07675BBFE2AB0997CD07CA5BBFE2AB0997CDC8 +:20028600083F350200E2AE08CD081C5BBFE2AB0597CD07487B052A095BBFE2AB0597CD0728 +:2002A6009D350100E2AE28A610CD07F33FEB3FEA5BBFE2AB0197CD0748350100E2AE28CDC7 +:2002C600083FA610CD07393FE93FE85BBFE2AB0197CD07CA5BBFE2AB0597CD083F5BBFE28F +:2002E600AB0197CD077025187B0CC7020B7B0BC7020A7B0AC702097B09C70208A60120010C +:200306004F6B0C9EAB0B958481A640C7831B725F831AC78327725F832648C78329725F8370 +:2003260028A620C7832B725F832AC7832D725F832C4FC70218C70219C7021AC7021BC7023D +:200346001CC7021DC7021EC7021FC70220C70221C70222C70223C70224C70225356901237F +:20036600725F012235590125725F012435510127725F01263520012B35A1012A350701294B +:2003860035000128720A410303CD04A3720C410322725C021B2604725C021AC6021BC0023A +:2003A60011C6021AC20210250ACD04E94FC7021AC7021B720E410320725C021D2604725CA8 +:2003C600021CC6021DCA021C2715C64101CE4100AA80C74101CF41004FC7021CC7021D720A +:2003E60000410224725C021F2604725C021EC6021FA00AC6021EA2002515CE4101C641001F +:20040600AA01CF4101C741004FC7021EC7021F7202410224725C02212604725C0220C60246 +:2004260021A014C60220A2002515CE4101C64100AA02CF4101C741004FC70220C7022172AD +:2004460004410224725C02232604725C0222C60223A03CC60222A2002515CE4101C6410078 +:20046600AA04CF4101C741004FC70222C702237206410217C6020F2712725F020FCE4101B5 +:20048600C64100AA08CF4101C74100720A410203CD0702A6CCAE0CCDF009CC038A88888809 +:2004A60088C640106B01C640116B02C640126B03C640136B047B01A50427067B03A50826F7 +:2004C60009350102128484848481C6021227F6725F0212C64101CE4100AA20C74101CF415C +:2004E6000020E2C602132632C64807CE4806AA01C74807CF4806C64809CE4808AA01C748C3 +:2005060009CF4808C6480BCE480AAA01C7480BCF480A35010213A6032030C64807CE480619 +:20052600A4FEC74807CF4806C64809CE4808A4FEC74809CF4808C6480BCE480AA4FEC748E8 +:200546000BCF480A725F0213A612C70211725F021081A6CCAE0CCDF009725C02152604721A +:200566005C0214C60215A058C60214A202253DCE4601C64600AA90CF4601C74600CE4009B7 +:20058600C64008A4FECF4009C74008C64603CE4602AA7341AAD1CF4603C74602A6CCAE0C2D +:2005A600CDF0004FC70214C70215201CC60215A1572615CE0214A302260ECE4603C6460240 +:2005C600AA40CF4603C74602CD02264D27847207410203CC05583501020FCC0558000000BF +:2005E6000000000000000000000000000088899EA010955BBFE2AB0197350500E590AEE382 +:20060600A610CD085ECE4009C64008AA01CF4009C74008CE4001C64000AA01CF4001C7401D +:2006260000CE4603C64602A4BFCF4603C74602CE4605C64604A40372EF026B01CE4607C685 +:200646004606A40372EF046B03CE4609C64608A40372EF066B05CE460BC6460AA40372EFE2 +:20066600086B07CE460DC6460CA40372EF0A6B09CE460FC6460EA40372EF0C6B0BCE46114F +:20068600C64610A40372EF0E6B0DCE4613C64612A40372EF106B0F7B02C7020E7B01C7023A +:2006A6000DC60127C0020EC60126C2020D2504A6022014C60125C0020EC60124C2020D2509 +:2006C6000BC6020C26064CC7020C201BC60123C0020EC60122C2020D240DC6020C27087293 +:2006E6005F020C3501020FC6020C27043501020F7B12AE01CDF2B69EAB1295818888C640C2 +:2007060011C64010A402974F6B0272EF01C1021726069FC1021627187B02C702177B01C7F4 +:200726000216CE4101C64100AA20CF4101C741008484814D270B34E836E936EA36EB4A26AD +:20074600F581BFE388B6E892C7E2AE01B6E992D7E25CB6EA92D7E25CB6EB92D7E284BEE3CD +:2007660081B7EBBFEA3FE93FE8813FE3B6E892D1E226233CE3B6E992D1E226123CE3B6EA8F +:2007860092D1E226093CE3B6EB92D1E227082404A6FF2002A60181BFE3723300E2AE01724A +:2007A6006300E25C726300E25C726000E226125A726C00E2260B5A726C00E22604723C0056 +:2007C600E2BEE381CD07D0CC074ABFE3B6E892CAE2B7E8B6E9AE0192DAE2B7E9B6EA5C9267 +:2007E600DAE2B7EAB6EB5C92DAE2B7EB81CD083F2003CD080A4D270B38EB39EA39E939E80A +:200806004A26F58188F6B7E8E601B7E9E602B7EAE603B7EB8481BFE3AE03B6EB92D0E2B740 +:20082600EBB6EA5A92D2E2B7EAB6E95A92D2E2B7E9B6E892C2E2B7E881BFE38892C6E2B7F8 +:20084600E8AE0192D6E2B7E95C92D6E2B7EA5C92D6E2B7EB84BEE381BFE390BFE6975A9282 +:0B086600D6E592D7E25D26F6BEE381E6 +:00000001FF \ No newline at end of file diff --git a/firmware/cpcap/firmware_1_2x.H16 b/firmware/cpcap/firmware_1_2x.H16 new file mode 100644 index 000000000000..c6c276014391 --- /dev/null +++ b/firmware/cpcap/firmware_1_2x.H16 @@ -0,0 +1,3 @@ +:03E0913000917A918C2400000401000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012005030001E410F82A1000052A1000041301200503000B0410F82A1000052A10000413040B20040531A40B20040532640B20080532840B20020532A40B20020532C43829160438291624382916443829166438291684382916A4382916C40B2006990E640B2005990E840B2005190EA40B2A12090EC40B2000790EE43829170438291724382917443C2917843C291794382916E43C2917643D29177D0B201000110C0B201000108C0B2040000C0C0B2040000C640B20C80018440B200100180435C52A100004130120A435AB0B201009FA428041200503001623C0412005030000C4C4A4A4C413A52A100004130B0B2002011022C13B0B204000100280DB0B208000102280993C29176240843C29176D0B2002001C83C0243D29176B0B2004011022C22539291629292916E9162281C93C29177200AD3921806D3921808D392180A403F0003435E3C09C3921806C3921808C392180A403F0012434E4EC291774F82916E43829162B0B2008011022C06539291642405D0B2008001C843829164B0B2010011022C095392916690B2000A91662805D0B2010001C843829166B0B2020011022C095392916890B2001491682805D0B2020001C843829168B0B2040011022C095392916A90B2003C916A2805D0B2040001C84382916AB0B2100011022C295392916C90B20258916C280940B29000030040B2D17303024382916C3C0790B20257916C2003D0B240000302120050300102934C240243D29179B0B2080011022C0693C291792405D0B2080001C843C29179435C52A10000413080310020410C403E9140403D001012005030010CC0B240000302403E03FF4E0CF21C03044C810000438100024E0CF21C03064C810004438100064E0CF21C03084C8100084381000A4E0CF21C030A4C81000C4381000E4E0CF21C030C4C810010438100124E0CF21C030E4C810014438100164E0CF21C03104C8100184381001AF21E03124E81001C4381001E41A29174421F91749F8290EA280343E291783C16425E91789F8290E82805934E200343D291783C0C9F8290E62C07934E240543C2917843D291793C04934E240243D291795031002052A100004130421F150C421E150A4E0C4F0D821C9170721D9172930D3404E33CE33D531C630D921D90EE280C2003921C90EC28084E8291704F829172435C52A100004130434C52A100004130120C4EBC0000532C831D23FB413C52A1000041304C0F5D0F3C0343CC0000531C9F0C23FB52A100004130403C9160403D001A12005030FFDC00008A +:0002901800913095 +:0000000001FF diff --git a/include/linux/spi/cpcap-regbits.h b/include/linux/spi/cpcap-regbits.h new file mode 100644 index 000000000000..b5bda42aaff2 --- /dev/null +++ b/include/linux/spi/cpcap-regbits.h @@ -0,0 +1,952 @@ +#ifndef __CPCAP_REGBITS_H__ +#define __CPCAP_REGBITS_H__ + +/* + * Copyright (C) 2007-2009 Motorola, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +/* + * Register 0 - CPCAP_REG_INT_0 bits + */ +#define CPCAP_BIT_ID_GROUND_I 0x00008000 +#define CPCAP_BIT_ID_FLOAT_I 0x00004000 +#define CPCAP_BIT_CHRG_DET_I 0x00002000 +#define CPCAP_BIT_RVRS_CHRG_I 0x00001000 +#define CPCAP_BIT_VBUSOV_I 0x00000800 +#define CPCAP_BIT_MB2_I 0x00000400 +#define CPCAP_BIT_HS_I 0x00000200 +#define CPCAP_BIT_ADCDONE_I 0x00000100 +#define CPCAP_BIT_TS_I 0x00000080 +#define CPCAP_BIT_EOL_I 0x00000040 +#define CPCAP_BIT_LOWBPH_I 0x00000020 +#define CPCAP_BIT_SEC2PRI_I 0x00000010 +#define CPCAP_BIT_LOWBPL_I 0x00000008 +#define CPCAP_BIT_UNUSED_0_2_I 0x00000004 +#define CPCAP_BIT_PRIMAC_I 0x00000002 +#define CPCAP_BIT_HSCLK_I 0x00000001 + +/* + * Register 1 - CPCAP_REG_INT_1 bits + */ +#define CPCAP_BIT_EXTMEMHD_I 0x00008000 +#define CPCAP_BIT_UART_ECHO_OVERRUN_I 0x00004000 +#define CPCAP_BIT_CHRG_SE1B_I 0x00002000 +#define CPCAP_BIT_SE0CONN_I 0x00001000 +#define CPCAP_BIT_PTT_I 0x00000800 +#define CPCAP_BIT_1HZ_I 0x00000400 +#define CPCAP_BIT_CLK_I 0x00000200 +#define CPCAP_BIT_ON2_I 0x00000100 +#define CPCAP_BIT_ON_I 0x00000080 +#define CPCAP_BIT_RVRS_MODE_I 0x00000040 +#define CPCAP_BIT_CHRGCURR2_I 0x00000020 +#define CPCAP_BIT_CHRGCURR1_I 0x00000010 +#define CPCAP_BIT_VBUSVLD_I 0x00000008 +#define CPCAP_BIT_SESSVLD_I 0x00000004 +#define CPCAP_BIT_SESSEND_I 0x00000002 +#define CPCAP_BIT_SE1_I 0x00000001 + +/* + * Register 2 CPCAP_REG_INT_2 - bits + */ +#define CPCAP_BIT_USBDPLLCLK_I 0x00008000 +#define CPCAP_BIT_PWRGOOD_I 0x00004000 +#define CPCAP_BIT_UCRESET_I 0x00002000 +#define CPCAP_BIT_ONEWIRE3_I 0x00001000 +#define CPCAP_BIT_ONEWIRE2_I 0x00000800 +#define CPCAP_BIT_ONEWIRE1_I 0x00000400 +#define CPCAP_BIT_OPT_SEL_STATE_I 0x00000200 +#define CPCAP_BIT_OPT_SEL_DTCH_I 0x00000100 +#define CPCAP_BIT_TODA_I 0x00000080 +#define CPCAP_BIT_OFLOWSW_I 0x00000040 +#define CPCAP_BIT_PC_I 0x00000020 +#define CPCAP_BIT_DIETEMPH_I 0x00000010 +#define CPCAP_BIT_DIEPWRDWN_I 0x00000008 +#define CPCAP_BIT_SOFTRST_I 0x00000004 +#define CPCAP_BIT_SYSRSTRT_I 0x00000002 +#define CPCAP_BIT_WARM_I 0x00000001 + +/* + * Register 3 - CPCAP_REG_INT_3 bits + */ +#define CPCAP_BIT_UNUSED_3_15_I 0x00008000 +#define CPCAP_BIT_UNUSED_3_14_I 0x00004000 +#define CPCAP_BIT_SPARE_3_13_I 0x00002000 +#define CPCAP_BIT_SPARE_3_12_I 0x00001000 +#define CPCAP_BIT_SPARE_3_11_I 0x00000800 +#define CPCAP_BIT_SPARE_3_10_I 0x00000400 +#define CPCAP_BIT_CC_CAL_I 0x00000200 +#define CPCAP_BIT_SECHALT_I 0x00000100 +#define CPCAP_BIT_PRIHALT_I 0x00000080 +#define CPCAP_BIT_BATTDETB_I 0x00000040 +#define CPCAP_BIT_SB_MAX_RETX_ERR_I 0x00000020 +#define CPCAP_BIT_GCAI_CURR2_I 0x00000010 +#define CPCAP_BIT_GCAI_CURR1_I 0x00000008 +#define CPCAP_BIT_UCBUSY_I 0x00000004 +#define CPCAP_BIT_DM_I 0x00000002 +#define CPCAP_BIT_DP_I 0x00000001 + +/* + * Register 4 - CPCAP_REG_INTM1 bits + */ +#define CPCAP_BIT_ID_GROUND_M 0x00008000 +#define CPCAP_BIT_ID_FLOAT_M 0x00004000 +#define CPCAP_BIT_CHRG_DET_M 0x00002000 +#define CPCAP_BIT_RVRS_CHRG_M 0x00001000 +#define CPCAP_BIT_VBUSOV_M 0x00000800 +#define CPCAP_BIT_MB2_M 0x00000400 +#define CPCAP_BIT_HS_M 0x00000200 +#define CPCAP_BIT_ADCDONE_M 0x00000100 +#define CPCAP_BIT_TS_M 0x00000080 +#define CPCAP_BIT_EOL_M 0x00000040 +#define CPCAP_BIT_LOWBPH_M 0x00000020 +#define CPCAP_BIT_SEC2PRI_M 0x00000010 +#define CPCAP_BIT_LOWBPL_M 0x00000008 +#define CPCAP_BIT_UNUSED_4_2_M 0x00000004 +#define CPCAP_BIT_PRIMAC_M 0x00000002 +#define CPCAP_BIT_HSCLK_M 0x00000001 + +/* + * Register 5 - CPCAP_REG_INTM2 bits + */ +#define CPCAP_BIT_EXTMEMHD_M 0x00008000 +#define CPCAP_BIT_UART_ECHO_OVERRUN_M 0x00004000 +#define CPCAP_BIT_CHRG_SE1B_M 0x00002000 +#define CPCAP_BIT_SE0CONN_M 0x00001000 +#define CPCAP_BIT_PTT_M 0x00000800 +#define CPCAP_BIT_1HZ_M 0x00000400 +#define CPCAP_BIT_CLK_M 0x00000200 +#define CPCAP_BIT_ON2_M 0x00000100 +#define CPCAP_BIT_ON_M 0x00000080 +#define CPCAP_BIT_RVRS_MODE_M 0x00000040 +#define CPCAP_BIT_CHRGCURR2_M 0x00000020 +#define CPCAP_BIT_CHRGCURR1_M 0x00000010 +#define CPCAP_BIT_VBUSVLD_M 0x00000008 +#define CPCAP_BIT_SESSVLD_M 0x00000004 +#define CPCAP_BIT_SESSEND_M 0x00000002 +#define CPCAP_BIT_SE1_M 0x00000001 + +/* + * Register 6 - CPCAP_REG_INTM3 bits + */ +#define CPCAP_BIT_USBDPLLCLK_M 0x00008000 +#define CPCAP_BIT_PWRGOOD_M 0x00004000 +#define CPCAP_BIT_UCRESET_M 0x00002000 +#define CPCAP_BIT_ONEWIRE3_M 0x00001000 +#define CPCAP_BIT_ONEWIRE2_M 0x00000800 +#define CPCAP_BIT_ONEWIRE1_M 0x00000400 +#define CPCAP_BIT_OPT_SEL_STATE_M 0x00000200 +#define CPCAP_BIT_OPT_SEL_DTCH_M 0x00000100 +#define CPCAP_BIT_TODA_M 0x00000080 +#define CPCAP_BIT_OFLOWSW_M 0x00000040 +#define CPCAP_BIT_PC_M 0x00000020 +#define CPCAP_BIT_DIETEMPH_M 0x00000010 +#define CPCAP_BIT_DIEPWRDWN_M 0x00000008 +#define CPCAP_BIT_SOFTRST_M 0x00000004 +#define CPCAP_BIT_SYSRSTRT_M 0x00000002 +#define CPCAP_BIT_WARM_M 0x00000001 + +/* + * Register 7 - CPCAP_REG_INTM4 bits + */ +#define CPCAP_BIT_UNUSED_7_15_M 0x00008000 +#define CPCAP_BIT_UNUSED_7_14_M 0x00004000 +#define CPCAP_BIT_SPARE_7_13_M 0x00002000 +#define CPCAP_BIT_SPARE_7_12_M 0x00001000 +#define CPCAP_BIT_SPARE_7_11_M 0x00000800 +#define CPCAP_BIT_SPARE_7_10_M 0x00000400 +#define CPCAP_BIT_CC_CAL_M 0x00000200 +#define CPCAP_BIT_SECHALT_M 0x00000100 +#define CPCAP_BIT_PRIHALT_M 0x00000080 +#define CPCAP_BIT_BATTDETB_M 0x00000040 +#define CPCAP_BIT_SB_MAX_RETX_ERR_M 0x00000020 +#define CPCAP_BIT_GCAI_CURR2_M 0x00000010 +#define CPCAP_BIT_GCAI_CURR1_M 0x00000008 +#define CPCAP_BIT_UCBUSY_M 0x00000004 +#define CPCAP_BIT_DM_M 0x00000002 +#define CPCAP_BIT_DP_M 0x00000001 + +/* + * Register 8 - CPCAP_REG_INTS1 bits + */ +#define CPCAP_BIT_ID_GROUND_S 0x00008000 +#define CPCAP_BIT_ID_FLOAT_S 0x00004000 +#define CPCAP_BIT_CHRG_DET_S 0x00002000 +#define CPCAP_BIT_RVRS_CHRG_S 0x00001000 +#define CPCAP_BIT_VBUSOV_S 0x00000800 +#define CPCAP_BIT_MB2_S 0x00000400 +#define CPCAP_BIT_HS_S 0x00000200 +#define CPCAP_BIT_ADCDONE_S 0x00000100 +#define CPCAP_BIT_TS_S 0x00000080 +#define CPCAP_BIT_EOL_S 0x00000040 +#define CPCAP_BIT_LOWBPH_S 0x00000020 +#define CPCAP_BIT_SEC2PRI_S 0x00000010 +#define CPCAP_BIT_LOWBPL_S 0x00000008 +#define CPCAP_BIT_UNUSED_8_2_S 0x00000004 +#define CPCAP_BIT_PRIMAC_S 0x00000002 +#define CPCAP_BIT_HSCLK_S 0x00000001 + +/* + * Register 9 - CPCAP_REG_INTS2 bits + */ +#define CPCAP_BIT_EXTMEMHD_S 0x00008000 +#define CPCAP_BIT_UART_ECHO_OVERRUN_S 0x00004000 +#define CPCAP_BIT_CHRG_SE1B_S 0x00002000 +#define CPCAP_BIT_SE0CONN_S 0x00001000 +#define CPCAP_BIT_PTT_S 0x00000800 +#define CPCAP_BIT_1HZ_S 0x00000400 +#define CPCAP_BIT_CLK_S 0x00000200 +#define CPCAP_BIT_ON2_S 0x00000100 +#define CPCAP_BIT_ON_S 0x00000080 +#define CPCAP_BIT_RVRS_MODE_S 0x00000040 +#define CPCAP_BIT_CHRGCURR2_S 0x00000020 +#define CPCAP_BIT_CHRGCURR1_S 0x00000010 +#define CPCAP_BIT_VBUSVLD_S 0x00000008 +#define CPCAP_BIT_SESSVLD_S 0x00000004 +#define CPCAP_BIT_SESSEND_S 0x00000002 +#define CPCAP_BIT_SE1_S 0x00000001 + +/* + * Register 10 - CPCAP_REG_INTS3 bits + */ +#define CPCAP_BIT_USBDPLLCLK_S 0x00008000 +#define CPCAP_BIT_PWRGOOD_S 0x00004000 +#define CPCAP_BIT_UCRESET_S 0x00002000 +#define CPCAP_BIT_ONEWIRE3_S 0x00001000 +#define CPCAP_BIT_ONEWIRE2_S 0x00000800 +#define CPCAP_BIT_ONEWIRE1_S 0x00000400 +#define CPCAP_BIT_OPT_SEL_STATE_S 0x00000200 +#define CPCAP_BIT_OPT_SEL_DTCH_S 0x00000100 +#define CPCAP_BIT_TODA_S 0x00000080 +#define CPCAP_BIT_OFLOWSW_S 0x00000040 +#define CPCAP_BIT_PC_S 0x00000020 +#define CPCAP_BIT_DIETEMPH_S 0x00000010 +#define CPCAP_BIT_DIEPWRDWN_S 0x00000008 +#define CPCAP_BIT_SOFTRST_S 0x00000004 +#define CPCAP_BIT_SYSRSTRT_S 0x00000002 +#define CPCAP_BIT_WARM_S 0x00000001 + +/* + * Register 11 - CPCAP_REG_INTS4 bits + */ +#define CPCAP_BIT_UNUSED_11_15_S 0x00008000 +#define CPCAP_BIT_UNUSED_11_14_S 0x00004000 +#define CPCAP_BIT_SPARE_11_13_S 0x00002000 +#define CPCAP_BIT_SPARE_11_12_S 0x00001000 +#define CPCAP_BIT_SPARE_11_11_S 0x00000800 +#define CPCAP_BIT_SPARE_11_10_S 0x00000400 +#define CPCAP_BIT_CC_CAL_S 0x00000200 +#define CPCAP_BIT_SECHALT_S 0x00000100 +#define CPCAP_BIT_PRIHALT_S 0x00000080 +#define CPCAP_BIT_BATTDETB_S 0x00000040 +#define CPCAP_BIT_SB_MAX_RETX_ERR_S 0x00000020 +#define CPCAP_BIT_GCAI_CURR2_S 0x00000010 +#define CPCAP_BIT_GCAI_CURR1_S 0x00000008 +#define CPCAP_BIT_UCBUSY_S 0x00000004 +#define CPCAP_BIT_DM_S 0x00000002 +#define CPCAP_BIT_DP_S 0x00000001 + +/* + * Register 128 - CPCAP_REG_MI1 bits + */ +#define CPCAP_BIT_PRIMACRO_15_S 0x00008000 +#define CPCAP_BIT_PRIMACRO_14_S 0x00004000 +#define CPCAP_BIT_PRIMACRO_13_S 0x00002000 +#define CPCAP_BIT_PRIMACRO_12_S 0x00001000 +#define CPCAP_BIT_PRIMACRO_11_S 0x00000800 +#define CPCAP_BIT_PRIMACRO_10_S 0x00000400 +#define CPCAP_BIT_PRIMACRO_9_S 0x00000200 +#define CPCAP_BIT_PRIMACRO_8_S 0x00000100 +#define CPCAP_BIT_PRIMACRO_7_S 0x00000080 +#define CPCAP_BIT_PRIMACRO_6_S 0x00000040 +#define CPCAP_BIT_PRIMACRO_5_S 0x00000020 +#define CPCAP_BIT_PRIMACRO_4_S 0x00000010 +#define CPCAP_BIT_USEROFF_S 0x00000008 +#define CPCAP_BIT_PRIRAMR_S 0x00000004 +#define CPCAP_BIT_PRIRAMW_S 0x00000002 +#define CPCAP_BIT_PRIROMR_S 0x00000001 + +/* + * Register 129 - CPCAP_REG_MIM1 bits + */ +#define CPCAP_BIT_PRIMACRO_15M 0x00008000 +#define CPCAP_BIT_PRIMACRO_14M 0x00004000 +#define CPCAP_BIT_PRIMACRO_13M 0x00002000 +#define CPCAP_BIT_PRIMACRO_12M 0x00001000 +#define CPCAP_BIT_PRIMACRO_11M 0x00000800 +#define CPCAP_BIT_PRIMACRO_10M 0x00000400 +#define CPCAP_BIT_PRIMACRO_9M 0x00000200 +#define CPCAP_BIT_PRIMACRO_8M 0x00000100 +#define CPCAP_BIT_PRIMACRO_7M 0x00000080 +#define CPCAP_BIT_PRIMACRO_6M 0x00000040 +#define CPCAP_BIT_PRIMACRO_5M 0x00000020 +#define CPCAP_BIT_PRIMACRO_4M 0x00000010 +#define CPCAP_BIT_USEROFFM 0x00000008 +#define CPCAP_BIT_PRIRAMRM 0x00000004 +#define CPCAP_BIT_PRIRAMWM 0x00000002 +#define CPCAP_BIT_PRIROMRM 0x00000001 + +/* + * Register 130 - CPCAP_REG_MI2 bits + */ +#define CPCAP_BIT_PRIMACRO_15 0x00008000 +#define CPCAP_BIT_PRIMACRO_14 0x00004000 +#define CPCAP_BIT_PRIMACRO_13 0x00002000 +#define CPCAP_BIT_PRIMACRO_12 0x00001000 +#define CPCAP_BIT_PRIMACRO_11 0x00000800 +#define CPCAP_BIT_PRIMACRO_10 0x00000400 +#define CPCAP_BIT_PRIMACRO_9 0x00000200 +#define CPCAP_BIT_PRIMACRO_8 0x00000100 +#define CPCAP_BIT_PRIMACRO_7 0x00000080 +#define CPCAP_BIT_PRIMACRO_6 0x00000040 +#define CPCAP_BIT_PRIMACRO_5 0x00000020 +#define CPCAP_BIT_PRIMACRO_4 0x00000010 +#define CPCAP_BIT_USEROFF 0x00000008 +#define CPCAP_BIT_PRIRAMR 0x00000004 +#define CPCAP_BIT_PRIRAMW 0x00000002 +#define CPCAP_BIT_PRIROMR 0x00000001 + +/* + * Register 131 - CPCAP_REG_MIM2 bits + */ +#define CPCAP_BIT_PRIMACRO_15S 0x00008000 +#define CPCAP_BIT_PRIMACRO_14S 0x00004000 +#define CPCAP_BIT_PRIMACRO_13S 0x00002000 +#define CPCAP_BIT_PRIMACRO_12S 0x00001000 +#define CPCAP_BIT_PRIMACRO_11S 0x00000800 +#define CPCAP_BIT_PRIMACRO_10S 0x00000400 +#define CPCAP_BIT_PRIMACRO_9S 0x00000200 +#define CPCAP_BIT_PRIMACRO_8S 0x00000100 +#define CPCAP_BIT_PRIMACRO_7S 0x00000080 +#define CPCAP_BIT_PRIMACRO_6S 0x00000040 +#define CPCAP_BIT_PRIMACRO_5S 0x00000020 +#define CPCAP_BIT_PRIMACRO_4S 0x00000010 +#define CPCAP_BIT_USEROFFS 0x00000008 +#define CPCAP_BIT_PRIRAMRS 0x00000004 +#define CPCAP_BIT_PRIRAMWS 0x00000002 +#define CPCAP_BIT_PRIROMRS 0x00000001 + +/* + * Register 132 - CPCAP_REG_UCC1 bits + */ +#define CPCAP_BIT_UNUSED_132_15 0x00008000 +#define CPCAP_BIT_UNUSED_132_14 0x00004000 +#define CPCAP_BIT_UNUSED_132_13 0x00002000 +#define CPCAP_BIT_UNUSED_132_12 0x00001000 +#define CPCAP_BIT_PRI_GPIO6_2MAC10 0x00000800 +#define CPCAP_BIT_PRI_GPIO5_2MAC9 0x00000400 +#define CPCAP_BIT_PRI_GPIO4_2MAC8 0x00000200 +#define CPCAP_BIT_PRI_GPIO3_2MAC7 0x00000100 +#define CPCAP_BIT_PRI_GPIO2_2MAC6 0x00000080 +#define CPCAP_BIT_PRI_GPIO1_2MAC5 0x00000040 +#define CPCAP_BIT_PRI_GPIO0_2MAC4 0x00000020 +#define CPCAP_BIT_USEROFFCLK 0x00000010 +#define CPCAP_BIT_UO_MH_PFM_EN 0x00000008 +#define CPCAP_BIT_CNTRLSEC 0x00000004 +#define CPCAP_BIT_SCHDOVERRIDE 0x00000002 +#define CPCAP_BIT_PRIHALT 0x00000001 + +/* + * Register 135 - CPCAP_REG_PC1 bits + */ +#define CPCAP_BIT_UNUSED_135_15 0x00008000 +#define CPCAP_BIT_UNUSED_135_14 0x00004000 +#define CPCAP_BIT_UNUSED_135_13 0x00002000 +#define CPCAP_BIT_UNUSED_135_12 0x00001000 +#define CPCAP_BIT_UNUSED_135_11 0x00000800 +#define CPCAP_BIT_UNUSED_135_10 0x00000400 +#define CPCAP_BIT_PC1_SC_SHTDWN_EN 0x00000200 +#define CPCAP_BIT_PC1_PCEN 0x00000100 +#define CPCAP_BIT_PC1_PCT7 0x00000080 +#define CPCAP_BIT_PC1_PCT6 0x00000040 +#define CPCAP_BIT_PC1_PCT5 0x00000020 +#define CPCAP_BIT_PC1_PCT4 0x00000010 +#define CPCAP_BIT_PC1_PCT3 0x00000008 +#define CPCAP_BIT_PC1_PCT2 0x00000004 +#define CPCAP_BIT_PC1_PCT1 0x00000002 +#define CPCAP_BIT_PC1_PCT0 0x00000001 + +/* + * Register 138 - CPCAP_REG_PGC bits + */ +#define CPCAP_BIT_UNUSED_138_15 0x00008000 +#define CPCAP_BIT_UNUSED_138_14 0x00004000 +#define CPCAP_BIT_UNUSED_138_13 0x00002000 +#define CPCAP_BIT_UNUSED_138_12 0x00001000 +#define CPCAP_BIT_UNUSED_138_11 0x00000800 +#define CPCAP_BIT_UNUSED_138_10 0x00000400 +#define CPCAP_BIT_UNUSED_138_9 0x00000200 +#define CPCAP_BIT_REVENINV 0x00000100 +#define CPCAP_BIT_PRISTBYINV 0x00000080 +#define CPCAP_BIT_SYS_RST_MODE 0x00000040 +#define CPCAP_BIT_MAC_TIME_LONG 0x00000020 +#define CPCAP_BIT_PRI_UC_SUSPEND 0x00000010 +#define CPCAP_BIT_PRIWARMSTART 0x00000008 +#define CPCAP_BIT_PRIPRESVRAM 0x00000004 +#define CPCAP_BIT_SPI_PWRGT1EN 0x00000002 +#define CPCAP_BIT_SPI_PWRGT2EN 0x00000001 + +/* + * Register 259 - CPCAP_REG_UCTM bits */ +#define CPCAP_BIT_UNUSED_259_15 0x00008000 +#define CPCAP_BIT_UNUSED_259_14 0x00004000 +#define CPCAP_BIT_UNUSED_259_13 0x00002000 +#define CPCAP_BIT_UNUSED_259_12 0x00001000 +#define CPCAP_BIT_UNUSED_259_11 0x00000800 +#define CPCAP_BIT_UNUSED_259_10 0x00000400 +#define CPCAP_BIT_UNUSED_259_9 0x00000200 +#define CPCAP_BIT_UNUSED_259_8 0x00000100 +#define CPCAP_BIT_UNUSED_259_7 0x00000080 +#define CPCAP_BIT_UNUSED_259_6 0x00000040 +#define CPCAP_BIT_UNUSED_259_5 0x00000020 +#define CPCAP_BIT_UNUSED_259_4 0x00000010 +#define CPCAP_BIT_UNUSED_259_3 0x00000008 +#define CPCAP_BIT_UNUSED_259_2 0x00000004 +#define CPCAP_BIT_UNUSED_259_1 0x00000002 +#define CPCAP_BIT_UCTM 0x00000001 + +/* + * Register 266 - CPCAP_REG_VAL1 bits + */ +#define CPCAP_BIT_UNUSED_266_15 0x00008000 +#define CPCAP_BIT_UNUSED_266_14 0x00004000 +#define CPCAP_BIT_UNUSED_266_13 0x00002000 +#define CPCAP_BIT_UNUSED_266_12 0x00001000 +#define CPCAP_BIT_BOOT_MODE 0x00000800 +#define CPCAP_BIT_UNUSED_266_10 0x00000400 +#define CPCAP_BIT_OUT_CHARGE_ONLY 0x00000200 +#define CPCAP_BIT_USB_BATT_RECOVERY 0x00000100 +#define CPCAP_BIT_PANIC 0x00000080 +#define CPCAP_BIT_BP_ONLY_FLASH 0x00000040 +#define CPCAP_BIT_WATCHDOG_RESET 0x00000020 +#define CPCAP_BIT_SOFT_RESET 0x00000010 +#define CPCAP_BIT_FLASH_FAIL 0x00000008 +#define CPCAP_BIT_FOTA_MODE 0x00000004 +#define CPCAP_BIT_AP_KERNEL_PANIC 0x00000002 +#define CPCAP_BIT_FLASH_MODE 0x00000001 + +/* + * Register 385 - CPCAP_REG_SI2CC1 + */ +#define CPCAP_BIT_CLK3M2_GATE_OVERRIDE 0x00000080 + +/* + * Register 411 - CPCAP_REG_VUSB bits + */ +#define CPCAP_BIT_UNUSED_411_15 0x00008000 +#define CPCAP_BIT_UNUSED_411_14 0x00004000 +#define CPCAP_BIT_UNUSED_411_13 0x00002000 +#define CPCAP_BIT_UNUSED_411_12 0x00001000 +#define CPCAP_BIT_UNUSED_411_11 0x00000800 +#define CPCAP_BIT_UNUSED_411_10 0x00000400 +#define CPCAP_BIT_UNUSED_411_9 0x00000200 +#define CPCAP_BIT_VUSBSTBY 0x00000100 +#define CPCAP_BIT_UNUSED_411_7 0x00000080 +#define CPCAP_BIT_VUSB 0x00000040 +#define CPCAP_BIT_UNUSED_411_5 0x00000020 +#define CPCAP_BIT_VUSB_MODE2 0x00000010 +#define CPCAP_BIT_VUSB_MODE1 0x00000008 +#define CPCAP_BIT_VUSB_MODE0 0x00000004 +#define CPCAP_BIT_SPARE_411_1 0x00000002 +#define CPCAP_BIT_VBUS_SWITCH 0x00000001 +/* + * Register 512 - Audio Regulator and Bias Voltage + */ + +#define CPCAP_BIT_AUDIO_LOW_PWR 0x00000040 +#define CPCAP_BIT_AUD_LOWPWR_SPEED 0x00000020 +#define CPCAP_BIT_VAUDIOPRISTBY 0x00000010 +#define CPCAP_BIT_VAUDIO_MODE1 0x00000004 +#define CPCAP_BIT_VAUDIO_MODE0 0x00000002 +#define CPCAP_BIT_V_AUDIO_EN 0x00000001 + +/* + * Register 513 CODEC + */ + +#define CPCAP_BIT_CDC_CLK2 0x00008000 +#define CPCAP_BIT_CDC_CLK1 0x00004000 +#define CPCAP_BIT_CDC_CLK0 0x00002000 +#define CPCAP_BIT_CDC_SR3 0x00001000 +#define CPCAP_BIT_CDC_SR2 0x00000800 +#define CPCAP_BIT_CDC_SR1 0x00000400 +#define CPCAP_BIT_CDC_SR0 0x00000200 +#define CPCAP_BIT_CDC_CLOCK_TREE_RESET 0x00000100 +#define CPCAP_BIT_MIC2_CDC_EN 0x00000080 +#define CPCAP_BIT_CDC_EN_RX 0x00000040 +#define CPCAP_BIT_DF_RESET 0x00000020 +#define CPCAP_BIT_MIC1_CDC_EN 0x00000010 +#define CPCAP_BIT_AUDOHPF_1 0x00000008 +#define CPCAP_BIT_AUDOHPF_0 0x00000004 +#define CPCAP_BIT_AUDIHPF_1 0x00000002 +#define CPCAP_BIT_AUDIHPF_0 0x00000001 + +/* + * Register 514 CODEC Digital Audio Interface + */ + +#define CPCAP_BIT_CDC_PLL_SEL 0x00008000 +#define CPCAP_BIT_CLK_IN_SEL 0x00002000 +#define CPCAP_BIT_DIG_AUD_IN 0x00001000 +#define CPCAP_BIT_CDC_CLK_EN 0x00000800 +#define CPCAP_BIT_CDC_DIG_AUD_FS1 0x00000400 +#define CPCAP_BIT_CDC_DIG_AUD_FS0 0x00000200 +#define CPCAP_BIT_MIC2_TIMESLOT2 0x00000100 +#define CPCAP_BIT_MIC2_TIMESLOT1 0x00000080 +#define CPCAP_BIT_MIC2_TIMESLOT0 0x00000040 +#define CPCAP_BIT_MIC1_RX_TIMESLOT2 0x00000020 +#define CPCAP_BIT_MIC1_RX_TIMESLOT1 0x00000010 +#define CPCAP_BIT_MIC1_RX_TIMESLOT0 0x00000008 +#define CPCAP_BIT_FS_INV 0x00000004 +#define CPCAP_BIT_CLK_INV 0x00000002 +#define CPCAP_BIT_SMB_CDC 0x00000001 + +/* + * Register 515 Stereo DAC + */ + +#define CPCAP_BIT_FSYNC_CLK_IN_COMMON 0x00000800 +#define CPCAP_BIT_SLAVE_PLL_CLK_INPUT 0x00000400 +#define CPCAP_BIT_ST_CLOCK_TREE_RESET 0x00000200 +#define CPCAP_BIT_DF_RESET_ST_DAC 0x00000100 +#define CPCAP_BIT_ST_SR3 0x00000080 +#define CPCAP_BIT_ST_SR2 0x00000040 +#define CPCAP_BIT_ST_SR1 0x00000020 +#define CPCAP_BIT_ST_SR0 0x00000010 +#define CPCAP_BIT_ST_DAC_CLK2 0x00000008 +#define CPCAP_BIT_ST_DAC_CLK1 0x00000004 +#define CPCAP_BIT_ST_DAC_CLK0 0x00000002 +#define CPCAP_BIT_ST_DAC_EN 0x00000001 + +/* + * Register 516 Stereo DAC Digital Audio Interface + */ + +#define CPCAP_BIT_ST_L_TIMESLOT2 0x00002000 +#define CPCAP_BIT_ST_L_TIMESLOT1 0x00001000 +#define CPCAP_BIT_ST_L_TIMESLOT0 0x00000800 +#define CPCAP_BIT_ST_R_TIMESLOT2 0x00000400 +#define CPCAP_BIT_ST_R_TIMESLOT1 0x00000200 +#define CPCAP_BIT_ST_R_TIMESLOT0 0x00000100 +#define CPCAP_BIT_ST_DAC_CLK_IN_SEL 0x00000080 +#define CPCAP_BIT_ST_FS_INV 0x00000040 +#define CPCAP_BIT_ST_CLK_INV 0x00000020 +#define CPCAP_BIT_ST_DIG_AUD_FS1 0x00000010 +#define CPCAP_BIT_ST_DIG_AUD_FS0 0x00000008 +#define CPCAP_BIT_DIG_AUD_IN_ST_DAC 0x00000004 +#define CPCAP_BIT_ST_CLK_EN 0x00000002 +#define CPCAP_BIT_SMB_ST_DAC 0x00000001 + +/* + * Register 517 - CPCAP_REG_TXI bits + */ +#define CPCAP_BIT_PTT_TH 0x00008000 +#define CPCAP_BIT_PTT_CMP_EN 0x00004000 +#define CPCAP_BIT_HS_ID_TX 0x00002000 +#define CPCAP_BIT_MB_ON2 0x00001000 +#define CPCAP_BIT_MB_ON1L 0x00000800 +#define CPCAP_BIT_MB_ON1R 0x00000400 +#define CPCAP_BIT_RX_L_ENCODE 0x00000200 +#define CPCAP_BIT_RX_R_ENCODE 0x00000100 +#define CPCAP_BIT_MIC2_MUX 0x00000080 +#define CPCAP_BIT_MIC2_PGA_EN 0x00000040 +#define CPCAP_BIT_CDET_DIS 0x00000020 +#define CPCAP_BIT_EMU_MIC_MUX 0x00000010 +#define CPCAP_BIT_HS_MIC_MUX 0x00000008 +#define CPCAP_BIT_MIC1_MUX 0x00000004 +#define CPCAP_BIT_MIC1_PGA_EN 0x00000002 +#define CPCAP_BIT_DLM 0x00000001 + +/* + * Register 518 MIC PGA's + */ +#define CPCAP_BIT_MB_BIAS_R1 0x00000800 +#define CPCAP_BIT_MB_BIAS_R0 0x00000400 +#define CPCAP_BIT_MIC2_GAIN_4 0x00000200 +#define CPCAP_BIT_MIC2_GAIN_3 0x00000100 +#define CPCAP_BIT_MIC2_GAIN_2 0x00000080 +#define CPCAP_BIT_MIC2_GAIN_1 0x00000040 +#define CPCAP_BIT_MIC2_GAIN_0 0x00000020 +#define CPCAP_BIT_MIC1_GAIN_4 0x00000010 +#define CPCAP_BIT_MIC1_GAIN_3 0x00000008 +#define CPCAP_BIT_MIC1_GAIN_2 0x00000004 +#define CPCAP_BIT_MIC1_GAIN_1 0x00000002 +#define CPCAP_BIT_MIC1_GAIN_0 0x00000001 + +/* + * Register 519 - CPCAP_REG_RXOA bits + */ +#define CPCAP_BIT_UNUSED_519_15 0x00008000 +#define CPCAP_BIT_UNUSED_519_14 0x00004000 +#define CPCAP_BIT_UNUSED_519_13 0x00002000 +#define CPCAP_BIT_STDAC_LOW_PWR_DISABLE 0x00001000 +#define CPCAP_BIT_HS_LOW_PWR 0x00000800 +#define CPCAP_BIT_HS_ID_RX 0x00000400 +#define CPCAP_BIT_ST_HS_CP_EN 0x00000200 +#define CPCAP_BIT_EMU_SPKR_R_EN 0x00000100 +#define CPCAP_BIT_EMU_SPKR_L_EN 0x00000080 +#define CPCAP_BIT_HS_L_EN 0x00000040 +#define CPCAP_BIT_HS_R_EN 0x00000020 +#define CPCAP_BIT_A4_LINEOUT_L_EN 0x00000010 +#define CPCAP_BIT_A4_LINEOUT_R_EN 0x00000008 +#define CPCAP_BIT_A2_LDSP_L_EN 0x00000004 +#define CPCAP_BIT_A2_LDSP_R_EN 0x00000002 +#define CPCAP_BIT_A1_EAR_EN 0x00000001 + +/* + * Register 520 RX Volume Control + */ +#define CPCAP_BIT_VOL_EXT3 0x00008000 +#define CPCAP_BIT_VOL_EXT2 0x00004000 +#define CPCAP_BIT_VOL_EXT1 0x00002000 +#define CPCAP_BIT_VOL_EXT0 0x00001000 +#define CPCAP_BIT_VOL_DAC3 0x00000800 +#define CPCAP_BIT_VOL_DAC2 0x00000400 +#define CPCAP_BIT_VOL_DAC1 0x00000200 +#define CPCAP_BIT_VOL_DAC0 0x00000100 +#define CPCAP_BIT_VOL_DAC_LSB_1dB1 0x00000080 +#define CPCAP_BIT_VOL_DAC_LSB_1dB0 0x00000040 +#define CPCAP_BIT_VOL_CDC3 0x00000020 +#define CPCAP_BIT_VOL_CDC2 0x00000010 +#define CPCAP_BIT_VOL_CDC1 0x00000008 +#define CPCAP_BIT_VOL_CDC0 0x00000004 +#define CPCAP_BIT_VOL_CDC_LSB_1dB1 0x00000002 +#define CPCAP_BIT_VOL_CDC_LSB_1dB0 0x00000001 + +/* + * Register 521 Codec to Output Amp Switches + */ +#define CPCAP_BIT_PGA_CDC_EN 0x00000400 +#define CPCAP_BIT_CDC_SW 0x00000200 +#define CPCAP_BIT_PGA_OUTR_USBDP_CDC_SW 0x00000100 +#define CPCAP_BIT_PGA_OUTL_USBDN_CDC_SW 0x00000080 +#define CPCAP_BIT_ALEFT_HS_CDC_SW 0x00000040 +#define CPCAP_BIT_ARIGHT_HS_CDC_SW 0x00000020 +#define CPCAP_BIT_A4_LINEOUT_L_CDC_SW 0x00000010 +#define CPCAP_BIT_A4_LINEOUT_R_CDC_SW 0x00000008 +#define CPCAP_BIT_A2_LDSP_L_CDC_SW 0x00000004 +#define CPCAP_BIT_A2_LDSP_R_CDC_SW 0x00000002 +#define CPCAP_BIT_A1_EAR_CDC_SW 0x00000001 + +/* + * Register 522 RX Stereo DAC to Output Amp Switches + */ +#define CPCAP_BIT_PGA_DAC_EN 0x00001000 +#define CPCAP_BIT_ST_DAC_SW 0x00000800 +#define CPCAP_BIT_MONO_DAC1 0x00000400 +#define CPCAP_BIT_MONO_DAC0 0x00000200 +#define CPCAP_BIT_PGA_OUTR_USBDP_DAC_SW 0x00000100 +#define CPCAP_BIT_PGA_OUTL_USBDN_DAC_SW 0x00000080 +#define CPCAP_BIT_ALEFT_HS_DAC_SW 0x00000040 +#define CPCAP_BIT_ARIGHT_HS_DAC_SW 0x00000020 +#define CPCAP_BIT_A4_LINEOUT_L_DAC_SW 0x00000010 +#define CPCAP_BIT_A4_LINEOUT_R_DAC_SW 0x00000008 +#define CPCAP_BIT_A2_LDSP_L_DAC_SW 0x00000004 +#define CPCAP_BIT_A2_LDSP_R_DAC_SW 0x00000002 +#define CPCAP_BIT_A1_EAR_DAC_SW 0x00000001 + +/* + * Register 523 RX External PGA to Output Amp Switches + */ +#define CPCAP_BIT_PGA_EXT_L_EN 0x00004000 +#define CPCAP_BIT_PGA_EXT_R_EN 0x00002000 +#define CPCAP_BIT_PGA_IN_L_SW 0x00001000 +#define CPCAP_BIT_PGA_IN_R_SW 0x00000800 +#define CPCAP_BIT_MONO_EXT1 0x00000400 +#define CPCAP_BIT_MONO_EXT0 0x00000200 +#define CPCAP_BIT_PGA_OUTR_USBDP_EXT_SW 0x00000100 +#define CPCAP_BIT_PGA_OUTL_USBDN_EXT_SW 0x00000080 +#define CPCAP_BIT_ALEFT_HS_EXT_SW 0x00000040 +#define CPCAP_BIT_ARIGHT_HS_EXT_SW 0x00000020 +#define CPCAP_BIT_A4_LINEOUT_L_EXT_SW 0x00000010 +#define CPCAP_BIT_A4_LINEOUT_R_EXT_SW 0x00000008 +#define CPCAP_BIT_A2_LDSP_L_EXT_SW 0x00000004 +#define CPCAP_BIT_A2_LDSP_R_EXT_SW 0x00000002 +#define CPCAP_BIT_A1_EAR_EXT_SW 0x00000001 + +/* + * Register 525 Loudspeaker Amplifier and Clock Configuration for Headset + */ +#define CPCAP_BIT_NCP_CLK_SYNC 0x00000080 +#define CPCAP_BIT_A2_CLK_SYNC 0x00000040 +#define CPCAP_BIT_A2_FREE_RUN 0x00000020 +#define CPCAP_BIT_A2_CLK2 0x00000010 +#define CPCAP_BIT_A2_CLK1 0x00000008 +#define CPCAP_BIT_A2_CLK0 0x00000004 +#define CPCAP_BIT_A2_CLK_IN 0x00000002 +#define CPCAP_BIT_A2_CONFIG 0x00000001 + +/* + * Register 641 - CPCAP_REG_CHRGR_1 bits + */ +#define CPCAP_BIT_UNUSED_641_15 0x00008000 +#define CPCAP_BIT_UNUSED_641_14 0x00004000 +#define CPCAP_BIT_CHRG_LED_EN 0x00002000 +#define CPCAP_BIT_RVRSMODE 0x00001000 +#define CPCAP_BIT_ICHRG_TR1 0x00000800 +#define CPCAP_BIT_ICHRG_TR0 0x00000400 +#define CPCAP_BIT_FET_OVRD 0x00000200 +#define CPCAP_BIT_FET_CTRL 0x00000100 +#define CPCAP_BIT_VCHRG3 0x00000080 +#define CPCAP_BIT_VCHRG2 0x00000040 +#define CPCAP_BIT_VCHRG1 0x00000020 +#define CPCAP_BIT_VCHRG0 0x00000010 +#define CPCAP_BIT_ICHRG3 0x00000008 +#define CPCAP_BIT_ICHRG2 0x00000004 +#define CPCAP_BIT_ICHRG1 0x00000002 +#define CPCAP_BIT_ICHRG0 0x00000001 + +/* + * Register 768 - CPCAP_REG_ADCC1 bits + */ +#define CPCAP_BIT_ADEN_AUTO_CLR 0x00008000 +#define CPCAP_BIT_CAL_MODE 0x00004000 +#define CPCAP_BIT_ADC_CLK_SEL1 0x00002000 +#define CPCAP_BIT_ADC_CLK_SEL0 0x00001000 +#define CPCAP_BIT_ATOX 0x00000800 +#define CPCAP_BIT_ATO3 0x00000400 +#define CPCAP_BIT_ATO2 0x00000200 +#define CPCAP_BIT_ATO1 0x00000100 +#define CPCAP_BIT_ATO0 0x00000080 +#define CPCAP_BIT_ADA2 0x00000040 +#define CPCAP_BIT_ADA1 0x00000020 +#define CPCAP_BIT_ADA0 0x00000010 +#define CPCAP_BIT_AD_SEL1 0x00000008 +#define CPCAP_BIT_RAND1 0x00000004 +#define CPCAP_BIT_RAND0 0x00000002 +#define CPCAP_BIT_ADEN 0x00000001 + +/* + * Register 769 - CPCAP_REG_ADCC2 bits + */ +#define CPCAP_BIT_CAL_FACTOR_ENABLE 0x00008000 +#define CPCAP_BIT_BATDETB_EN 0x00004000 +#define CPCAP_BIT_ADTRIG_ONESHOT 0x00002000 +#define CPCAP_BIT_ASC 0x00001000 +#define CPCAP_BIT_ATOX_PS_FACTOR 0x00000800 +#define CPCAP_BIT_ADC_PS_FACTOR1 0x00000400 +#define CPCAP_BIT_ADC_PS_FACTOR0 0x00000200 +#define CPCAP_BIT_AD4_SELECT 0x00000100 +#define CPCAP_BIT_ADC_BUSY 0x00000080 +#define CPCAP_BIT_THERMBIAS_EN 0x00000040 +#define CPCAP_BIT_ADTRIG_DIS 0x00000020 +#define CPCAP_BIT_LIADC 0x00000010 +#define CPCAP_BIT_TS_REFEN 0x00000008 +#define CPCAP_BIT_TS_M2 0x00000004 +#define CPCAP_BIT_TS_M1 0x00000002 +#define CPCAP_BIT_TS_M0 0x00000001 + +/* + * Register 896 - CPCAP_REG_USBC1 bits + */ +#define CPCAP_BIT_IDPULSE 0x00008000 +#define CPCAP_BIT_ID100KPU 0x00004000 +#define CPCAP_BIT_IDPUCNTRL 0x00002000 +#define CPCAP_BIT_IDPU 0x00001000 +#define CPCAP_BIT_IDPD 0x00000800 +#define CPCAP_BIT_VBUSCHRGTMR3 0x00000400 +#define CPCAP_BIT_VBUSCHRGTMR2 0x00000200 +#define CPCAP_BIT_VBUSCHRGTMR1 0x00000100 +#define CPCAP_BIT_VBUSCHRGTMR0 0x00000080 +#define CPCAP_BIT_VBUSPU 0x00000040 +#define CPCAP_BIT_VBUSPD 0x00000020 +#define CPCAP_BIT_DMPD 0x00000010 +#define CPCAP_BIT_DPPD 0x00000008 +#define CPCAP_BIT_DM1K5PU 0x00000004 +#define CPCAP_BIT_DP1K5PU 0X00000002 +#define CPCAP_BIT_DP150KPU 0x00000001 + +/* + * Register 897 - CPCAP_REG_USBC2 bits + */ +#define CPCAP_BIT_ZHSDRV1 0x00008000 +#define CPCAP_BIT_ZHSDRV0 0x00004000 +#define CPCAP_BIT_DPLLCLKREQ 0x00002000 +#define CPCAP_BIT_SE0CONN 0x00001000 +#define CPCAP_BIT_UARTTXTRI 0x00000800 +#define CPCAP_BIT_UARTSWAP 0x00000400 +#define CPCAP_BIT_UARTMUX1 0x00000200 +#define CPCAP_BIT_UARTMUX0 0x00000100 +#define CPCAP_BIT_ULPISTPLOW 0x00000080 +#define CPCAP_BIT_TXENPOL 0x00000040 +#define CPCAP_BIT_USBXCVREN 0x00000020 +#define CPCAP_BIT_USBCNTRL 0x00000010 +#define CPCAP_BIT_USBSUSPEND 0x00000008 +#define CPCAP_BIT_EMUMODE2 0x00000004 +#define CPCAP_BIT_EMUMODE1 0x00000002 +#define CPCAP_BIT_EMUMODE0 0x00000001 + +/* + * Register 898 - CPCAP_REG_USBC3 bits + */ +#define CPCAP_BIT_SPARE_898_15 0x00008000 +#define CPCAP_BIT_IHSTX03 0x00004000 +#define CPCAP_BIT_IHSTX02 0x00002000 +#define CPCAP_BIT_IHSTX01 0x00001000 +#define CPCAP_BIT_IHSTX0 0x00000800 +#define CPCAP_BIT_IDPU_SPI 0x00000400 +#define CPCAP_BIT_UNUSED_898_9 0x00000200 +#define CPCAP_BIT_VBUSSTBY_EN 0x00000100 +#define CPCAP_BIT_VBUSEN_SPI 0x00000080 +#define CPCAP_BIT_VBUSPU_SPI 0x00000040 +#define CPCAP_BIT_VBUSPD_SPI 0x00000020 +#define CPCAP_BIT_DMPD_SPI 0x00000010 +#define CPCAP_BIT_DPPD_SPI 0x00000008 +#define CPCAP_BIT_SUSPEND_SPI 0x00000004 +#define CPCAP_BIT_PU_SPI 0x00000002 +#define CPCAP_BIT_ULPI_SPI_SEL 0x00000001 + +/* + * Register 941 - CPCAP_REG_GPIO0 bits + */ +#define CPCAP_BIT_GPIO0MACROINITL 0x00008000 +#define CPCAP_BIT_GPIO0MACROINITH 0x00004000 +#define CPCAP_BIT_GPIO0MACROML 0x00002000 +#define CPCAP_BIT_GPIO0MACROMH 0x00001000 +#define CPCAP_BIT_UNUSED_941_11 0x00000800 +#define CPCAP_BIT_UNUSED_941_10 0x00000400 +#define CPCAP_BIT_GPIO0VLEV 0x00000200 +#define CPCAP_BIT_UNUSED_941_8 0x00000100 +#define CPCAP_BIT_GPIO0MUX1 0x00000080 +#define CPCAP_BIT_GPIO0MUX0 0x00000040 +#define CPCAP_BIT_GPIO0OT 0x00000020 +#define CPCAP_BIT_SPARE_941_4 0x00000010 +#define CPCAP_BIT_GPIO0PUEN 0x00000008 +#define CPCAP_BIT_GPIO0DIR 0x00000004 +#define CPCAP_BIT_GPIO0DRV 0x00000002 +#define CPCAP_BIT_GPIO0S 0x00000001 + +/* + * Register 943 - CPCAP_REG_GPIO1 bits + */ +#define CPCAP_BIT_GPIO1MACROINITL 0x00008000 +#define CPCAP_BIT_GPIO1MACROINITH 0x00004000 +#define CPCAP_BIT_GPIO1MACROML 0x00002000 +#define CPCAP_BIT_GPIO1MACROMH 0x00001000 +#define CPCAP_BIT_UNUSED_943_11 0x00000800 +#define CPCAP_BIT_UNUSED_943_10 0x00000400 +#define CPCAP_BIT_GPIO1VLEV 0x00000200 +#define CPCAP_BIT_UNUSED_943_8 0x00000100 +#define CPCAP_BIT_GPIO1MUX1 0x00000080 +#define CPCAP_BIT_GPIO1MUX0 0x00000040 +#define CPCAP_BIT_GPIO1OT 0x00000020 +#define CPCAP_BIT_SPARE_943_4 0x00000010 +#define CPCAP_BIT_GPIO1PUEN 0x00000008 +#define CPCAP_BIT_GPIO1DIR 0x00000004 +#define CPCAP_BIT_GPIO1DRV 0x00000002 +#define CPCAP_BIT_GPIO1S 0x00000001 + +/* + * Register 945 - CPCAP_REG_GPIO2 bits + */ +#define CPCAP_BIT_GPIO2MACROINITL 0x00008000 +#define CPCAP_BIT_GPIO2MACROINITH 0x00004000 +#define CPCAP_BIT_GPIO2MACROML 0x00002000 +#define CPCAP_BIT_GPIO2MACROMH 0x00001000 +#define CPCAP_BIT_UNUSED_945_11 0x00000800 +#define CPCAP_BIT_UNUSED_945_10 0x00000400 +#define CPCAP_BIT_GPIO2VLEV 0x00000200 +#define CPCAP_BIT_UNUSED_945_8 0x00000100 +#define CPCAP_BIT_GPIO2MUX1 0x00000080 +#define CPCAP_BIT_GPIO2MUX0 0x00000040 +#define CPCAP_BIT_GPIO2OT 0x00000020 +#define CPCAP_BIT_SPARE_945_4 0x00000010 +#define CPCAP_BIT_GPIO2PUEN 0x00000008 +#define CPCAP_BIT_GPIO2DIR 0x00000004 +#define CPCAP_BIT_GPIO2DRV 0x00000002 +#define CPCAP_BIT_GPIO2S 0x00000001 + +/* + * Register 947 - CPCAP_REG_GPIO3 bits + */ +#define CPCAP_BIT_GPIO3MACROINITL 0x00008000 +#define CPCAP_BIT_GPIO3MACROINITH 0x00004000 +#define CPCAP_BIT_GPIO3MACROML 0x00002000 +#define CPCAP_BIT_GPIO3MACROMH 0x00001000 +#define CPCAP_BIT_UNUSED_947_11 0x00000800 +#define CPCAP_BIT_UNUSED_947_10 0x00000400 +#define CPCAP_BIT_GPIO3VLEV 0x00000200 +#define CPCAP_BIT_UNUSED_947_8 0x00000100 +#define CPCAP_BIT_GPIO3MUX1 0x00000080 +#define CPCAP_BIT_GPIO3MUX0 0x00000040 +#define CPCAP_BIT_GPIO3OT 0x00000020 +#define CPCAP_BIT_SPARE_947_4 0x00000010 +#define CPCAP_BIT_GPIO3PUEN 0x00000008 +#define CPCAP_BIT_GPIO3DIR 0x00000004 +#define CPCAP_BIT_GPIO3DRV 0x00000002 +#define CPCAP_BIT_GPIO3S 0x00000001 + +/* + * Register 949 - CPCAP_REG_GPIO4 bits + */ +#define CPCAP_BIT_GPIO4MACROINITL 0x00008000 +#define CPCAP_BIT_GPIO4MACROINITH 0x00004000 +#define CPCAP_BIT_GPIO4MACROML 0x00002000 +#define CPCAP_BIT_GPIO4MACROMH 0x00001000 +#define CPCAP_BIT_UNUSED_949_11 0x00000800 +#define CPCAP_BIT_UNUSED_949_10 0x00000400 +#define CPCAP_BIT_GPIO4VLEV 0x00000200 +#define CPCAP_BIT_UNUSED_949_8 0x00000100 +#define CPCAP_BIT_GPIO4MUX1 0x00000080 +#define CPCAP_BIT_GPIO4MUX0 0x00000040 +#define CPCAP_BIT_GPIO4OT 0x00000020 +#define CPCAP_BIT_SPARE_949_4 0x00000010 +#define CPCAP_BIT_GPIO4PUEN 0x00000008 +#define CPCAP_BIT_GPIO4DIR 0x00000004 +#define CPCAP_BIT_GPIO4DRV 0x00000002 +#define CPCAP_BIT_GPIO4S 0x00000001 + +/* + * Register 951 - CPCAP_REG_GPIO5 bits + */ +#define CPCAP_BIT_GPIO5MACROINITL 0x00008000 +#define CPCAP_BIT_GPIO5MACROINITH 0x00004000 +#define CPCAP_BIT_GPIO5MACROML 0x00002000 +#define CPCAP_BIT_GPIO5MACROMH 0x00001000 +#define CPCAP_BIT_UNUSED_951_11 0x00000800 +#define CPCAP_BIT_UNUSED_951_10 0x00000400 +#define CPCAP_BIT_GPIO5VLEV 0x00000200 +#define CPCAP_BIT_GPIO5MUX2 0x00000100 +#define CPCAP_BIT_GPIO5MUX1 0x00000080 +#define CPCAP_BIT_GPIO5MUX0 0x00000040 +#define CPCAP_BIT_GPIO5OT 0x00000020 +#define CPCAP_BIT_SPARE_951_4 0x00000010 +#define CPCAP_BIT_GPIO5PUEN 0x00000008 +#define CPCAP_BIT_GPIO5DIR 0x00000004 +#define CPCAP_BIT_GPIO5DRV 0x00000002 +#define CPCAP_BIT_GPIO5S 0x00000001 + +/* + * Register 953 - CPCAP_REG_GPIO6 bits + */ +#define CPCAP_BIT_GPIO6MACROINITL 0x00008000 +#define CPCAP_BIT_GPIO6MACROINITH 0x00004000 +#define CPCAP_BIT_GPIO6MACROML 0x00002000 +#define CPCAP_BIT_GPIO6MACROMH 0x00001000 +#define CPCAP_BIT_UNUSED_953_11 0x00000800 +#define CPCAP_BIT_UNUSED_953_10 0x00000400 +#define CPCAP_BIT_GPIO6VLEV 0x00000200 +#define CPCAP_BIT_GPIO6MUX2 0x00000100 +#define CPCAP_BIT_GPIO6MUX1 0x00000080 +#define CPCAP_BIT_GPIO6MUX0 0x00000040 +#define CPCAP_BIT_GPIO6OT 0x00000020 +#define CPCAP_BIT_SPARE_953_4 0x00000010 +#define CPCAP_BIT_GPIO6PUEN 0x00000008 +#define CPCAP_BIT_GPIO6DIR 0x00000004 +#define CPCAP_BIT_GPIO6DRV 0x00000002 +#define CPCAP_BIT_GPIO6S 0x00000001 + +#endif /* __CPCAP_REGBITS_H__ */ diff --git a/include/linux/spi/cpcap.h b/include/linux/spi/cpcap.h new file mode 100644 index 000000000000..fc806858c318 --- /dev/null +++ b/include/linux/spi/cpcap.h @@ -0,0 +1,791 @@ +#ifndef _LINUX_SPI_CPCAP_H +#define _LINUX_SPI_CPCAP_H + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + * + */ + +#include +#ifdef __KERNEL__ +#include +#include +#include +#include +#endif + +#ifdef CONFIG_RTC_INTF_SECCLKD +#include +#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 */ -- 2.34.1