Initial Contribution
msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142
Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 37b83eb..8f2c9ac 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -145,6 +145,39 @@
This driver can also be built as a module. If so, the module
will be called tps65010.
+config TPS65023
+ tristate "TPS65023 Power Management chip"
+ depends on I2C && ARCH_MSM_SCORPION && !MSM_SMP
+ default y if I2C && ARCH_MSM_SCORPION && !MSM_SMP
+ help
+ Say yes here for Qualcomm QSD chips. The TI PMIC is used by the
+ QSD8x50 series of chips for power management.
+
+config PMIC8058
+ tristate "PMIC8058 Power Management chip"
+ depends on I2C_SSBI && (ARCH_MSM7X30 || ARCH_MSM8X60 || ARCH_FSM9XXX)
+ default y if I2C_SSBI && (ARCH_MSM7X30 || ARCH_MSM8X60 || ARCH_FSM9XXX)
+ select MFD_CORE
+ select MSM_SHOW_RESUME_IRQ
+ help
+ Say yes here for Qualcomm PM8058 chip.
+
+config PMIC8901
+ tristate "PMIC8901 Power Management chip"
+ depends on I2C_SSBI && ARCH_MSM8X60
+ default y if I2C_SSBI && ARCH_MSM8X60
+ select MFD_CORE
+ help
+ Say yes here for Qualcomm PM8901 chip.
+
+config MARIMBA_TSADC
+ tristate "Support for Marimba Touchscreen ADC"
+ depends on MARIMBA_CORE && ARCH_MSM7X30
+ default y if MARIMBA_CORE
+ help
+ Say yes here if you want to include support for TSADC in the
+ Qualcomm Marimba chip.
+
config TPS6507X
tristate "TPS6507x Power Management / Touch Screen chips"
select MFD_CORE
@@ -181,6 +214,33 @@
and other features that are often used in portable devices like
cell phones and PDAs.
+config MARIMBA_CORE
+ tristate "Marimba Core"
+ depends on I2C && (ARCH_MSM7X30 || ARCH_MSM8X60 || ARCH_MSM7X27A)
+ default n
+ help
+ Enables the Marimba Core driver. The core driver provides
+ read/write capability to registers which are part of the
+ marimba core.
+ This driver dynamically detects the SoC and works for both
+ Marimba and Bahama Chip.
+
+config MARIMBA_CODEC
+ tristate "Marimba Codec"
+ depends on MARIMBA_CORE
+ default n
+ help
+ This driver programs Marimba Wideband Codec for input/output of
+ audio signal.
+
+config TIMPANI_CODEC
+ tristate "Timpani Codec"
+ depends on MARIMBA_CORE
+ default n
+ help
+ This driver programs Timpani Wideband Codec for input/output of
+ audio signal.
+
config TWL4030_CORE
bool "Texas Instruments TWL4030/TWL5030/TWL6030/TPS659x0 Support"
depends on I2C=y && GENERIC_HARDIRQS
@@ -737,6 +797,63 @@
config TPS65911_COMPARATOR
tristate
+config MFD_PM8921_ADC
+ tristate "Support for Qualcomm PM8921 ADC"
+ depends on MFD_PM8921_CORE
+ help
+ This is the ADC arbiter driver for Qualcomm PM8921 Chip.
+
+ The driver supports reading the HKADC, XOADC and support to set and receive
+ temperature threshold notifications using the Battery temperature module.
+
+config MFD_PM8XXX_DEBUG
+ tristate "Qualcomm PM8xxx debugfs support"
+ depends on MFD_PM8XXX && DEBUG_FS
+ default y if MFD_PM8XXX
+ help
+ This driver provides a debugfs interface to the SSBI registers on
+ Qualcomm PM 8xxx PMIC chips. It allows for reads and writes to
+ arbitrary addresses. Writes are blocking so values are guaranteed to
+ be set into hardware registers upon return.
+
+config MFD_PM8XXX_PWM
+ tristate "Support for Qualcomm PM8xxx PWM feature"
+ depends on MFD_PM8XXX
+ default y if MFD_PM8XXX
+ help
+ This is the Pulse Width Modulation (PWM) driver for Qualcomm
+ PM 8xxx PMIC chips. It can drive 8 channels of PWM output, and
+ has a lookup table with size of 64 to be shared by any of the
+ 8 channels.
+
+config MFD_PM8XXX_MISC
+ tristate "Support for Qualcomm PM8xxx miscellaneous APIs"
+ depends on MFD_PM8XXX
+ default y if MFD_PM8XXX
+ help
+ This driver implements several miscellaneous APIs that may be needed
+ in order to control the PM8XXX PMIC chip.
+
+config MFD_PM8XXX_BATT_ALARM
+ tristate "Support for Qualcomm PM8xxx battery voltage alarm"
+ depends on MFD_PM8XXX
+ help
+ This driver provides a means monitor battery under and over-voltage
+ conditions. An upper and/or lower threshold can be specified for
+ normal operation. A wakeable interrupt is triggered when the battery
+ voltage leaves the accepatable range which then calls a notifier call
+ chain.
+
+config WCD9310_CODEC
+ tristate "WCD9310 Codec"
+ depends on SLIMBUS
+ select MFD_CORE
+ default n
+ help
+ Enables the WCD9310 core driver. The core driver provides
+ read/write capability to registers which are part of the
+ WCD9310 core and gives the ability to use the WCD9310 codec.
+
endif # MFD_SUPPORT
menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 22a280f..f66fc76 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -35,12 +35,26 @@
obj-$(CONFIG_TPS6105X) += tps6105x.o
obj-$(CONFIG_TPS65010) += tps65010.o
obj-$(CONFIG_TPS6507X) += tps6507x.o
+obj-$(CONFIG_MARIMBA_CODEC) += marimba-codec.o
obj-$(CONFIG_MENELAUS) += menelaus.o
obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
obj-$(CONFIG_MFD_TWL4030_AUDIO) += twl4030-audio.o
+
+obj-$(CONFIG_MARIMBA_CORE) += marimba-core.o
+obj-$(CONFIG_MARIMBA_TSADC) += marimba-tsadc.o
+obj-$(CONFIG_TPS65023) += tps65023.o
+
+obj-$(CONFIG_TIMPANI_CODEC) += timpani-codec.o
+
+ifdef CONFIG_TIMPANI_CODEC
+obj-$(CONFIG_TIMPANI_CODEC) += msm-adie-codec.o
+else ifdef CONFIG_MARIMBA_CODEC
+obj-$(CONFIG_MARIMBA_CODEC) += msm-adie-codec.o
+endif
+
obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o
obj-$(CONFIG_TWL6040_CORE) += twl6040-core.o twl6040-irq.o
@@ -55,6 +69,8 @@
obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o
obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o
+obj-$(CONFIG_WCD9310_CODEC) += wcd9310-core.o wcd9310-irq.o
+
ifeq ($(CONFIG_SA1100_ASSABET),y)
obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o
endif
@@ -96,3 +112,12 @@
obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o
obj-$(CONFIG_MFD_TPS65910) += tps65910.o tps65910-irq.o
obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
+obj-$(CONFIG_PMIC8058) += pmic8058.o
+obj-$(CONFIG_PMIC8901) += pmic8901.o
+obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o
+obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o
+obj-$(CONFIG_MFD_PM8XXX_DEBUG) += pm8xxx-debug.o
+obj-$(CONFIG_MFD_PM8XXX_PWM) += pm8xxx-pwm.o
+obj-$(CONFIG_MFD_PM8XXX_MISC) += pm8xxx-misc.o
+obj-$(CONFIG_MFD_PM8XXX_BATT_ALARM) += pm8xxx-batt-alarm.o
+obj-$(CONFIG_MFD_PM8921_ADC) += pm8921-adc.o msmproc_adc.o
diff --git a/drivers/mfd/marimba-codec.c b/drivers/mfd/marimba-codec.c
new file mode 100644
index 0000000..6416e0a
--- /dev/null
+++ b/drivers/mfd/marimba-codec.c
@@ -0,0 +1,963 @@
+/* Copyright (c) 2009-2010, Code Aurora Forum. 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 and
+ * only 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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/msm-adie-codec.h>
+#include <linux/mfd/marimba.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/string.h>
+
+#define MARIMBA_CDC_RX_CTL 0x81
+#define MARIMBA_CDC_RX_CTL_ST_EN_MASK 0x20
+#define MARIMBA_CDC_RX_CTL_ST_EN_SHFT 0x5
+#define MARIMBA_CODEC_CDC_LRXG 0x84
+#define MARIMBA_CODEC_CDC_RRXG 0x85
+#define MARIMBA_CODEC_CDC_LTXG 0x86
+#define MARIMBA_CODEC_CDC_RTXG 0x87
+
+#define MAX_MDELAY_US 2000
+#define MIN_MDELAY_US 1000
+
+struct adie_codec_path {
+ struct adie_codec_dev_profile *profile;
+ struct adie_codec_register_image img;
+ u32 hwsetting_idx;
+ u32 stage_idx;
+ u32 curr_stage;
+};
+
+static struct adie_codec_register adie_codec_tx_regs[] = {
+ { 0x04, 0xc0, 0x8C },
+ { 0x0D, 0xFF, 0x00 },
+ { 0x0E, 0xFF, 0x00 },
+ { 0x0F, 0xFF, 0x00 },
+ { 0x10, 0xF8, 0x68 },
+ { 0x11, 0xFE, 0x00 },
+ { 0x12, 0xFE, 0x00 },
+ { 0x13, 0xFF, 0x58 },
+ { 0x14, 0xFF, 0x00 },
+ { 0x15, 0xFE, 0x00 },
+ { 0x16, 0xFF, 0x00 },
+ { 0x1A, 0xFF, 0x00 },
+ { 0x80, 0x01, 0x00 },
+ { 0x82, 0x7F, 0x18 },
+ { 0x83, 0x1C, 0x00 },
+ { 0x86, 0xFF, 0xAC },
+ { 0x87, 0xFF, 0xAC },
+ { 0x89, 0xFF, 0xFF },
+ { 0x8A, 0xF0, 0x30 }
+};
+
+static struct adie_codec_register adie_codec_rx_regs[] = {
+ { 0x23, 0xF8, 0x00 },
+ { 0x24, 0x6F, 0x00 },
+ { 0x25, 0x7F, 0x00 },
+ { 0x26, 0xFC, 0x00 },
+ { 0x28, 0xFE, 0x00 },
+ { 0x29, 0xFE, 0x00 },
+ { 0x33, 0xFF, 0x00 },
+ { 0x34, 0xFF, 0x00 },
+ { 0x35, 0xFC, 0x00 },
+ { 0x36, 0xFE, 0x00 },
+ { 0x37, 0xFE, 0x00 },
+ { 0x38, 0xFE, 0x00 },
+ { 0x39, 0xF0, 0x00 },
+ { 0x3A, 0xFF, 0x0A },
+ { 0x3B, 0xFC, 0xAC },
+ { 0x3C, 0xFC, 0xAC },
+ { 0x3D, 0xFF, 0x55 },
+ { 0x3E, 0xFF, 0x55 },
+ { 0x3F, 0xCF, 0x00 },
+ { 0x40, 0x3F, 0x00 },
+ { 0x41, 0x3F, 0x00 },
+ { 0x42, 0xFF, 0x00 },
+ { 0x43, 0xF7, 0x00 },
+ { 0x43, 0xF7, 0x00 },
+ { 0x43, 0xF7, 0x00 },
+ { 0x43, 0xF7, 0x00 },
+ { 0x44, 0xF7, 0x00 },
+ { 0x45, 0xFF, 0x00 },
+ { 0x46, 0xFF, 0x00 },
+ { 0x47, 0xF7, 0x00 },
+ { 0x48, 0xF7, 0x00 },
+ { 0x49, 0xFF, 0x00 },
+ { 0x4A, 0xFF, 0x00 },
+ { 0x80, 0x02, 0x00 },
+ { 0x81, 0xFF, 0x4C },
+ { 0x83, 0x23, 0x00 },
+ { 0x84, 0xFF, 0xAC },
+ { 0x85, 0xFF, 0xAC },
+ { 0x88, 0xFF, 0xFF },
+ { 0x8A, 0x0F, 0x03 },
+ { 0x8B, 0xFF, 0xAC },
+ { 0x8C, 0x03, 0x01 },
+ { 0x8D, 0xFF, 0x00 },
+ { 0x8E, 0xFF, 0x00 }
+};
+
+static struct adie_codec_register adie_codec_lb_regs[] = {
+ { 0x2B, 0x8F, 0x02 },
+ { 0x2C, 0x8F, 0x02 }
+};
+
+struct adie_codec_state {
+ struct adie_codec_path path[ADIE_CODEC_MAX];
+ u32 ref_cnt;
+ struct marimba *pdrv_ptr;
+ struct marimba_codec_platform_data *codec_pdata;
+ struct mutex lock;
+};
+
+static struct adie_codec_state adie_codec;
+
+/* Array containing write details of Tx and RX Digital Volume
+ Tx and Rx and both the left and right channel use the same data
+*/
+u8 adie_codec_rx_tx_dig_vol_data[] = {
+ 0x81, 0x82, 0x83, 0x84,
+ 0x85, 0x86, 0x87, 0x88,
+ 0x89, 0x8a, 0x8b, 0x8c,
+ 0x8d, 0x8e, 0x8f, 0x90,
+ 0x91, 0x92, 0x93, 0x94,
+ 0x95, 0x96, 0x97, 0x98,
+ 0x99, 0x9a, 0x9b, 0x9c,
+ 0x9d, 0x9e, 0x9f, 0xa0,
+ 0xa1, 0xa2, 0xa3, 0xa4,
+ 0xa5, 0xa6, 0xa7, 0xa8,
+ 0xa9, 0xaa, 0xab, 0xac,
+ 0xad, 0xae, 0xaf, 0xb0,
+ 0xb1, 0xb2, 0xb3, 0xb4,
+ 0xb5, 0xb6, 0xb7, 0xb8,
+ 0xb9, 0xba, 0xbb, 0xbc,
+ 0xbd, 0xbe, 0xbf, 0xc0,
+ 0xc1, 0xc2, 0xc3, 0xc4,
+ 0xc5, 0xc6, 0xc7, 0xc8,
+ 0xc9, 0xca, 0xcb, 0xcc,
+ 0xcd, 0xce, 0xcf, 0xd0,
+ 0xd1, 0xd2, 0xd3, 0xd4,
+ 0xd5, 0xd6, 0xd7, 0xd8,
+ 0xd9, 0xda, 0xdb, 0xdc,
+ 0xdd, 0xde, 0xdf, 0xe0,
+ 0xe1, 0xe2, 0xe3, 0xe4,
+ 0xe5, 0xe6, 0xe7, 0xe8,
+ 0xe9, 0xea, 0xeb, 0xec,
+ 0xed, 0xee, 0xf0, 0xf1,
+ 0xf2, 0xf3, 0xf4, 0xf5,
+ 0xf6, 0xf7, 0xf8, 0xf9,
+ 0xfa, 0xfb, 0xfc, 0xfd,
+ 0xfe, 0xff, 0x00, 0x01,
+ 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x0c, 0x0d,
+ 0x0e, 0x0f, 0x10, 0x11,
+ 0x12, 0x13, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x19,
+ 0x1a, 0x1b, 0x1c, 0x1d,
+ 0x1e, 0x1f, 0x20, 0x21,
+ 0x22, 0x23, 0x24, 0x25,
+ 0x26, 0x27, 0x28, 0x29,
+ 0x2a, 0x2b, 0x2c, 0x2d,
+ 0x2e, 0x2f, 0x30, 0x31,
+ 0x32, 0x33, 0x34, 0x35,
+ 0x36, 0x37, 0x38, 0x39,
+ 0x3a, 0x3b, 0x3c, 0x3d,
+ 0x3e, 0x3f, 0x40, 0x41,
+ 0x42, 0x43, 0x44, 0x45,
+ 0x46, 0x47, 0x48, 0x49,
+ 0x4a, 0x4b, 0x4c, 0x4d,
+ 0x4e, 0x4f, 0x50, 0x51,
+ 0x52, 0x53, 0x54, 0x55,
+ 0x56, 0x57, 0x58, 0x59,
+ 0x5a, 0x5b, 0x5c, 0x5d,
+ 0x5e, 0x5f, 0x60, 0x61,
+ 0x62, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69,
+ 0x6a, 0x6b, 0x6c, 0x6d,
+ 0x6e, 0x6f, 0x70, 0x71,
+ 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79,
+ 0x7a, 0x7b, 0x7c, 0x7d,
+ 0x7e, 0x7f
+};
+
+enum adie_vol_type {
+ ADIE_CODEC_RX_DIG_VOL,
+ ADIE_CODEC_TX_DIG_VOL,
+ ADIE_CODEC_VOL_TYPE_MAX
+};
+
+struct adie_codec_ch_vol_cntrl {
+ u8 codec_reg;
+ u8 codec_mask;
+ u8 *vol_cntrl_data;
+};
+
+struct adie_codec_vol_cntrl_data {
+
+ enum adie_vol_type vol_type;
+
+ /* Jump length used while doing writes in incremental fashion */
+ u32 jump_length;
+ s32 min_mb; /* Min Db applicable to the vol control */
+ s32 max_mb; /* Max Db applicable to the vol control */
+ u32 step_in_mb;
+ u32 steps; /* No of steps allowed for this vol type */
+
+ struct adie_codec_ch_vol_cntrl *ch_vol_cntrl_info;
+};
+
+static struct adie_codec_ch_vol_cntrl adie_codec_rx_vol_cntrl[] = {
+ {MARIMBA_CODEC_CDC_LRXG, 0xff, adie_codec_rx_tx_dig_vol_data},
+ {MARIMBA_CODEC_CDC_RRXG, 0xff, adie_codec_rx_tx_dig_vol_data}
+};
+
+static struct adie_codec_ch_vol_cntrl adie_codec_tx_vol_cntrl[] = {
+ {MARIMBA_CODEC_CDC_LTXG, 0xff, adie_codec_rx_tx_dig_vol_data},
+ {MARIMBA_CODEC_CDC_RTXG, 0xff, adie_codec_rx_tx_dig_vol_data}
+};
+
+static struct adie_codec_vol_cntrl_data adie_codec_vol_cntrl[] = {
+ {ADIE_CODEC_RX_DIG_VOL, 5100, -12700, 12700, 100, 255,
+ adie_codec_rx_vol_cntrl},
+
+ {ADIE_CODEC_TX_DIG_VOL, 5100, -12700, 12700, 100, 255,
+ adie_codec_tx_vol_cntrl}
+};
+
+static int adie_codec_write(u8 reg, u8 mask, u8 val)
+{
+ int rc;
+
+ rc = marimba_write_bit_mask(adie_codec.pdrv_ptr, reg, &val, 1, mask);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("%s: fail to write reg %x\n", __func__, reg);
+ return -EIO;
+ }
+
+ pr_debug("%s: write reg %x val %x\n", __func__, reg, val);
+
+ return 0;
+}
+
+static int adie_codec_read(u8 reg, u8 *val)
+{
+ return marimba_read(adie_codec.pdrv_ptr, reg, val, 1);
+}
+
+static int adie_codec_read_dig_vol(enum adie_vol_type vol_type, u32 chan_index,
+ u32 *cur_index)
+{
+ u32 counter;
+ u32 size;
+ u8 reg, mask, cur_val;
+ int rc;
+
+ reg =
+ adie_codec_vol_cntrl[vol_type].
+ ch_vol_cntrl_info[chan_index].codec_reg;
+
+ mask =
+ adie_codec_vol_cntrl[vol_type].
+ ch_vol_cntrl_info[chan_index].codec_mask;
+
+ rc = marimba_read(adie_codec.pdrv_ptr, reg, &cur_val, 1);
+
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("%s: fail to read reg %x\n", __func__, reg);
+ return -EIO;
+ }
+
+ cur_val = cur_val & mask;
+
+ pr_debug("%s: reg 0x%x mask 0x%x reg_value = 0x%x"
+ "vol_type = %d\n", __func__, reg, mask, cur_val, vol_type);
+
+ size = adie_codec_vol_cntrl[vol_type].steps;
+
+ for (counter = 0; counter <= size; counter++) {
+
+ if (adie_codec_vol_cntrl[vol_type].ch_vol_cntrl_info
+ [chan_index].vol_cntrl_data[counter] == cur_val) {
+ *cur_index = counter;
+ return 0;
+ }
+ }
+
+ pr_err("%s: could not find 0x%x in reg 0x%x values array\n",
+ __func__, cur_val, reg);
+
+ return -EINVAL;;
+}
+
+static int adie_codec_set_dig_vol(enum adie_vol_type vol_type, u32 chan_index,
+ u32 cur_index, u32 target_index)
+{
+ u32 count;
+ u8 reg, mask, val;
+ u32 i;
+ u32 index;
+ u32 index_jump;
+
+ int rc;
+
+ index_jump = adie_codec_vol_cntrl[vol_type].jump_length;
+
+ reg =
+ adie_codec_vol_cntrl[vol_type].
+ ch_vol_cntrl_info[chan_index].codec_reg;
+
+ mask =
+ adie_codec_vol_cntrl[vol_type].
+ ch_vol_cntrl_info[chan_index].codec_mask;
+
+ /* compare the target index with current index */
+ if (cur_index < target_index) {
+
+ /* Volume is being increased loop and increase it in 4-5 steps
+ */
+ count = ((target_index - cur_index) * 100 / index_jump);
+ index = cur_index;
+
+ for (i = 1; i <= count; i++) {
+ index = index + (int)(index_jump / 100);
+
+ val =
+ adie_codec_vol_cntrl[vol_type].ch_vol_cntrl_info
+ [chan_index].vol_cntrl_data[index];
+
+ pr_debug("%s: write reg %x val 0x%x\n",
+ __func__, reg, val);
+
+ rc = adie_codec_write(reg, mask, val);
+ if (rc < 0) {
+ pr_err("%s: write reg %x val 0x%x failed\n",
+ __func__, reg, val);
+ return rc;
+ }
+ }
+
+ /*do one final write to take it to the target index level */
+ val =
+ adie_codec_vol_cntrl[vol_type].ch_vol_cntrl_info
+ [chan_index].vol_cntrl_data[target_index];
+
+ pr_debug("%s: write reg %x val 0x%x\n", __func__, reg, val);
+
+ rc = adie_codec_write(reg, mask, val);
+
+ if (rc < 0) {
+ pr_err("%s: write reg %x val 0x%x failed\n",
+ __func__, reg, val);
+ return rc;
+ }
+
+ } else {
+
+ /* Volume is being decreased from the current setting */
+ index = cur_index;
+ /* loop and decrease it in 4-5 steps */
+ count = ((cur_index - target_index) * 100 / index_jump);
+
+ for (i = 1; i <= count; i++) {
+ index = index - (int)(index_jump / 100);
+
+ val =
+ adie_codec_vol_cntrl[vol_type].ch_vol_cntrl_info
+ [chan_index].vol_cntrl_data[index];
+
+ pr_debug("%s: write reg %x val 0x%x\n",
+ __func__, reg, val);
+
+ rc = adie_codec_write(reg, mask, val);
+ if (rc < 0) {
+ pr_err("%s: write reg %x val 0x%x failed\n",
+ __func__, reg, val);
+ return rc;
+ }
+ }
+
+ /* do one final write to take it to the target index level */
+ val =
+ adie_codec_vol_cntrl[vol_type].ch_vol_cntrl_info
+ [chan_index].vol_cntrl_data[target_index];
+
+ pr_debug("%s: write reg %x val 0x%x\n", __func__, reg, val);
+
+ rc = adie_codec_write(reg, mask, val);
+
+ if (rc < 0) {
+ pr_err("%s: write reg %x val 0x%x failed\n",
+ __func__, reg, val);
+ return rc;
+ }
+ }
+ return 0;
+}
+
+static int marimba_adie_codec_set_device_digital_volume(
+ struct adie_codec_path *path_ptr,
+ u32 num_channels, u32 vol_percentage /* in percentage */)
+{
+ enum adie_vol_type vol_type;
+ s32 milli_bel;
+ u32 chan_index;
+ u32 step_index;
+ u32 cur_step_index = 0;
+
+ if (!path_ptr || (path_ptr->curr_stage !=
+ ADIE_CODEC_DIGITAL_ANALOG_READY)) {
+ pr_info("%s: Marimba codec not ready for volume control\n",
+ __func__);
+ return -EPERM;
+ }
+
+ if (num_channels > 2) {
+ pr_err("%s: Marimba codec only supports max two channels\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (path_ptr->profile->path_type == ADIE_CODEC_RX)
+ vol_type = ADIE_CODEC_RX_DIG_VOL;
+ else if (path_ptr->profile->path_type == ADIE_CODEC_TX)
+ vol_type = ADIE_CODEC_TX_DIG_VOL;
+ else {
+ pr_err("%s: invalid device data neither RX nor TX\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ milli_bel = ((adie_codec_vol_cntrl[vol_type].max_mb -
+ adie_codec_vol_cntrl[vol_type].min_mb) *
+ vol_percentage) / 100;
+
+ milli_bel = adie_codec_vol_cntrl[vol_type].min_mb + milli_bel;
+
+ pr_debug("%s: milli bell = %d vol_type = %d vol_percentage = %d"
+ " num_cha = %d \n",
+ __func__, milli_bel, vol_type, vol_percentage, num_channels);
+
+
+ step_index = ((milli_bel
+ - adie_codec_vol_cntrl[vol_type].min_mb
+ + (adie_codec_vol_cntrl[vol_type].step_in_mb / 2))
+ / adie_codec_vol_cntrl[vol_type].step_in_mb);
+
+
+ for (chan_index = 0; chan_index < num_channels; chan_index++) {
+ adie_codec_read_dig_vol(vol_type, chan_index, &cur_step_index);
+
+ pr_debug("%s: cur_step_index = %u current vol = 0x%x\n",
+ __func__, cur_step_index,
+ adie_codec_vol_cntrl[vol_type].ch_vol_cntrl_info
+ [chan_index].vol_cntrl_data[cur_step_index]);
+
+ pr_debug("%s: step index = %u new volume = 0x%x\n",
+ __func__, step_index,
+ adie_codec_vol_cntrl[vol_type].ch_vol_cntrl_info
+ [chan_index].vol_cntrl_data[step_index]);
+
+ adie_codec_set_dig_vol(vol_type, chan_index, cur_step_index,
+ step_index);
+
+ }
+ return 0;
+}
+
+static int marimba_adie_codec_setpath(struct adie_codec_path *path_ptr,
+ u32 freq_plan, u32 osr)
+{
+ int rc = 0;
+ u32 i, freq_idx = 0, freq = 0;
+
+ if ((path_ptr->curr_stage != ADIE_CODEC_DIGITAL_OFF) &&
+ (path_ptr->curr_stage != ADIE_CODEC_FLASH_IMAGE)) {
+ rc = -EBUSY;
+ goto error;
+ }
+
+ for (i = 0; i < path_ptr->profile->setting_sz; i++) {
+ if (path_ptr->profile->settings[i].osr == osr) {
+ if (path_ptr->profile->settings[i].freq_plan >=
+ freq_plan) {
+ if (freq == 0) {
+ freq = path_ptr->profile->settings[i].
+ freq_plan;
+ freq_idx = i;
+ } else if (path_ptr->profile->settings[i].
+ freq_plan < freq) {
+ freq = path_ptr->profile->settings[i].
+ freq_plan;
+ freq_idx = i;
+ }
+ }
+ }
+ }
+
+ if (freq_idx >= path_ptr->profile->setting_sz)
+ rc = -ENODEV;
+ else {
+ path_ptr->hwsetting_idx = freq_idx;
+ path_ptr->stage_idx = 0;
+ }
+
+error:
+ return rc;
+}
+
+static u32 marimba_adie_codec_freq_supported(
+ struct adie_codec_dev_profile *profile,
+ u32 requested_freq)
+{
+ u32 i, rc = -EINVAL;
+
+ for (i = 0; i < profile->setting_sz; i++) {
+ if (profile->settings[i].freq_plan >= requested_freq) {
+ rc = 0;
+ break;
+ }
+ }
+ return rc;
+}
+
+static int marimba_adie_codec_enable_sidetone(
+ struct adie_codec_path *rx_path_ptr,
+ u32 enable)
+{
+ int rc = 0;
+
+ pr_debug("%s()\n", __func__);
+
+ mutex_lock(&adie_codec.lock);
+
+ if (!rx_path_ptr || &adie_codec.path[ADIE_CODEC_RX] != rx_path_ptr) {
+ pr_err("%s: invalid path pointer\n", __func__);
+ rc = -EINVAL;
+ goto error;
+ } else if (rx_path_ptr->curr_stage !=
+ ADIE_CODEC_DIGITAL_ANALOG_READY) {
+ pr_err("%s: bad state\n", __func__);
+ rc = -EPERM;
+ goto error;
+ }
+
+ if (enable)
+ rc = adie_codec_write(MARIMBA_CDC_RX_CTL,
+ MARIMBA_CDC_RX_CTL_ST_EN_MASK,
+ (0x1 << MARIMBA_CDC_RX_CTL_ST_EN_SHFT));
+ else
+ rc = adie_codec_write(MARIMBA_CDC_RX_CTL,
+ MARIMBA_CDC_RX_CTL_ST_EN_MASK, 0);
+
+error:
+ mutex_unlock(&adie_codec.lock);
+ return rc;
+}
+
+static void adie_codec_reach_stage_action(struct adie_codec_path *path_ptr,
+ u32 stage)
+{
+ u32 iter;
+ struct adie_codec_register *reg_info;
+
+ if (stage == ADIE_CODEC_FLASH_IMAGE) {
+ /* perform reimage */
+ for (iter = 0; iter < path_ptr->img.img_sz; iter++) {
+ reg_info = &path_ptr->img.regs[iter];
+ adie_codec_write(reg_info->reg,
+ reg_info->mask, reg_info->val);
+ }
+ }
+}
+
+static int marimba_adie_codec_proceed_stage(struct adie_codec_path *path_ptr,
+ u32 state)
+{
+ int rc = 0, loop_exit = 0;
+ struct adie_codec_action_unit *curr_action;
+ struct adie_codec_hwsetting_entry *setting;
+ u8 reg, mask, val;
+
+ mutex_lock(&adie_codec.lock);
+ setting = &path_ptr->profile->settings[path_ptr->hwsetting_idx];
+ while (!loop_exit) {
+ curr_action = &setting->actions[path_ptr->stage_idx];
+ switch (curr_action->type) {
+ case ADIE_CODEC_ACTION_ENTRY:
+ ADIE_CODEC_UNPACK_ENTRY(curr_action->action,
+ reg, mask, val);
+ adie_codec_write(reg, mask, val);
+ break;
+ case ADIE_CODEC_ACTION_DELAY_WAIT:
+ if (curr_action->action > MAX_MDELAY_US)
+ msleep(curr_action->action/1000);
+ else if (curr_action->action < MIN_MDELAY_US)
+ udelay(curr_action->action);
+ else
+ mdelay(curr_action->action/1000);
+ break;
+ case ADIE_CODEC_ACTION_STAGE_REACHED:
+ adie_codec_reach_stage_action(path_ptr,
+ curr_action->action);
+ if (curr_action->action == state) {
+ path_ptr->curr_stage = state;
+ loop_exit = 1;
+ }
+ break;
+ default:
+ BUG();
+ }
+
+ path_ptr->stage_idx++;
+ if (path_ptr->stage_idx == setting->action_sz)
+ path_ptr->stage_idx = 0;
+ }
+ mutex_unlock(&adie_codec.lock);
+ return rc;
+}
+
+static void marimba_codec_bring_up(void)
+{
+ /* bring up sequence for Marimba codec core
+ * ensure RESET_N = 0 and GDFS_CLAMP_EN=1 -
+ * set GDFS_EN_FEW=1 then GDFS_EN_REST=1 then
+ * GDFS_CLAMP_EN = 0 and finally RESET_N = 1
+ * Marimba codec bring up should use the Marimba
+ * slave address after which the codec slave
+ * address can be used
+ */
+
+ /* Bring up codec */
+ adie_codec_write(0xFF, 0xFF, 0x08);
+
+ /* set GDFS_EN_FEW=1 */
+ adie_codec_write(0xFF, 0xFF, 0x0a);
+
+ /* set GDFS_EN_REST=1 */
+ adie_codec_write(0xFF, 0xFF, 0x0e);
+
+ /* set RESET_N=1 */
+ adie_codec_write(0xFF, 0xFF, 0x07);
+
+ adie_codec_write(0xFF, 0xFF, 0x17);
+
+ /* enable band gap */
+ adie_codec_write(0x03, 0xFF, 0x04);
+
+ /* dither delay selected and dmic gain stage bypassed */
+ adie_codec_write(0x8F, 0xFF, 0x44);
+}
+
+static void marimba_codec_bring_down(void)
+{
+ adie_codec_write(0xFF, 0xFF, 0x07);
+ adie_codec_write(0xFF, 0xFF, 0x06);
+ adie_codec_write(0xFF, 0xFF, 0x0e);
+ adie_codec_write(0xFF, 0xFF, 0x08);
+ adie_codec_write(0x03, 0xFF, 0x00);
+}
+
+static int marimba_adie_codec_open(struct adie_codec_dev_profile *profile,
+ struct adie_codec_path **path_pptr)
+{
+ int rc = 0;
+
+ mutex_lock(&adie_codec.lock);
+
+ if (!profile || !path_pptr) {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ if (adie_codec.path[profile->path_type].profile) {
+ rc = -EBUSY;
+ goto error;
+ }
+
+ if (!adie_codec.ref_cnt) {
+
+ if (adie_codec.codec_pdata &&
+ adie_codec.codec_pdata->marimba_codec_power) {
+
+ rc = adie_codec.codec_pdata->marimba_codec_power(1);
+ if (rc) {
+ pr_err("%s: could not power up marimba "
+ "codec\n", __func__);
+ goto error;
+ }
+ }
+ marimba_codec_bring_up();
+ }
+
+ adie_codec.path[profile->path_type].profile = profile;
+ *path_pptr = (void *) &adie_codec.path[profile->path_type];
+ adie_codec.ref_cnt++;
+ adie_codec.path[profile->path_type].hwsetting_idx = 0;
+ adie_codec.path[profile->path_type].curr_stage = ADIE_CODEC_FLASH_IMAGE;
+ adie_codec.path[profile->path_type].stage_idx = 0;
+
+
+error:
+
+ mutex_unlock(&adie_codec.lock);
+ return rc;
+}
+
+static int marimba_adie_codec_close(struct adie_codec_path *path_ptr)
+{
+ int rc = 0;
+
+ mutex_lock(&adie_codec.lock);
+
+ if (!path_ptr) {
+ rc = -EINVAL;
+ goto error;
+ }
+ if (path_ptr->curr_stage != ADIE_CODEC_DIGITAL_OFF)
+ adie_codec_proceed_stage(path_ptr, ADIE_CODEC_DIGITAL_OFF);
+
+ BUG_ON(!adie_codec.ref_cnt);
+
+ path_ptr->profile = NULL;
+ adie_codec.ref_cnt--;
+
+ if (!adie_codec.ref_cnt) {
+
+ marimba_codec_bring_down();
+
+ if (adie_codec.codec_pdata &&
+ adie_codec.codec_pdata->marimba_codec_power) {
+
+ rc = adie_codec.codec_pdata->marimba_codec_power(0);
+ if (rc) {
+ pr_err("%s: could not power down marimba "
+ "codec\n", __func__);
+ goto error;
+ }
+ }
+ }
+
+error:
+ mutex_unlock(&adie_codec.lock);
+ return rc;
+}
+
+static const struct adie_codec_operations marimba_adie_ops = {
+ .codec_id = MARIMBA_ID,
+ .codec_open = marimba_adie_codec_open,
+ .codec_close = marimba_adie_codec_close,
+ .codec_setpath = marimba_adie_codec_setpath,
+ .codec_proceed_stage = marimba_adie_codec_proceed_stage,
+ .codec_freq_supported = marimba_adie_codec_freq_supported,
+ .codec_enable_sidetone = marimba_adie_codec_enable_sidetone,
+ .codec_set_device_digital_volume =
+ marimba_adie_codec_set_device_digital_volume,
+};
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_marimba_dent;
+static struct dentry *debugfs_peek;
+static struct dentry *debugfs_poke;
+static struct dentry *debugfs_power;
+
+static unsigned char read_data;
+
+static int codec_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static int get_parameters(char *buf, long int *param1, int num_of_par)
+{
+ char *token;
+ int base, cnt;
+
+ token = strsep(&buf, " ");
+
+ for (cnt = 0; cnt < num_of_par; cnt++) {
+ if (token != NULL) {
+ if ((token[1] == 'x') || (token[1] == 'X'))
+ base = 16;
+ else
+ base = 10;
+
+ if (strict_strtoul(token, base, ¶m1[cnt]) != 0)
+ return -EINVAL;
+
+ token = strsep(&buf, " ");
+ }
+ else
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static ssize_t codec_debug_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ char lbuf[8];
+
+ snprintf(lbuf, sizeof(lbuf), "0x%x\n", read_data);
+ return simple_read_from_buffer(ubuf, count, ppos, lbuf, strlen(lbuf));
+}
+
+static ssize_t codec_debug_write(struct file *filp,
+ const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+ char *access_str = filp->private_data;
+ char lbuf[32];
+ int rc;
+ long int param[5];
+
+ if (cnt > sizeof(lbuf) - 1)
+ return -EINVAL;
+
+ rc = copy_from_user(lbuf, ubuf, cnt);
+ if (rc)
+ return -EFAULT;
+
+ lbuf[cnt] = '\0';
+
+ if (!strcmp(access_str, "power")) {
+ if (get_parameters(lbuf, param, 1) == 0) {
+ switch (param[0]) {
+ case 1:
+ adie_codec.codec_pdata->marimba_codec_power(1);
+ marimba_codec_bring_up();
+ break;
+ case 0:
+ marimba_codec_bring_down();
+ adie_codec.codec_pdata->marimba_codec_power(0);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ } else
+ rc = -EINVAL;
+ } else if (!strcmp(access_str, "poke")) {
+ /* write */
+ rc = get_parameters(lbuf, param, 2);
+ if ((param[0] <= 0xFF) && (param[1] <= 0xFF) &&
+ (rc == 0))
+ adie_codec_write(param[0], 0xFF, param[1]);
+ else
+ rc = -EINVAL;
+ } else if (!strcmp(access_str, "peek")) {
+ /* read */
+ rc = get_parameters(lbuf, param, 1);
+ if ((param[0] <= 0xFF) && (rc == 0))
+ adie_codec_read(param[0], &read_data);
+ else
+ rc = -EINVAL;
+ }
+
+ if (rc == 0)
+ rc = cnt;
+ else
+ pr_err("%s: rc = %d\n", __func__, rc);
+
+ return rc;
+}
+
+static const struct file_operations codec_debug_ops = {
+ .open = codec_debug_open,
+ .write = codec_debug_write,
+ .read = codec_debug_read
+};
+#endif
+
+static int marimba_codec_probe(struct platform_device *pdev)
+{
+ int rc;
+
+ adie_codec.pdrv_ptr = platform_get_drvdata(pdev);
+ adie_codec.codec_pdata = pdev->dev.platform_data;
+
+ if (adie_codec.codec_pdata->snddev_profile_init)
+ adie_codec.codec_pdata->snddev_profile_init();
+
+ /* Register the marimba ADIE operations */
+ rc = adie_codec_register_codec_operations(&marimba_adie_ops);
+
+#ifdef CONFIG_DEBUG_FS
+ debugfs_marimba_dent = debugfs_create_dir("msm_adie_codec", 0);
+ if (!IS_ERR(debugfs_marimba_dent)) {
+ debugfs_peek = debugfs_create_file("peek",
+ S_IFREG | S_IRUGO, debugfs_marimba_dent,
+ (void *) "peek", &codec_debug_ops);
+
+ debugfs_poke = debugfs_create_file("poke",
+ S_IFREG | S_IRUGO, debugfs_marimba_dent,
+ (void *) "poke", &codec_debug_ops);
+
+ debugfs_power = debugfs_create_file("power",
+ S_IFREG | S_IRUGO, debugfs_marimba_dent,
+ (void *) "power", &codec_debug_ops);
+ }
+#endif
+ return rc;
+}
+
+static struct platform_driver marimba_codec_driver = {
+ .probe = marimba_codec_probe,
+ .driver = {
+ .name = "marimba_codec",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init marimba_codec_init(void)
+{
+ s32 rc;
+
+ rc = platform_driver_register(&marimba_codec_driver);
+ if (IS_ERR_VALUE(rc))
+ goto error;
+
+ adie_codec.path[ADIE_CODEC_TX].img.regs = adie_codec_tx_regs;
+ adie_codec.path[ADIE_CODEC_TX].img.img_sz =
+ ARRAY_SIZE(adie_codec_tx_regs);
+ adie_codec.path[ADIE_CODEC_RX].img.regs = adie_codec_rx_regs;
+ adie_codec.path[ADIE_CODEC_RX].img.img_sz =
+ ARRAY_SIZE(adie_codec_rx_regs);
+ adie_codec.path[ADIE_CODEC_LB].img.regs = adie_codec_lb_regs;
+ adie_codec.path[ADIE_CODEC_LB].img.img_sz =
+ ARRAY_SIZE(adie_codec_lb_regs);
+ mutex_init(&adie_codec.lock);
+
+error:
+ return rc;
+}
+
+static void __exit marimba_codec_exit(void)
+{
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove(debugfs_peek);
+ debugfs_remove(debugfs_poke);
+ debugfs_remove(debugfs_power);
+ debugfs_remove(debugfs_marimba_dent);
+#endif
+ platform_driver_unregister(&marimba_codec_driver);
+}
+
+module_init(marimba_codec_init);
+module_exit(marimba_codec_exit);
+
+MODULE_DESCRIPTION("Marimba codec driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/marimba-core.c b/drivers/mfd/marimba-core.c
new file mode 100644
index 0000000..fec9ef4
--- /dev/null
+++ b/drivers/mfd/marimba-core.c
@@ -0,0 +1,699 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. 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 and
+ * only 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.
+ *
+ */
+/*
+ * Qualcomm Marimba Core Driver
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+
+#include <linux/i2c.h>
+#include <linux/mfd/marimba.h>
+
+#define MARIMBA_MODE 0x00
+
+#define ADIE_ARRY_SIZE (CHIP_ID_MAX * MARIMBA_NUM_CHILD)
+
+static int marimba_shadow[ADIE_ARRY_SIZE][0xff];
+
+struct marimba marimba_modules[ADIE_ARRY_SIZE];
+
+#define MARIMBA_VERSION_REG 0x11
+#define MARIMBA_MODE_REG 0x00
+
+struct marimba_platform_data *marimba_pdata;
+
+static uint32_t marimba_gpio_count;
+static bool fm_status;
+static bool bt_status;
+
+#ifdef CONFIG_I2C_SSBI
+#define NUM_ADD MARIMBA_NUM_CHILD
+#else
+#define NUM_ADD (MARIMBA_NUM_CHILD - 1)
+#endif
+
+
+/**
+ * marimba_read_bahama_ver - Reads Bahama version.
+ * @param marimba: marimba structure pointer passed by client
+ * @returns result of the operation.
+ */
+int marimba_read_bahama_ver(struct marimba *marimba)
+{
+ int rc;
+ u8 bahama_version;
+
+ rc = marimba_read_bit_mask(marimba, 0x00, &bahama_version, 1, 0x1F);
+ if (rc < 0)
+ return rc;
+ switch (bahama_version) {
+ case 0x08: /* varient of bahama v1 */
+ case 0x10:
+ case 0x00:
+ return BAHAMA_VER_1_0;
+ case 0x09: /* variant of bahama v2 */
+ return BAHAMA_VER_2_0;
+ default:
+ return BAHAMA_VER_UNSUPPORTED;
+ }
+}
+EXPORT_SYMBOL(marimba_read_bahama_ver);
+/**
+ * marimba_ssbi_write - Writes a n bit TSADC register in Marimba
+ * @param marimba: marimba structure pointer passed by client
+ * @param reg: register address
+ * @param value: buffer to be written
+ * @param len: num of bytes
+ * @returns result of the operation.
+ */
+int marimba_ssbi_write(struct marimba *marimba, u16 reg , u8 *value, int len)
+{
+ struct i2c_msg *msg;
+ int ret;
+
+ marimba = &marimba_modules[marimba->mod_id];
+
+ mutex_lock(&marimba->xfer_lock);
+
+ msg = &marimba->xfer_msg[0];
+ msg->addr = reg;
+ msg->flags = 0x0;
+ msg->buf = value;
+ msg->len = len;
+
+ ret = i2c_transfer(marimba->client->adapter, marimba->xfer_msg, 1);
+
+ mutex_unlock(&marimba->xfer_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(marimba_ssbi_write);
+
+/**
+ * marimba_ssbi_read - Reads a n bit TSADC register in Marimba
+ * @param marimba: marimba structure pointer passed by client
+ * @param reg: register address
+ * @param value: ssbi read of the register to be stored
+ * @param len: num of bytes
+ *
+ * @returns result of the operation.
+*/
+int marimba_ssbi_read(struct marimba *marimba, u16 reg, u8 *value, int len)
+{
+ struct i2c_msg *msg;
+ int ret;
+
+ marimba = &marimba_modules[marimba->mod_id];
+
+ mutex_lock(&marimba->xfer_lock);
+
+ msg = &marimba->xfer_msg[0];
+ msg->addr = reg;
+ msg->flags = I2C_M_RD;
+ msg->buf = value;
+ msg->len = len;
+
+ ret = i2c_transfer(marimba->client->adapter, marimba->xfer_msg, 1);
+
+ mutex_unlock(&marimba->xfer_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(marimba_ssbi_read);
+
+/**
+ * marimba_write_bit_mask - Sets n bit register using bit mask
+ * @param marimba: marimba structure pointer passed by client
+ * @param reg: register address
+ * @param value: buffer to be written to the registers
+ * @param num_bytes: n bytes to write
+ * @param mask: bit mask corresponding to the registers
+ *
+ * @returns result of the operation.
+ */
+int marimba_write_bit_mask(struct marimba *marimba, u8 reg, u8 *value,
+ unsigned num_bytes, u8 mask)
+{
+ int ret, i;
+ struct i2c_msg *msg;
+ u8 data[num_bytes + 1];
+ u8 mask_value[num_bytes];
+
+ marimba = &marimba_modules[marimba->mod_id];
+
+ mutex_lock(&marimba->xfer_lock);
+
+ for (i = 0; i < num_bytes; i++)
+ mask_value[i] = (marimba_shadow[marimba->mod_id][reg + i]
+ & ~mask) | (value[i] & mask);
+
+ msg = &marimba->xfer_msg[0];
+ msg->addr = marimba->client->addr;
+ msg->flags = 0;
+ msg->len = num_bytes + 1;
+ msg->buf = data;
+ data[0] = reg;
+ memcpy(data+1, mask_value, num_bytes);
+
+ ret = i2c_transfer(marimba->client->adapter, marimba->xfer_msg, 1);
+
+ /* Try again if the write fails */
+ if (ret != 1)
+ ret = i2c_transfer(marimba->client->adapter,
+ marimba->xfer_msg, 1);
+
+ if (ret == 1) {
+ for (i = 0; i < num_bytes; i++)
+ marimba_shadow[marimba->mod_id][reg + i]
+ = mask_value[i];
+ } else
+ dev_err(&marimba->client->dev, "i2c write failed\n");
+
+ mutex_unlock(&marimba->xfer_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(marimba_write_bit_mask);
+
+/**
+ * marimba_write - Sets n bit register in Marimba
+ * @param marimba: marimba structure pointer passed by client
+ * @param reg: register address
+ * @param value: buffer values to be written
+ * @param num_bytes: n bytes to write
+ *
+ * @returns result of the operation.
+ */
+int marimba_write(struct marimba *marimba, u8 reg, u8 *value,
+ unsigned num_bytes)
+{
+ return marimba_write_bit_mask(marimba, reg, value, num_bytes, 0xff);
+}
+EXPORT_SYMBOL(marimba_write);
+
+/**
+ * marimba_read_bit_mask - Reads a n bit register based on bit mask
+ * @param marimba: marimba structure pointer passed by client
+ * @param reg: register address
+ * @param value: i2c read of the register to be stored
+ * @param num_bytes: n bytes to be read.
+ * @param mask: bit mask concerning its register
+ *
+ * @returns result of the operation.
+*/
+int marimba_read_bit_mask(struct marimba *marimba, u8 reg, u8 *value,
+ unsigned num_bytes, u8 mask)
+{
+ int ret, i;
+
+ struct i2c_msg *msg;
+
+ marimba = &marimba_modules[marimba->mod_id];
+
+ mutex_lock(&marimba->xfer_lock);
+
+ msg = &marimba->xfer_msg[0];
+ msg->addr = marimba->client->addr;
+ msg->len = 1;
+ msg->flags = 0;
+ msg->buf = ®
+
+ msg = &marimba->xfer_msg[1];
+ msg->addr = marimba->client->addr;
+ msg->len = num_bytes;
+ msg->flags = I2C_M_RD;
+ msg->buf = value;
+
+ ret = i2c_transfer(marimba->client->adapter, marimba->xfer_msg, 2);
+
+ /* Try again if read fails first time */
+ if (ret != 2)
+ ret = i2c_transfer(marimba->client->adapter,
+ marimba->xfer_msg, 2);
+
+ if (ret == 2) {
+ for (i = 0; i < num_bytes; i++) {
+ marimba_shadow[marimba->mod_id][reg + i] = value[i];
+ value[i] &= mask;
+ }
+ } else
+ dev_err(&marimba->client->dev, "i2c read failed\n");
+
+ mutex_unlock(&marimba->xfer_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(marimba_read_bit_mask);
+
+/**
+ * marimba_read - Reads n bit registers in Marimba
+ * @param marimba: marimba structure pointer passed by client
+ * @param reg: register address
+ * @param value: i2c read of the register to be stored
+ * @param num_bytes: n bytes to read.
+ * @param mask: bit mask concerning its register
+ *
+ * @returns result of the operation.
+*/
+int marimba_read(struct marimba *marimba, u8 reg, u8 *value, unsigned num_bytes)
+{
+ return marimba_read_bit_mask(marimba, reg, value, num_bytes, 0xff);
+}
+EXPORT_SYMBOL(marimba_read);
+
+int timpani_read(struct marimba *marimba, u8 reg, u8 *value, unsigned num_bytes)
+{
+ return marimba_read_bit_mask(marimba, reg, value, num_bytes, 0xff);
+}
+EXPORT_SYMBOL(timpani_read);
+
+int timpani_write(struct marimba *marimba, u8 reg,
+ u8 *value, unsigned num_bytes)
+{
+ return marimba_write_bit_mask(marimba, reg, value, num_bytes, 0xff);
+}
+EXPORT_SYMBOL(timpani_write);
+
+static int cur_codec_type = -1, cur_adie_type = -1, cur_connv_type = -1;
+static int adie_arry_idx;
+
+int adie_get_detected_codec_type(void)
+{
+ return cur_codec_type;
+}
+EXPORT_SYMBOL(adie_get_detected_codec_type);
+
+int adie_get_detected_connectivity_type(void)
+{
+ return cur_connv_type;
+}
+EXPORT_SYMBOL(adie_get_detected_connectivity_type);
+
+static struct device *
+add_numbered_child(unsigned chip, const char *name, int num, u8 driver_data,
+ void *pdata, unsigned pdata_len)
+{
+ struct platform_device *pdev;
+ struct marimba *marimba = &marimba_modules[chip + adie_arry_idx];
+ int status = 0;
+
+ pdev = platform_device_alloc(name, num);
+ if (!pdev) {
+ status = -ENOMEM;
+ return ERR_PTR(status);
+ }
+
+ pdev->dev.parent = &marimba->client->dev;
+
+ marimba->mod_id = chip + adie_arry_idx;
+
+ platform_set_drvdata(pdev, marimba);
+
+ if (pdata) {
+ status = platform_device_add_data(pdev, pdata, pdata_len);
+ if (status < 0)
+ goto err;
+ }
+
+ status = platform_device_add(pdev);
+ if (status < 0)
+ goto err;
+
+err:
+ if (status < 0) {
+ platform_set_drvdata(pdev, NULL);
+ platform_device_put(pdev);
+ dev_err(&marimba->client->dev, "can't add %s dev\n", name);
+ return ERR_PTR(status);
+ }
+ return &pdev->dev;
+}
+
+static inline struct device *add_child(unsigned chip, const char *name,
+ u8 driver_data, void *pdata, unsigned pdata_len)
+{
+ return add_numbered_child(chip, name, -1, driver_data, pdata,
+ pdata_len);
+}
+
+static int marimba_add_child(struct marimba_platform_data *pdata,
+ u8 driver_data)
+{
+ struct device *child;
+
+ if (cur_adie_type == MARIMBA_ID) {
+ child = add_child(MARIMBA_SLAVE_ID_FM, "marimba_fm",
+ driver_data, pdata->fm, sizeof(*pdata->fm));
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+ } else if ((cur_adie_type == BAHAMA_ID) &&
+ (cur_connv_type == BAHAMA_ID)) {
+ child = add_child(BAHAMA_SLAVE_ID_FM_ID, "marimba_fm",
+ driver_data, pdata->fm, sizeof(*pdata->fm));
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+ }
+
+ /* Add Codec for Marimba and Timpani */
+ if (cur_adie_type == MARIMBA_ID) {
+ child = add_child(MARIMBA_SLAVE_ID_CDC, "marimba_codec",
+ driver_data, pdata->codec, sizeof(*pdata->codec));
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+ } else if (cur_adie_type == TIMPANI_ID) {
+ child = add_child(MARIMBA_SLAVE_ID_CDC, "timpani_codec",
+ driver_data, pdata->codec, sizeof(*pdata->codec));
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+ }
+
+#if defined(CONFIG_I2C_SSBI)
+ if ((pdata->tsadc != NULL) && (cur_adie_type != BAHAMA_ID)) {
+ child = add_child(MARIMBA_ID_TSADC, "marimba_tsadc",
+ driver_data, pdata->tsadc, sizeof(*pdata->tsadc));
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+ }
+#endif
+ return 0;
+}
+
+int marimba_gpio_config(int gpio_value)
+{
+ struct marimba *marimba = &marimba_modules[MARIMBA_SLAVE_ID_MARIMBA];
+ struct marimba_platform_data *pdata = marimba_pdata;
+ int rc = 0;
+
+ /* Clients BT/FM need to manage GPIO 34 on Fusion for its clocks */
+
+ mutex_lock(&marimba->xfer_lock);
+
+ if (gpio_value) {
+ marimba_gpio_count++;
+ if (marimba_gpio_count == 1)
+ rc = pdata->marimba_gpio_config(1);
+ } else {
+ marimba_gpio_count--;
+ if (marimba_gpio_count == 0)
+ rc = pdata->marimba_gpio_config(0);
+ }
+
+ mutex_unlock(&marimba->xfer_lock);
+
+ return rc;
+
+}
+EXPORT_SYMBOL(marimba_gpio_config);
+
+bool marimba_get_fm_status(struct marimba *marimba)
+{
+ bool ret;
+
+ marimba = &marimba_modules[marimba->mod_id];
+
+ mutex_lock(&marimba->xfer_lock);
+
+ ret = fm_status;
+
+ mutex_unlock(&marimba->xfer_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(marimba_get_fm_status);
+
+void marimba_set_fm_status(struct marimba *marimba, bool value)
+{
+ marimba = &marimba_modules[marimba->mod_id];
+
+ mutex_lock(&marimba->xfer_lock);
+
+ fm_status = value;
+
+ mutex_unlock(&marimba->xfer_lock);
+}
+EXPORT_SYMBOL(marimba_set_fm_status);
+
+bool marimba_get_bt_status(struct marimba *marimba)
+{
+ bool ret;
+
+ marimba = &marimba_modules[marimba->mod_id];
+
+ mutex_lock(&marimba->xfer_lock);
+
+ ret = bt_status;
+
+ mutex_unlock(&marimba->xfer_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(marimba_get_bt_status);
+
+void marimba_set_bt_status(struct marimba *marimba, bool value)
+{
+ marimba = &marimba_modules[marimba->mod_id];
+
+ mutex_lock(&marimba->xfer_lock);
+
+ bt_status = value;
+
+ mutex_unlock(&marimba->xfer_lock);
+}
+EXPORT_SYMBOL(marimba_set_bt_status);
+
+static int get_adie_type(void)
+{
+ u8 rd_val;
+ int ret;
+
+ struct marimba *marimba = &marimba_modules[ADIE_ARRY_SIZE - 1];
+
+ marimba->mod_id = ADIE_ARRY_SIZE - 1;
+ /* Enable the Mode for Marimba/Timpani */
+ ret = marimba_read(marimba, MARIMBA_MODE_REG, &rd_val, 1);
+
+ if (ret >= 0) {
+ if (rd_val & 0x80) {
+ cur_adie_type = BAHAMA_ID;
+ return cur_adie_type;
+ } else {
+ ret = marimba_read(marimba,
+ MARIMBA_VERSION_REG, &rd_val, 1);
+ if ((ret >= 0) && (rd_val & 0x20)) {
+ cur_adie_type = TIMPANI_ID;
+ return cur_adie_type;
+ } else if (ret >= 0) {
+ cur_adie_type = MARIMBA_ID;
+ return cur_adie_type;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void marimba_init_reg(struct i2c_client *client, u8 driver_data)
+{
+ struct marimba_platform_data *pdata = client->dev.platform_data;
+ struct marimba *marimba =
+ &marimba_modules[MARIMBA_SLAVE_ID_MARIMBA + adie_arry_idx];
+
+ u8 buf[1];
+
+ buf[0] = 0x10;
+
+ if (cur_adie_type != BAHAMA_ID) {
+ marimba->mod_id = MARIMBA_SLAVE_ID_MARIMBA + adie_arry_idx;
+ /* Enable the Mode for Marimba/Timpani */
+ marimba_write(marimba, MARIMBA_MODE, buf, 1);
+ } else if ((cur_adie_type == BAHAMA_ID) &&
+ (cur_connv_type == BAHAMA_ID)) {
+ marimba->mod_id = MARIMBA_SLAVE_ID_MARIMBA + adie_arry_idx;
+ marimba_write(marimba, BAHAMA_SLAVE_ID_FM_ID,
+ &pdata->slave_id[SLAVE_ID_BAHAMA_FM], 1);
+ /* Configure Bahama core registers (AREG & DREG) */
+ /* with optimal values to eliminate power leakage */
+ if (pdata->bahama_core_config != NULL)
+ pdata->bahama_core_config(cur_adie_type);
+ }
+}
+
+static int marimba_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct marimba_platform_data *pdata = client->dev.platform_data;
+ struct i2c_adapter *ssbi_adap;
+ struct marimba *marimba;
+ int i, status, rc, client_loop, adie_slave_idx_offset;
+ int rc_bahama = 0, rc_marimba = 0;
+
+ if (!pdata) {
+ dev_dbg(&client->dev, "no platform data?\n");
+ return -EINVAL;
+ }
+
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
+ dev_dbg(&client->dev, "can't talk I2C?\n");
+ return -EIO;
+ }
+
+ /* First, identify the codec type */
+ if (pdata->marimba_setup != NULL) {
+ rc_marimba = pdata->marimba_setup();
+ if (rc_marimba)
+ pdata->marimba_shutdown();
+ }
+
+ if (pdata->bahama_setup != NULL &&
+ cur_connv_type != BAHAMA_ID) {
+ rc_bahama = pdata->bahama_setup();
+ if (rc_bahama)
+ pdata->bahama_shutdown(cur_connv_type);
+ }
+
+ if (rc_marimba & rc_bahama)
+ return -EAGAIN;
+
+ marimba = &marimba_modules[ADIE_ARRY_SIZE - 1];
+ marimba->client = client;
+ mutex_init(&marimba->xfer_lock);
+
+ rc = get_adie_type();
+
+ mutex_destroy(&marimba->xfer_lock);
+
+ if (rc < 0) {
+ if (pdata->bahama_setup != NULL)
+ pdata->bahama_shutdown(cur_adie_type);
+ if (pdata->marimba_shutdown != NULL)
+ pdata->marimba_shutdown();
+ return -ENODEV;
+ }
+
+ if (rc < 2) {
+ adie_arry_idx = 0;
+ adie_slave_idx_offset = 0;
+ client_loop = 0;
+ cur_codec_type = rc;
+ if (cur_connv_type < 0)
+ cur_connv_type = rc;
+ if (pdata->bahama_shutdown != NULL)
+ pdata->bahama_shutdown(cur_connv_type);
+ } else {
+ adie_arry_idx = 5;
+ adie_slave_idx_offset = 5;
+ client_loop = 1;
+ cur_connv_type = rc;
+ }
+
+ marimba = &marimba_modules[adie_arry_idx];
+ marimba->client = client;
+ mutex_init(&marimba->xfer_lock);
+
+ for (i = 1; i <= (NUM_ADD - client_loop); i++) {
+ /* Skip adding BT/FM for Timpani */
+ if (i == 1 && rc >= 1)
+ i++;
+ marimba = &marimba_modules[i + adie_arry_idx];
+ if (i != MARIMBA_ID_TSADC)
+ marimba->client = i2c_new_dummy(client->adapter,
+ pdata->slave_id[i + adie_slave_idx_offset]);
+ else if (pdata->tsadc_ssbi_adap) {
+ ssbi_adap = i2c_get_adapter(pdata->tsadc_ssbi_adap);
+ marimba->client = i2c_new_dummy(ssbi_adap,
+ 0x55);
+ } else
+ ssbi_adap = NULL;
+
+ if (!marimba->client) {
+ dev_err(&marimba->client->dev,
+ "can't attach client %d\n", i);
+ status = -ENOMEM;
+ goto fail;
+ }
+ strlcpy(marimba->client->name, id->name,
+ sizeof(marimba->client->name));
+
+ mutex_init(&marimba->xfer_lock);
+ }
+
+ marimba_init_reg(client, id->driver_data);
+
+ status = marimba_add_child(pdata, id->driver_data);
+
+ marimba_pdata = pdata;
+
+ return 0;
+
+fail:
+ return status;
+}
+
+static int __devexit marimba_remove(struct i2c_client *client)
+{
+ int i;
+ struct marimba_platform_data *pdata;
+
+ pdata = client->dev.platform_data;
+ for (i = 0; i <= ADIE_ARRY_SIZE; i++) {
+ struct marimba *marimba = &marimba_modules[i];
+
+ if (marimba->client && marimba->client != client)
+ i2c_unregister_device(marimba->client);
+
+ marimba_modules[i].client = NULL;
+ }
+
+ if (pdata->marimba_shutdown != NULL)
+ pdata->marimba_shutdown();
+
+ return 0;
+}
+
+static struct i2c_device_id marimba_id_table[] = {
+ {"marimba", MARIMBA_ID},
+ {"timpani", TIMPANI_ID},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, marimba_id_table);
+
+static struct i2c_driver marimba_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "marimba-core",
+ },
+ .id_table = marimba_id_table,
+ .probe = marimba_probe,
+ .remove = __devexit_p(marimba_remove),
+};
+
+static int __init marimba_init(void)
+{
+ return i2c_add_driver(&marimba_driver);
+}
+module_init(marimba_init);
+
+static void __exit marimba_exit(void)
+{
+ i2c_del_driver(&marimba_driver);
+}
+module_exit(marimba_exit);
+
+MODULE_DESCRIPTION("Marimba Top level Driver");
+MODULE_ALIAS("platform:marimba-core");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1");
diff --git a/drivers/mfd/marimba-tsadc.c b/drivers/mfd/marimba-tsadc.c
new file mode 100644
index 0000000..8a7b781
--- /dev/null
+++ b/drivers/mfd/marimba-tsadc.c
@@ -0,0 +1,696 @@
+/*
+ * Marimba TSADC driver.
+ *
+ * Copyright (c) 2009-2010, Code Aurora Forum. 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 and
+ * only 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/mfd/marimba.h>
+#include <linux/mfd/marimba-tsadc.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#endif
+
+/* marimba configuration block: TS_CTL0 */
+#define TS_CTL0 0xFF
+#define TS_CTL0_RESET BIT(0)
+#define TS_CTL0_CLK_EN BIT(1)
+#define TS_CTL0_XO_EN BIT(2)
+#define TS_CTL0_EOC_EN BIT(3)
+#define TS_CTL0_PENIRQ_EN BIT(4)
+
+/* TSADC registers */
+#define SSBI_PRESET 0x00
+#define TSHK_DIG_CONFIG 0x4F
+#define TSHK_INTF_CONFIG 0x50
+#define TSHK_SETUP 0x51
+ #define TSHK_SETUP_EN_ADC BIT(0)
+ #define TSHK_SETUP_EN_PIRQ BIT(7)
+#define TSHK_PARAM 0x52
+#define TSHK_DATA_RD 0x53
+#define TSHK_STATUS 0x54
+#define TSHK_SETUP2 0x55
+#define TSHK_RSV1 0x56
+ #define TSHK_RSV1_PRECHARGE_EN BIT(0)
+#define TSHK_COMMAND 0x57
+#define TSHK_PARAM2 0x58
+ #define TSHK_INPUT_CLK_MASK 0x3F
+ #define TSHK_SAMPLE_PRD_MASK 0xC7
+ #define TSHK_INPUT_CLK_SHIFT 0x6
+ #define TSHK_SAMPLE_PRD_SHIFT 0x3
+#define TSHK_PARAM3 0x59
+ #define TSHK_PARAM3_MODE_MASK 0xFC
+ #define TSHK_PARAM3_PRE_CHG_SHIFT (5)
+ #define TSHK_PARAM3_STABIZ_SHIFT (2)
+ #define TSHK_STABLE_TIME_MASK 0xE3
+ #define TSHK_PRECHG_TIME_MASK 0x1F
+#define TSHK_PARAM4 0x5A
+#define TSHK_RSV2 0x5B
+#define TSHK_RSV3 0x5C
+#define TSHK_RSV4 0x5D
+#define TSHK_RSV5 0x5E
+
+struct marimba_tsadc_client {
+ unsigned int is_ts;
+ struct platform_device *pdev;
+};
+
+struct marimba_tsadc {
+ struct marimba *marimba;
+ struct device *dev;
+ struct marimba_tsadc_platform_data *pdata;
+ struct clk *codec_ssbi;
+ struct device *child_tssc;
+ bool clk_enabled;
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+ struct early_suspend early_suspend;
+#endif
+};
+
+static struct marimba_tsadc *tsadc_dev;
+
+static int marimba_write_u8(struct marimba_tsadc *tsadc, u8 reg, u8 data)
+{
+ int rc;
+
+ tsadc->marimba->mod_id = MARIMBA_SLAVE_ID_MARIMBA;
+ rc = marimba_write(tsadc->marimba, reg, &data, 1);
+
+ if (!rc)
+ dev_warn(tsadc->dev, "Error writing marimba reg %X - ret %X\n",
+ reg, data);
+ return 0;
+}
+
+static int marimba_tsadc_write(struct marimba_tsadc *tsadc, u8 reg, u8 data)
+{
+ int rc;
+
+ tsadc->marimba->mod_id = MARIMBA_ID_TSADC;
+
+ rc = marimba_ssbi_write(tsadc->marimba, reg, &data, 1);
+ if (!rc)
+ dev_warn(tsadc->dev, "Error writing marimba reg %X - ret %X\n",
+ reg, data);
+ return rc;
+}
+
+static int marimba_tsadc_shutdown(struct marimba_tsadc *tsadc)
+{
+ u8 val;
+ int rc;
+
+ /* force reset */
+ val = TS_CTL0_XO_EN | TS_CTL0_EOC_EN | TS_CTL0_PENIRQ_EN |
+ TS_CTL0_CLK_EN;
+ rc = marimba_write_u8(tsadc, TS_CTL0, val);
+ if (rc < 0)
+ return rc;
+
+ /* disable xo, clock */
+ val = TS_CTL0_PENIRQ_EN | TS_CTL0_EOC_EN;
+ rc = marimba_write_u8(tsadc, TS_CTL0, val);
+ if (rc < 0)
+ return rc;
+
+ /* de-vote S2 1.3v */
+ if (tsadc->pdata->level_vote)
+ /* REVISIT: Ignore error for level_vote(0) for now*/
+ tsadc->pdata->level_vote(0);
+
+ return 0;
+}
+
+static int marimba_tsadc_startup(struct marimba_tsadc *tsadc)
+{
+ u8 val;
+ int rc = 0;
+
+ /* vote for S2 1.3v */
+ if (tsadc->pdata->level_vote) {
+ rc = tsadc->pdata->level_vote(1);
+ if (rc < 0)
+ return rc;
+ }
+
+ /* disable XO, clock and output enables */
+ rc = marimba_write_u8(tsadc, TS_CTL0, 0x00);
+ if (rc < 0)
+ goto fail_marimba_write;
+
+ /* Enable output enables */
+ val = TS_CTL0_XO_EN | TS_CTL0_EOC_EN | TS_CTL0_PENIRQ_EN;
+ rc = marimba_write_u8(tsadc, TS_CTL0, val);
+ if (rc < 0)
+ goto fail_marimba_write;
+
+ /* Enable clock */
+ val = val | TS_CTL0_CLK_EN;
+ rc = marimba_write_u8(tsadc, TS_CTL0, val);
+ if (rc < 0)
+ goto fail_marimba_write;
+
+ /* remove reset */
+ val = val | TS_CTL0_RESET;
+ rc = marimba_write_u8(tsadc, TS_CTL0, val);
+ if (rc < 0)
+ goto fail_marimba_write;
+
+ return 0;
+
+fail_marimba_write:
+ if (tsadc->pdata->level_vote)
+ /* REVISIT: Ignore error for level_vote(0) for now*/
+ tsadc->pdata->level_vote(0);
+ return rc;
+}
+
+
+static int marimba_tsadc_configure(struct marimba_tsadc *tsadc)
+{
+ u8 rsv1 = 0, setup = 0, i, count = 0;
+ u8 param2 = 0, param3 = 0;
+ unsigned long val;
+ int rc;
+
+ rc = marimba_tsadc_write(tsadc, SSBI_PRESET, 0x00);
+ if (rc < 0)
+ return rc;
+
+ if (!tsadc->pdata)
+ return -EINVAL;
+
+ /* Configure RSV1 register*/
+ if (tsadc->pdata->tsadc_prechg_en == true)
+ rsv1 |= TSHK_RSV1_PRECHARGE_EN;
+ else
+ rsv1 &= ~TSHK_RSV1_PRECHARGE_EN;
+
+ /* Set RSV1 register*/
+ rc = marimba_tsadc_write(tsadc, TSHK_RSV1, rsv1);
+ if (rc < 0)
+ return rc;
+
+ /* Configure PARAM2 register */
+ /* Input clk */
+ val = tsadc->pdata->params2.input_clk_khz;
+ param2 &= TSHK_INPUT_CLK_MASK;
+ val /= 600;
+ if (val >= 1 && val <= 8 && !(val & (val - 1))) {
+ /* Input clk can be .6, 1.2, 2.4, 4.8Mhz */
+ if (val % 4 != 0)
+ param2 = (4 - (val % 4)) << TSHK_INPUT_CLK_SHIFT;
+ else
+ param2 = ((val / 4) - 1) << TSHK_INPUT_CLK_SHIFT;
+ } else /* Configure the default clk 2.4Mhz */
+ param2 = 0x00 << TSHK_INPUT_CLK_SHIFT;
+
+ /* Sample period */
+ param2 &= TSHK_SAMPLE_PRD_MASK;
+ param2 |= tsadc->pdata->params2.sample_prd << TSHK_SAMPLE_PRD_SHIFT;
+
+ /* Write PARAM2 register */
+ rc = marimba_tsadc_write(tsadc, TSHK_PARAM2, param2);
+ if (rc < 0)
+ return rc;
+
+ /* REVISIT: If Precharge time, stabilization time > 409.6us */
+ /* Configure PARAM3 register */
+ val = tsadc->pdata->params3.prechg_time_nsecs;
+ param3 &= TSHK_PRECHG_TIME_MASK;
+ val /= 6400;
+ if (val >= 1 && val <= 64 && !(val & (val - 1))) {
+ count = 0;
+ while ((val = val >> 1) != 0)
+ count++;
+ param3 |= count << TSHK_PARAM3_PRE_CHG_SHIFT;
+ } else /* Set default value if the input is wrong */
+ param3 |= 0x00 << TSHK_PARAM3_PRE_CHG_SHIFT;
+
+ val = tsadc->pdata->params3.stable_time_nsecs;
+ param3 &= TSHK_STABLE_TIME_MASK;
+ val /= 6400;
+ if (val >= 1 && val <= 64 && !(val & (val - 1))) {
+ count = 0;
+ while ((val = val >> 1) != 0)
+ count++;
+ param3 |= count << TSHK_PARAM3_STABIZ_SHIFT;
+ } else /* Set default value if the input is wrong */
+ param3 |= 0x00 << TSHK_PARAM3_STABIZ_SHIFT;
+
+ /* Get TSADC mode */
+ val = tsadc->pdata->params3.tsadc_test_mode;
+ param3 &= TSHK_PARAM3_MODE_MASK;
+ if (val == 0)
+ param3 |= 0x00;
+ else
+ for (i = 0; i < 3 ; i++) {
+ if (((val + i) % 39322) == 0) {
+ param3 |= (i + 1);
+ break;
+ }
+ }
+ if (i == 3) /* Set to normal mode if input is wrong */
+ param3 |= 0x00;
+
+ rc = marimba_tsadc_write(tsadc, TSHK_PARAM3, param3);
+ if (rc < 0)
+ return rc;
+
+ /* Configure TSHK SETUP Register */
+ if (tsadc->pdata->setup.pen_irq_en == true)
+ setup |= TSHK_SETUP_EN_PIRQ;
+ else
+ setup &= ~TSHK_SETUP_EN_PIRQ;
+
+ if (tsadc->pdata->setup.tsadc_en == true)
+ setup |= TSHK_SETUP_EN_ADC;
+ else
+ setup &= ~TSHK_SETUP_EN_ADC;
+
+ /* Enable signals to ADC, pen irq assertion */
+ rc = marimba_tsadc_write(tsadc, TSHK_SETUP, setup);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+int marimba_tsadc_start(struct marimba_tsadc_client *client)
+{
+ int rc = 0;
+
+ if (!client) {
+ pr_err("%s: Not a valid client\n", __func__);
+ return -ENODEV;
+ }
+
+ if (!tsadc_dev) {
+ dev_err(&client->pdev->dev,
+ "%s: No tsadc device available\n", __func__);
+ return -ENODEV;
+ }
+
+ /* REVISIT - add locks */
+ if (client->is_ts) {
+ rc = marimba_tsadc_startup(tsadc_dev);
+ if (rc < 0)
+ goto fail_tsadc_startup;
+ rc = marimba_tsadc_configure(tsadc_dev);
+ if (rc < 0)
+ goto fail_tsadc_conf;
+ }
+
+ return 0;
+fail_tsadc_conf:
+ marimba_tsadc_shutdown(tsadc_dev);
+fail_tsadc_startup:
+ return rc;
+}
+EXPORT_SYMBOL(marimba_tsadc_start);
+
+struct marimba_tsadc_client *
+marimba_tsadc_register(struct platform_device *pdev, unsigned int is_ts)
+{
+ struct marimba_tsadc_client *client;
+
+ if (!pdev) {
+ pr_err("%s: valid platform device pointer please\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (!is_ts) {
+ dev_err(&pdev->dev, "%s: only TS right now\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (!tsadc_dev) {
+ dev_err(&pdev->dev,
+ "%s: No tsadc device available\n", __func__);
+ return ERR_PTR(-ENODEV);
+ }
+
+ client = kzalloc(sizeof *client, GFP_KERNEL);
+ if (!client)
+ return ERR_PTR(-ENOMEM);
+
+ client->pdev = pdev;
+ client->is_ts = is_ts;
+
+ return client;
+}
+EXPORT_SYMBOL(marimba_tsadc_register);
+
+void marimba_tsadc_unregister(struct marimba_tsadc_client *client)
+{
+ if (client->is_ts)
+ marimba_tsadc_shutdown(tsadc_dev);
+ kfree(client);
+}
+EXPORT_SYMBOL(marimba_tsadc_unregister);
+
+static struct resource resources_tssc[] = {
+ {
+ .start = 0xAD300000,
+ .end = 0xAD300000 + SZ_4K - 1,
+ .name = "tssc",
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = 55,
+ .end = 55,
+ .name = "tssc1",
+ .flags = IORESOURCE_IRQ | IRQF_TRIGGER_RISING,
+ },
+ {
+ .start = 56,
+ .end = 56,
+ .name = "tssc2",
+ .flags = IORESOURCE_IRQ | IRQF_TRIGGER_RISING,
+ },
+};
+
+static struct device *
+marimba_add_tssc_subdev(struct device *parent, const char *name, int num,
+ struct resource *resources, int num_resources,
+ void *pdata, int pdata_len)
+{
+ struct platform_device *pdev;
+ int status;
+
+ pdev = platform_device_alloc(name, num);
+ if (!pdev) {
+ dev_dbg(parent, "can't alloc dev\n");
+ status = -ENOMEM;
+ goto err;
+ }
+
+ pdev->dev.parent = parent;
+
+ if (pdata) {
+ status = platform_device_add_data(pdev, pdata, pdata_len);
+ if (status < 0) {
+ dev_dbg(&pdev->dev, "can't add platform_data\n");
+ goto err;
+ }
+ }
+
+ status = platform_device_add_resources(pdev, resources, num_resources);
+ if (status < 0) {
+ dev_dbg(&pdev->dev, "can't add resources\n");
+ goto err;
+ }
+
+ status = platform_device_add(pdev);
+
+err:
+ if (status < 0) {
+ platform_device_put(pdev);
+ dev_err(parent, "can't add %s dev\n", name);
+ return ERR_PTR(status);
+ }
+ return &pdev->dev;
+}
+
+#ifdef CONFIG_PM
+static int
+marimba_tsadc_suspend(struct device *dev)
+{
+ int rc = 0, ret = 0;
+ struct marimba_tsadc *tsadc = dev_get_drvdata(dev);
+
+ if (tsadc->clk_enabled == true) {
+ clk_disable(tsadc->codec_ssbi);
+ tsadc->clk_enabled = false;
+ }
+
+ if (!(device_may_wakeup(dev) &&
+ device_may_wakeup(tsadc->child_tssc))) {
+ rc = marimba_tsadc_shutdown(tsadc);
+ if (rc < 0) {
+ pr_err("%s: Unable to shutdown TSADC\n", __func__);
+ goto fail_shutdown;
+ }
+
+ if (tsadc->pdata->marimba_tsadc_power) {
+ rc = tsadc->pdata->marimba_tsadc_power(0);
+ if (rc < 0)
+ goto fail_tsadc_power;
+ }
+ }
+ return rc;
+
+fail_tsadc_power:
+ marimba_tsadc_startup(tsadc_dev);
+ marimba_tsadc_configure(tsadc_dev);
+fail_shutdown:
+ if (tsadc->clk_enabled == false) {
+ ret = clk_enable(tsadc->codec_ssbi);
+ if (ret == 0)
+ tsadc->clk_enabled = true;
+ }
+ return rc;
+}
+
+static int marimba_tsadc_resume(struct device *dev)
+{
+ int rc = 0;
+ struct marimba_tsadc *tsadc = dev_get_drvdata(dev);
+
+ if (tsadc->clk_enabled == false) {
+ rc = clk_enable(tsadc->codec_ssbi);
+ if (rc != 0) {
+ pr_err("%s: Clk enable failed\n", __func__);
+ return rc;
+ }
+ tsadc->clk_enabled = true;
+ }
+
+ if (!(device_may_wakeup(dev) &&
+ device_may_wakeup(tsadc->child_tssc))) {
+ if (tsadc->pdata->marimba_tsadc_power) {
+ rc = tsadc->pdata->marimba_tsadc_power(1);
+ if (rc) {
+ pr_err("%s: Unable to power on TSADC \n",
+ __func__);
+ goto fail_tsadc_power;
+ }
+ }
+
+ rc = marimba_tsadc_startup(tsadc_dev);
+ if (rc < 0) {
+ pr_err("%s: Unable to startup TSADC\n", __func__);
+ goto fail_tsadc_startup;
+ }
+
+ rc = marimba_tsadc_configure(tsadc_dev);
+ if (rc < 0) {
+ pr_err("%s: Unable to configure TSADC\n", __func__);
+ goto fail_tsadc_configure;
+ }
+ }
+ return rc;
+
+fail_tsadc_configure:
+ marimba_tsadc_shutdown(tsadc_dev);
+fail_tsadc_startup:
+ if (tsadc->pdata->marimba_tsadc_power)
+ tsadc->pdata->marimba_tsadc_power(0);
+fail_tsadc_power:
+ if (tsadc->clk_enabled == true) {
+ clk_disable(tsadc->codec_ssbi);
+ tsadc->clk_enabled = false;
+ }
+ return rc;
+}
+
+static struct dev_pm_ops tsadc_pm_ops = {
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .suspend = marimba_tsadc_suspend,
+ .resume = marimba_tsadc_resume,
+#endif
+};
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void marimba_tsadc_early_suspend(struct early_suspend *h)
+{
+ struct marimba_tsadc *tsadc = container_of(h, struct marimba_tsadc,
+ early_suspend);
+
+ marimba_tsadc_suspend(tsadc->dev);
+}
+
+static void marimba_tsadc_late_resume(struct early_suspend *h)
+{
+ struct marimba_tsadc *tsadc = container_of(h, struct marimba_tsadc,
+ early_suspend);
+
+ marimba_tsadc_resume(tsadc->dev);
+}
+#endif
+
+static int __devinit marimba_tsadc_probe(struct platform_device *pdev)
+{
+ struct marimba *marimba = platform_get_drvdata(pdev);
+ struct marimba_tsadc *tsadc;
+ struct marimba_tsadc_platform_data *pdata = pdev->dev.platform_data;
+ int rc = 0;
+ struct device *child;
+
+ printk("%s\n", __func__);
+
+ if (!pdata) {
+ dev_dbg(&pdev->dev, "no tsadc platform data?\n");
+ return -EINVAL;
+ }
+
+ tsadc = kzalloc(sizeof *tsadc, GFP_KERNEL);
+ if (!tsadc)
+ return -ENOMEM;
+
+ tsadc->marimba = marimba;
+ tsadc->dev = &pdev->dev;
+ tsadc->pdata = pdata;
+
+ platform_set_drvdata(pdev, tsadc);
+
+ if (tsadc->pdata->init) {
+ rc = tsadc->pdata->init();
+ if (rc < 0)
+ goto fail_tsadc_init;
+ }
+
+ if (tsadc->pdata->marimba_tsadc_power) {
+ rc = tsadc->pdata->marimba_tsadc_power(1);
+ if (rc) {
+ pr_err("%s: Unable to power up TSADC \n", __func__);
+ goto fail_tsadc_power;
+ }
+ }
+
+ tsadc->codec_ssbi = clk_get(NULL, "codec_ssbi_clk");
+ if (IS_ERR(tsadc->codec_ssbi)) {
+ rc = PTR_ERR(tsadc->codec_ssbi);
+ goto fail_clk_get;
+ }
+ rc = clk_enable(tsadc->codec_ssbi);
+ if (rc != 0)
+ goto fail_clk_enable;
+
+ tsadc->clk_enabled = true;
+
+ child = marimba_add_tssc_subdev(&pdev->dev, "msm_touchscreen", -1,
+ resources_tssc, ARRAY_SIZE(resources_tssc),
+ pdata->tssc_data, sizeof(*pdata->tssc_data));
+
+ if (IS_ERR(child)) {
+ rc = PTR_ERR(child);
+ goto fail_add_subdev;
+ }
+
+ tsadc->child_tssc = child;
+ platform_set_drvdata(pdev, tsadc);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ tsadc->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +
+ TSADC_SUSPEND_LEVEL;
+ tsadc->early_suspend.suspend = marimba_tsadc_early_suspend;
+ tsadc->early_suspend.resume = marimba_tsadc_late_resume;
+ register_early_suspend(&tsadc->early_suspend);
+#endif
+
+ tsadc_dev = tsadc;
+ device_init_wakeup(&pdev->dev, pdata->can_wakeup);
+
+ return rc;
+
+fail_add_subdev:
+ clk_disable(tsadc->codec_ssbi);
+
+fail_clk_enable:
+ clk_put(tsadc->codec_ssbi);
+
+fail_clk_get:
+ if (tsadc->pdata->marimba_tsadc_power)
+ rc = tsadc->pdata->marimba_tsadc_power(0);
+fail_tsadc_power:
+ if (tsadc->pdata->exit)
+ rc = tsadc->pdata->exit();
+fail_tsadc_init:
+ kfree(tsadc);
+ return rc;
+}
+
+static int __devexit marimba_tsadc_remove(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct marimba_tsadc *tsadc = platform_get_drvdata(pdev);
+
+ device_init_wakeup(&pdev->dev, 0);
+
+ if (tsadc->clk_enabled == true)
+ clk_disable(tsadc->codec_ssbi);
+
+ clk_put(tsadc->codec_ssbi);
+
+ if (tsadc->pdata->exit)
+ rc = tsadc->pdata->exit();
+
+ if (tsadc->pdata->marimba_tsadc_power)
+ rc = tsadc->pdata->marimba_tsadc_power(0);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&tsadc->early_suspend);
+#endif
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(tsadc);
+ return rc;
+}
+
+static struct platform_driver tsadc_driver = {
+ .probe = marimba_tsadc_probe,
+ .remove = __devexit_p(marimba_tsadc_remove),
+ .driver = {
+ .name = "marimba_tsadc",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &tsadc_pm_ops,
+#endif
+ },
+};
+
+static int __init marimba_tsadc_init(void)
+{
+ return platform_driver_register(&tsadc_driver);
+}
+device_initcall(marimba_tsadc_init);
+
+static void __exit marimba_tsadc_exit(void)
+{
+ return platform_driver_unregister(&tsadc_driver);
+}
+module_exit(marimba_tsadc_exit);
+
+MODULE_DESCRIPTION("Marimba TSADC driver");
+MODULE_VERSION("0.1");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:marimba_tsadc");
diff --git a/drivers/mfd/msm-adie-codec.c b/drivers/mfd/msm-adie-codec.c
new file mode 100644
index 0000000..d9414ed
--- /dev/null
+++ b/drivers/mfd/msm-adie-codec.c
@@ -0,0 +1,196 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 and
+ * only 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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/msm-adie-codec.h>
+#include <linux/mfd/marimba.h>
+
+static const struct adie_codec_operations *cur_adie_ops;
+
+int adie_codec_register_codec_operations(
+ const struct adie_codec_operations *adie_ops)
+{
+ if (adie_ops == NULL)
+ return -EINVAL;
+
+ if (adie_ops->codec_id != adie_get_detected_codec_type())
+ return -EINVAL;
+
+ cur_adie_ops = adie_ops;
+ pr_info("%s: codec type %d\n", __func__, adie_ops->codec_id);
+ return 0;
+}
+
+int adie_codec_open(struct adie_codec_dev_profile *profile,
+ struct adie_codec_path **path_pptr)
+{
+ int rc = -EPERM;
+
+ if (cur_adie_ops != NULL) {
+ if (cur_adie_ops->codec_open != NULL)
+ rc = cur_adie_ops->codec_open(profile, path_pptr);
+ } else
+ rc = -ENODEV;
+
+ return rc;
+}
+EXPORT_SYMBOL(adie_codec_open);
+
+int adie_codec_close(struct adie_codec_path *path_ptr)
+{
+ int rc = -EPERM;
+
+ if (cur_adie_ops != NULL) {
+ if (cur_adie_ops->codec_close != NULL)
+ rc = cur_adie_ops->codec_close(path_ptr);
+ } else
+ rc = -ENODEV;
+
+ return rc;
+}
+EXPORT_SYMBOL(adie_codec_close);
+
+int adie_codec_set_device_digital_volume(struct adie_codec_path *path_ptr,
+ u32 num_channels, u32 vol_percentage /* in percentage */)
+{
+ int rc = -EPERM;
+
+ if (cur_adie_ops != NULL) {
+ if (cur_adie_ops->codec_set_device_digital_volume != NULL) {
+ rc = cur_adie_ops->codec_set_device_digital_volume(
+ path_ptr,
+ num_channels,
+ vol_percentage);
+ }
+ } else
+ rc = -ENODEV;
+
+ return rc;
+}
+EXPORT_SYMBOL(adie_codec_set_device_digital_volume);
+
+int adie_codec_set_device_analog_volume(struct adie_codec_path *path_ptr,
+ u32 num_channels, u32 volume /* in percentage */)
+{
+ int rc = -EPERM;
+
+ if (cur_adie_ops != NULL) {
+ if (cur_adie_ops->codec_set_device_analog_volume != NULL) {
+ rc = cur_adie_ops->codec_set_device_analog_volume(
+ path_ptr,
+ num_channels,
+ volume);
+ }
+ } else
+ rc = -ENODEV;
+
+ return rc;
+}
+EXPORT_SYMBOL(adie_codec_set_device_analog_volume);
+
+int adie_codec_setpath(struct adie_codec_path *path_ptr, u32 freq_plan, u32 osr)
+{
+ int rc = -EPERM;
+
+ if (cur_adie_ops != NULL) {
+ if (cur_adie_ops->codec_setpath != NULL) {
+ rc = cur_adie_ops->codec_setpath(path_ptr,
+ freq_plan,
+ osr);
+ }
+ } else
+ rc = -ENODEV;
+
+ return rc;
+}
+EXPORT_SYMBOL(adie_codec_setpath);
+
+u32 adie_codec_freq_supported(struct adie_codec_dev_profile *profile,
+ u32 requested_freq)
+{
+ int rc = -EPERM;
+
+ if (cur_adie_ops != NULL) {
+ if (cur_adie_ops->codec_freq_supported != NULL)
+ rc = cur_adie_ops->codec_freq_supported(profile,
+ requested_freq);
+ } else
+ rc = -ENODEV;
+
+ return rc;
+}
+EXPORT_SYMBOL(adie_codec_freq_supported);
+
+int adie_codec_enable_sidetone(struct adie_codec_path *rx_path_ptr,
+ u32 enable)
+{
+ int rc = -EPERM;
+
+ if (cur_adie_ops != NULL) {
+ if (cur_adie_ops->codec_enable_sidetone != NULL)
+ rc = cur_adie_ops->codec_enable_sidetone(rx_path_ptr,
+ enable);
+ } else
+ rc = -ENODEV;
+
+ return rc;
+}
+EXPORT_SYMBOL(adie_codec_enable_sidetone);
+
+int adie_codec_enable_anc(struct adie_codec_path *rx_path_ptr,
+ u32 enable, struct adie_codec_anc_data *calibration_writes)
+{
+ int rc = -EPERM;
+
+ if (cur_adie_ops != NULL) {
+ if (cur_adie_ops->codec_enable_anc != NULL)
+ rc = cur_adie_ops->codec_enable_anc(rx_path_ptr,
+ enable, calibration_writes);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(adie_codec_enable_anc);
+
+int adie_codec_proceed_stage(struct adie_codec_path *path_ptr, u32 state)
+{
+ int rc = -EPERM;
+
+ if (cur_adie_ops != NULL) {
+ if (cur_adie_ops->codec_proceed_stage != NULL)
+ rc = cur_adie_ops->codec_proceed_stage(path_ptr,
+ state);
+ } else
+ rc = -ENODEV;
+
+ return rc;
+}
+EXPORT_SYMBOL(adie_codec_proceed_stage);
+
+int adie_codec_set_master_mode(struct adie_codec_path *path_ptr, u8 master)
+{
+ int rc = -EPERM;
+
+ if (cur_adie_ops != NULL) {
+ if (cur_adie_ops->codec_set_master_mode != NULL)
+ rc = cur_adie_ops->codec_set_master_mode(path_ptr,
+ master);
+ } else
+ rc = -ENODEV;
+
+ return rc;
+}
+EXPORT_SYMBOL(adie_codec_set_master_mode);
+
+
diff --git a/drivers/mfd/msmproc_adc.c b/drivers/mfd/msmproc_adc.c
new file mode 100644
index 0000000..a47486a
--- /dev/null
+++ b/drivers/mfd/msmproc_adc.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. 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 and
+ * only 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.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/mfd/pm8921-adc.h>
+#define KELVINMIL_DEGMIL 273160
+#define PM8921_ADC_SLOPE 10
+#define PM8921_ADC_CODE_SCALE 24576
+
+static const struct pm8921_adc_map_pt adcmap_batttherm[] = {
+ {2020, -30},
+ {1923, -20},
+ {1796, -10},
+ {1640, 0},
+ {1459, 10},
+ {1260, 20},
+ {1159, 25},
+ {1059, 30},
+ {871, 40},
+ {706, 50},
+ {567, 60},
+ {453, 70},
+ {364, 80}
+};
+
+static const struct pm8921_adc_map_pt adcmap_ntcg_104ef_104fb[] = {
+ {696483, -40960},
+ {649148, -39936},
+ {605368, -38912},
+ {564809, -37888},
+ {527215, -36864},
+ {492322, -35840},
+ {460007, -34816},
+ {429982, -33792},
+ {402099, -32768},
+ {376192, -31744},
+ {352075, -30720},
+ {329714, -29696},
+ {308876, -28672},
+ {289480, -27648},
+ {271417, -26624},
+ {254574, -25600},
+ {238903, -24576},
+ {224276, -23552},
+ {210631, -22528},
+ {197896, -21504},
+ {186007, -20480},
+ {174899, -19456},
+ {164521, -18432},
+ {154818, -17408},
+ {145744, -16384},
+ {137265, -15360},
+ {129307, -14336},
+ {121866, -13312},
+ {114896, -12288},
+ {108365, -11264},
+ {102252, -10240},
+ {96499, -9216},
+ {91111, -8192},
+ {86055, -7168},
+ {81308, -6144},
+ {76857, -5120},
+ {72660, -4096},
+ {68722, -3072},
+ {65020, -2048},
+ {61538, -1024},
+ {58261, 0},
+ {55177, 1024},
+ {52274, 2048},
+ {49538, 3072},
+ {46962, 4096},
+ {44531, 5120},
+ {42243, 6144},
+ {40083, 7168},
+ {38045, 8192},
+ {36122, 9216},
+ {34308, 10240},
+ {32592, 11264},
+ {30972, 12288},
+ {29442, 13312},
+ {27995, 14336},
+ {26624, 15360},
+ {25333, 16384},
+ {24109, 17408},
+ {22951, 18432},
+ {21854, 19456},
+ {20807, 20480},
+ {19831, 21504},
+ {18899, 22528},
+ {18016, 23552},
+ {17178, 24576},
+ {16384, 25600},
+ {15631, 26624},
+ {14916, 27648},
+ {14237, 28672},
+ {13593, 29696},
+ {12976, 30720},
+ {12400, 31744},
+ {11848, 32768},
+ {11324, 33792},
+ {10825, 34816},
+ {10354, 35840},
+ {9900, 36864},
+ {9471, 37888},
+ {9062, 38912},
+ {8674, 39936},
+ {8306, 40960},
+ {7951, 41984},
+ {7616, 43008},
+ {7296, 44032},
+ {6991, 45056},
+ {6701, 46080},
+ {6424, 47104},
+ {6160, 48128},
+ {5908, 49152},
+ {5667, 50176},
+ {5439, 51200},
+ {5219, 52224},
+ {5010, 53248},
+ {4810, 54272},
+ {4619, 55296},
+ {4440, 56320},
+ {4263, 57344},
+ {4097, 58368},
+ {3938, 59392},
+ {3785, 60416},
+ {3637, 61440},
+ {3501, 62464},
+ {3368, 63488},
+ {3240, 64512},
+ {3118, 65536},
+ {2998, 66560},
+ {2889, 67584},
+ {2782, 68608},
+ {2680, 69632},
+ {2581, 70656},
+ {2490, 71680},
+ {2397, 72704},
+ {2310, 73728},
+ {2227, 74752},
+ {2147, 75776},
+ {2064, 76800},
+ {1998, 77824},
+ {1927, 78848},
+ {1860, 79872},
+ {1795, 80896},
+ {1736, 81920},
+ {1673, 82944},
+ {1615, 83968},
+ {1560, 84992},
+ {1507, 86016},
+ {1456, 87040},
+ {1407, 88064},
+ {1360, 89088},
+ {1314, 90112},
+ {1271, 91136},
+ {1228, 92160},
+ {1189, 93184},
+ {1150, 94208},
+ {1112, 95232},
+ {1076, 96256},
+ {1042, 97280},
+ {1008, 98304},
+ {976, 99328},
+ {945, 100352},
+ {915, 101376},
+ {886, 102400},
+ {859, 103424},
+ {832, 104448},
+ {807, 105472},
+ {782, 106496},
+ {756, 107520},
+ {735, 108544},
+ {712, 109568},
+ {691, 110592},
+ {670, 111616},
+ {650, 112640},
+ {631, 113664},
+ {612, 114688},
+ {594, 115712},
+ {577, 116736},
+ {560, 117760},
+ {544, 118784},
+ {528, 119808},
+ {513, 120832},
+ {498, 121856},
+ {483, 122880},
+ {470, 123904},
+ {457, 124928},
+ {444, 125952},
+ {431, 126976},
+ {419, 128000}
+};
+
+static int32_t pm8921_adc_map_linear(const struct pm8921_adc_map_pt *pts,
+ uint32_t tablesize, int32_t input, int64_t *output)
+{
+ bool descending = 1;
+ uint32_t i = 0;
+
+ if ((pts == NULL) || (output == NULL))
+ return -EINVAL;
+
+ /* Check if table is descending or ascending */
+ if (tablesize > 1) {
+ if (pts[0].x < pts[1].x)
+ descending = 0;
+ }
+
+ while (i < tablesize) {
+ if ((descending == 1) && (pts[i].x < input)) {
+ /* table entry is less than measured
+ value and table is descending, stop */
+ break;
+ } else if ((descending == 0) &&
+ (pts[i].x > input)) {
+ /* table entry is greater than measured
+ value and table is ascending, stop */
+ break;
+ } else {
+ i++;
+ }
+ }
+
+ if (i == 0)
+ *output = pts[0].y;
+ else if (i == tablesize)
+ *output = pts[tablesize-1].y;
+ else {
+ /* result is between search_index and search_index-1 */
+ /* interpolate linearly */
+ *output = (((int32_t) ((pts[i].y - pts[i-1].y)*
+ (input - pts[i-1].x))/
+ (pts[i].x - pts[i-1].x))+
+ pts[i-1].y);
+ }
+
+ return 0;
+}
+
+int32_t pm8921_adc_scale_default(int32_t adc_code,
+ const struct pm8921_adc_properties *adc_properties,
+ const struct pm8921_adc_chan_properties *chan_properties,
+ struct pm8921_adc_chan_result *adc_chan_result)
+{
+ bool negative_rawfromoffset = 0;
+ int32_t rawfromoffset = 0;
+
+ if (!chan_properties || !chan_properties->offset_gain_numerator ||
+ !chan_properties->offset_gain_denominator || !adc_properties
+ || !adc_chan_result)
+ return -EINVAL;
+
+ rawfromoffset = adc_code -
+ chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].offset;
+
+ adc_chan_result->adc_code = adc_code;
+ if (rawfromoffset < 0) {
+ if (adc_properties->bipolar) {
+ rawfromoffset = -rawfromoffset;
+ negative_rawfromoffset = 1;
+ } else {
+ rawfromoffset = 0;
+ }
+ }
+
+ if (rawfromoffset >= 1 << adc_properties->bitresolution)
+ rawfromoffset = (1 << adc_properties->bitresolution) - 1;
+
+ adc_chan_result->measurement = (int64_t)rawfromoffset *
+ chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dx *
+ chan_properties->offset_gain_denominator;
+
+ /* do_div only perform positive integer division! */
+ do_div(adc_chan_result->measurement,
+ chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dy *
+ chan_properties->offset_gain_numerator);
+
+ if (negative_rawfromoffset)
+ adc_chan_result->measurement = -adc_chan_result->measurement;
+
+ /* Note: adc_chan_result->measurement is in the unit of
+ * adc_properties.adc_reference. For generic channel processing,
+ * channel measurement is a scale/ratio relative to the adc
+ * reference input */
+ adc_chan_result->physical = (int32_t) adc_chan_result->measurement;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pm8921_adc_scale_default);
+
+int32_t pm8921_adc_scale_batt_therm(int32_t adc_code,
+ const struct pm8921_adc_properties *adc_properties,
+ const struct pm8921_adc_chan_properties *chan_properties,
+ struct pm8921_adc_chan_result *adc_chan_result)
+{
+ int rc;
+
+ rc = pm8921_adc_scale_default(adc_code, adc_properties, chan_properties,
+ adc_chan_result);
+ if (rc < 0) {
+ pr_debug("PM8921 ADC scale default error with %d\n", rc);
+ return rc;
+ }
+ /* convert mV ---> degC using the table */
+ return pm8921_adc_map_linear(
+ adcmap_batttherm,
+ sizeof(adcmap_batttherm)/sizeof(adcmap_batttherm[0]),
+ adc_chan_result->physical,
+ &adc_chan_result->physical);
+}
+EXPORT_SYMBOL_GPL(pm8921_adc_scale_batt_therm);
+
+int32_t pm8921_adc_scale_pmic_therm(int32_t adc_code,
+ const struct pm8921_adc_properties *adc_properties,
+ const struct pm8921_adc_chan_properties *chan_properties,
+ struct pm8921_adc_chan_result *adc_chan_result)
+{
+ int32_t rawfromoffset;
+
+ if (!chan_properties || !chan_properties->offset_gain_numerator ||
+ !chan_properties->offset_gain_denominator || !adc_properties
+ || !adc_chan_result)
+ return -EINVAL;
+
+ adc_chan_result->adc_code = adc_code;
+ rawfromoffset = adc_code -
+ chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].offset;
+ if (rawfromoffset > 0) {
+ if (rawfromoffset >= 1 << adc_properties->bitresolution)
+ rawfromoffset = (1 << adc_properties->bitresolution)
+ - 1;
+ /* 2mV/K */
+ adc_chan_result->measurement = (int64_t)rawfromoffset*
+ chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dx *
+ chan_properties->offset_gain_denominator * 1000;
+
+ do_div(adc_chan_result->measurement,
+ chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dy *
+ chan_properties->offset_gain_numerator*2);
+ } else {
+ adc_chan_result->measurement = 0;
+ }
+ /* Note: adc_chan_result->measurement is in the unit of
+ adc_properties.adc_reference */
+ adc_chan_result->physical = (int32_t)adc_chan_result->measurement;
+ /* Change to .001 deg C */
+ adc_chan_result->physical -= KELVINMIL_DEGMIL;
+ adc_chan_result->measurement <<= 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pm8921_adc_scale_pmic_therm);
+
+/* Scales the ADC code to 0.001 degrees C using the map
+ * table for the XO thermistor.
+ */
+int32_t pm8921_adc_tdkntcg_therm(int32_t adc_code,
+ const struct pm8921_adc_properties *adc_properties,
+ const struct pm8921_adc_chan_properties *chan_properties,
+ struct pm8921_adc_chan_result *adc_chan_result)
+{
+ uint32_t num1, num2, denom, rt_r25;
+ int32_t offset = chan_properties->adc_graph->offset,
+ dy = chan_properties->adc_graph->dy,
+ dx = chan_properties->adc_graph->dx,
+ fullscale_calibrated_adc_code;
+
+ adc_chan_result->adc_code = adc_code;
+ fullscale_calibrated_adc_code = dy + offset;
+ /* The above is a short cut in math that would reduce a lot of
+ computation whereas the below expression
+ (adc_properties->adc_reference*dy+dx*offset+(dx>>1))/dx
+ is a more generic formula when the 2 reference voltages are
+ different than 0 and full scale voltage. */
+
+ if ((dy == 0) || (dx == 0) ||
+ (offset >= fullscale_calibrated_adc_code)) {
+ return -EINVAL;
+ } else {
+ if (adc_code >= fullscale_calibrated_adc_code) {
+ rt_r25 = (uint32_t)-1;
+ } else if (adc_code <= offset) {
+ rt_r25 = 0;
+ } else {
+ /* The formula used is (adc_code of current reading - offset)/
+ * (the calibrated fullscale adc code - adc_code of current
+ * reading). For this channel, at this time, chan_properties->
+ * offset_gain_numerator = chan_properties->
+ * offset_gain_denominator = 1, so no need to incorporate into
+ * the formula even though it could be multiplied/divided by 1
+ * which yields the same result but
+ * expensive on computation. */
+ num1 = (adc_code - offset) << 14;
+ num2 = (fullscale_calibrated_adc_code - adc_code) >> 1;
+ denom = fullscale_calibrated_adc_code - adc_code;
+
+ if ((int)denom <= 0)
+ rt_r25 = 0x7FFFFFFF;
+ else
+ rt_r25 = (num1 + num2) / denom;
+ }
+
+ if (rt_r25 > 0x7FFFFFFF)
+ rt_r25 = 0x7FFFFFFF;
+
+ pm8921_adc_map_linear(adcmap_ntcg_104ef_104fb,
+ sizeof(adcmap_ntcg_104ef_104fb)/
+ sizeof(adcmap_ntcg_104ef_104fb[0]),
+ (int32_t)rt_r25, &adc_chan_result->physical);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pm8921_adc_tdkntcg_therm);
+
+int32_t pm8921_adc_scale_xtern_chgr_cur(int32_t adc_code,
+ const struct pm8921_adc_properties *adc_properties,
+ const struct pm8921_adc_chan_properties *chan_properties,
+ struct pm8921_adc_chan_result *adc_chan_result)
+{
+ int32_t rawfromoffset = (adc_code - PM8921_ADC_CODE_SCALE)
+ /PM8921_ADC_SLOPE;
+
+ if (!chan_properties || !chan_properties->offset_gain_numerator ||
+ !chan_properties->offset_gain_denominator || !adc_properties
+ || !adc_chan_result)
+ return -EINVAL;
+
+ adc_chan_result->adc_code = adc_code;
+ if (rawfromoffset > 0) {
+ if (rawfromoffset >= 1 << adc_properties->bitresolution)
+ rawfromoffset = (1 << adc_properties->bitresolution)
+ - 1;
+ adc_chan_result->measurement = ((int64_t)rawfromoffset * 5)*
+ chan_properties->offset_gain_denominator;
+ do_div(adc_chan_result->measurement,
+ chan_properties->offset_gain_numerator);
+ } else {
+ adc_chan_result->measurement = 0;
+ }
+ adc_chan_result->physical = (int32_t) adc_chan_result->measurement;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pm8921_adc_scale_xtern_chgr_cur);
+
+int32_t pm8921_adc_batt_scaler(struct pm8921_adc_arb_btm_param *btm_param)
+{
+ /* TODO based on the schematics for the batt thermistor
+ parameters and the HW/SW doc for the device. This is the
+ external batt therm */
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pm8921_adc_batt_scaler);
diff --git a/drivers/mfd/pm8921-adc.c b/drivers/mfd/pm8921-adc.c
new file mode 100644
index 0000000..b48b033
--- /dev/null
+++ b/drivers/mfd/pm8921-adc.c
@@ -0,0 +1,1022 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. 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 and
+ * only 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.
+ *
+ * Qualcomm's PM8921 ADC Arbiter driver
+ */
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/mpp.h>
+#include <linux/mfd/pm8921-adc.h>
+#include <linux/debugfs.h>
+
+/* User Bank register set */
+#define PM8921_ADC_ARB_USRP_CNTRL1 0x197
+#define PM8921_ADC_ARB_USRP_CNTRL1_EN_ARB BIT(0)
+#define PM8921_ADC_ARB_USRP_CNTRL1_RSV1 BIT(1)
+#define PM8921_ADC_ARB_USRP_CNTRL1_RSV2 BIT(2)
+#define PM8921_ADC_ARB_USRP_CNTRL1_RSV3 BIT(3)
+#define PM8921_ADC_ARB_USRP_CNTRL1_RSV4 BIT(4)
+#define PM8921_ADC_ARB_USRP_CNTRL1_RSV5 BIT(5)
+#define PM8921_ADC_ARB_USRP_CNTRL1_EOC BIT(6)
+#define PM8921_ADC_ARB_USRP_CNTRL1_REQ BIT(7)
+
+#define PM8921_ADC_ARB_USRP_AMUX_CNTRL 0x198
+#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_RSV0 BIT(0)
+#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_RSV1 BIT(1)
+#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_PREMUX0 BIT(2)
+#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_PREMUX1 BIT(3)
+#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_SEL0 BIT(4)
+#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_SEL1 BIT(5)
+#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_SEL2 BIT(6)
+#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_SEL3 BIT(7)
+
+#define PM8921_ADC_ARB_USRP_ANA_PARAM 0x199
+#define PM8921_ADC_ARB_USRP_DIG_PARAM 0x19A
+#define PM8921_ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT0 BIT(0)
+#define PM8921_ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT1 BIT(1)
+#define PM8921_ADC_ARB_USRP_DIG_PARAM_CLK_RATE0 BIT(2)
+#define PM8921_ADC_ARB_USRP_DIG_PARAM_CLK_RATE1 BIT(3)
+#define PM8921_ADC_ARB_USRP_DIG_PARAM_EOC BIT(4)
+#define PM8921_ADC_ARB_USRP_DIG_PARAM_DEC_RATE0 BIT(5)
+#define PM8921_ADC_ARB_USRP_DIG_PARAM_DEC_RATE1 BIT(6)
+#define PM8921_ADC_ARB_USRP_DIG_PARAM_EN BIT(7)
+
+#define PM8921_ADC_ARB_USRP_RSV 0x19B
+#define PM8921_ADC_ARB_USRP_RSV_RST BIT(0)
+#define PM8921_ADC_ARB_USRP_RSV_DTEST0 BIT(1)
+#define PM8921_ADC_ARB_USRP_RSV_DTEST1 BIT(2)
+#define PM8921_ADC_ARB_USRP_RSV_OP BIT(3)
+#define PM8921_ADC_ARB_USRP_RSV_IP_SEL0 BIT(4)
+#define PM8921_ADC_ARB_USRP_RSV_IP_SEL1 BIT(5)
+#define PM8921_ADC_ARB_USRP_RSV_IP_SEL2 BIT(6)
+#define PM8921_ADC_ARB_USRP_RSV_TRM BIT(7)
+
+#define PM8921_ADC_ARB_USRP_DATA0 0x19D
+#define PM8921_ADC_ARB_USRP_DATA1 0x19C
+
+#define PM8921_ADC_ARB_BTM_CNTRL1 0x17e
+#define PM8921_ADC_ARB_BTM_CNTRL1_EN_BTM BIT(0)
+#define PM8921_ADC_ARB_BTM_CNTRL1_SEL_OP_MODE BIT(1)
+#define PM8921_ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL1 BIT(2)
+#define PM8921_ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL2 BIT(3)
+#define PM8921_ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL3 BIT(4)
+#define PM8921_ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL4 BIT(5)
+#define PM8921_ADC_ARB_BTM_CNTRL1_EOC BIT(6)
+#define PM8921_ADC_ARB_BTM_CNTRL1_REQ BIT(7)
+
+#define PM8921_ADC_ARB_BTM_CNTRL2 0x18c
+#define PM8921_ADC_ARB_BTM_AMUX_CNTRL 0x17f
+#define PM8921_ADC_ARB_BTM_ANA_PARAM 0x180
+#define PM8921_ADC_ARB_BTM_DIG_PARAM 0x181
+#define PM8921_ADC_ARB_BTM_RSV 0x182
+#define PM8921_ADC_ARB_BTM_DATA1 0x183
+#define PM8921_ADC_ARB_BTM_DATA0 0x184
+#define PM8921_ADC_ARB_BTM_BAT_COOL_THR1 0x185
+#define PM8921_ADC_ARB_BTM_BAT_COOL_THR0 0x186
+#define PM8921_ADC_ARB_BTM_BAT_WARM_THR1 0x187
+#define PM8921_ADC_ARB_BTM_BAT_WARM_THR0 0x188
+
+#define PM8921_ADC_ARB_SECP_CNTRL 0x190
+#define PM8921_ADC_ARB_IRQ_BLOCK_SEL_SEC 0x1ac
+#define PM8921_ADC_ARB_IRQ_CONFIG_SEC 0x1ae
+#define PM8921_ADC_ARB_IRQ_BIT_PERM_USR 0x1a6
+#define PM8921_ADC_ARB_IRQ_BLOCK_SEL_USR 0x1c0
+#define PM8921_ADC_ARB_IRQ_CONFIG_USR 0x1c2
+
+#define PM8921_ADC_ARB_IRQ_BLOCK_SEL_DATA 0x09
+#define PM8921_ADC_ARB_IRQ_CONFIG_SEC_DATA 0xe0
+#define PM8921_ADC_ARB_IRQ_BIT_PERM_USR_DATA 0x40
+#define PM8921_ADC_ARB_IRQ_BLOCK_SEL_USR_DATA 0x09
+#define PM8921_ADC_ARB_IRQ_CONFIG_USR_DATA 0xe0
+
+#define PM8921_ADC_ARB_ANA_DIG 0xa0
+#define PM8921_ADC_ARB_SECP_CNTRL_WR 0x31
+
+#define PM8921_ADC_AMUX_MPP_SEL 2
+#define PM8921_ADC_AMUX_SEL 4
+#define PM8921_ADC_RSV_IP_SEL 4
+#define PM8921_ADC_BTM_CHANNEL_SEL 4
+#define PM8921_MAX_CHANNEL_PROPERTIES 2
+#define PM8921_ADC_IRQ_0 0
+#define PM8921_ADC_IRQ_1 1
+#define PM8921_ADC_IRQ_2 2
+#define PM8921_ADC_BTM_INTERVAL_SEL 5
+#define PM8921_ADC_BTM_DECIMATION_SEL 5
+#define PM8921_ADC_MUL 10
+#define PM8921_ADC_CONV_TIME_MIN 2000
+#define PM8921_ADC_CONV_TIME_MAX 2100
+
+struct pm8921_adc {
+ struct device *dev;
+ struct pm8921_adc_properties *adc_prop;
+ int adc_irq;
+ struct mutex adc_lock;
+ struct mutex btm_lock;
+ uint32_t adc_num_channel;
+ struct completion adc_rslt_completion;
+ struct pm8921_adc_amux *adc_channel;
+ struct pm8921_adc_amux_properties *conv;
+ struct pm8921_adc_arb_btm *batt;
+ int btm_warm_irq;
+ int btm_cold_irq;
+ struct dentry *dent;
+};
+
+struct pm8921_adc_amux_properties {
+ uint32_t amux_channel;
+ uint32_t decimation;
+ uint32_t amux_ip_rsv;
+ uint32_t amux_mpp_channel;
+ struct pm8921_adc_chan_properties *chan_prop;
+};
+
+static const struct pm8921_adc_scaling_ratio pm8921_amux_scaling_ratio[] = {
+ {1, 1},
+ {1, 3},
+ {1, 4},
+ {1, 6}
+};
+
+static struct pm8921_adc *pmic_adc;
+
+static struct pm8921_adc_scale_fn adc_scale_fn[] = {
+ [ADC_SCALE_DEFAULT] = {pm8921_adc_scale_default},
+ [ADC_SCALE_BATT_THERM] = {pm8921_adc_scale_batt_therm},
+ [ADC_SCALE_PMIC_THERM] = {pm8921_adc_scale_pmic_therm},
+ [ADC_SCALE_XTERN_CHGR_CUR] = {pm8921_adc_scale_xtern_chgr_cur},
+};
+
+static bool pm8921_adc_calib_first_adc, pm8921_btm_calib_first_adc;
+static bool pm8921_adc_initialized, pm8921_adc_calib_device_init;
+
+static int32_t pm8921_adc_arb_cntrl(uint32_t arb_cntrl)
+{
+ struct pm8921_adc *adc_pmic = pmic_adc;
+ int i, rc;
+ u8 data_arb_cntrl = 0;
+
+ if (arb_cntrl)
+ data_arb_cntrl |= (PM8921_ADC_ARB_USRP_CNTRL1_REQ |
+ PM8921_ADC_ARB_USRP_CNTRL1_EN_ARB);
+
+ /* Write twice to the CNTRL register for the arbiter settings
+ to take into effect */
+ for (i = 0; i < 2; i++) {
+ rc = pm8xxx_writeb(adc_pmic->dev->parent,
+ PM8921_ADC_ARB_USRP_CNTRL1, data_arb_cntrl);
+ if (rc < 0) {
+ pr_err("PM8921 arb cntrl write failed with %d\n", rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static uint32_t pm8921_adc_read_reg(uint32_t reg, u8 *data)
+{
+ struct pm8921_adc *adc_pmic = pmic_adc;
+ int rc;
+
+ rc = pm8xxx_readb(adc_pmic->dev->parent, reg, data);
+ if (rc < 0) {
+ pr_err("PM8921 adc read reg %d failed with %d\n", reg, rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static uint32_t pm8921_adc_write_reg(uint32_t reg, u8 data)
+{
+ struct pm8921_adc *adc_pmic = pmic_adc;
+ int rc;
+
+ rc = pm8xxx_writeb(adc_pmic->dev->parent, reg, data);
+ if (rc < 0) {
+ pr_err("PM8921 adc write reg %d failed with %d\n", reg, rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int32_t pm8921_adc_configure(
+ struct pm8921_adc_amux_properties *chan_prop)
+{
+ struct pm8921_adc *adc_pmic = pmic_adc;
+ u8 data_amux_chan = 0, data_arb_rsv = 0, data_dig_param = 0;
+ int rc, i;
+
+ for (i = 0; i < 2; i++) {
+ rc = pm8921_adc_write_reg(PM8921_ADC_ARB_SECP_CNTRL,
+ PM8921_ADC_ARB_SECP_CNTRL_WR);
+ if (rc < 0)
+ return rc;
+ }
+
+ data_amux_chan |= chan_prop->amux_channel << PM8921_ADC_AMUX_SEL;
+
+ if (chan_prop->amux_mpp_channel)
+ data_amux_chan |= chan_prop->amux_mpp_channel <<
+ PM8921_ADC_AMUX_MPP_SEL;
+
+ rc = pm8921_adc_write_reg(PM8921_ADC_ARB_USRP_AMUX_CNTRL,
+ data_amux_chan);
+ if (rc < 0)
+ return rc;
+
+ data_arb_rsv &= (PM8921_ADC_ARB_USRP_RSV_RST |
+ PM8921_ADC_ARB_USRP_RSV_DTEST0 |
+ PM8921_ADC_ARB_USRP_RSV_DTEST1 |
+ PM8921_ADC_ARB_USRP_RSV_OP |
+ PM8921_ADC_ARB_USRP_RSV_TRM);
+ data_arb_rsv |= chan_prop->amux_ip_rsv << PM8921_ADC_RSV_IP_SEL;
+
+ rc = pm8921_adc_write_reg(PM8921_ADC_ARB_USRP_RSV, data_arb_rsv);
+ if (rc < 0)
+ return rc;
+
+ rc = pm8921_adc_read_reg(PM8921_ADC_ARB_USRP_DIG_PARAM,
+ &data_dig_param);
+ if (rc < 0)
+ return rc;
+
+ /* Default 2.4Mhz clock rate */
+ /* Client chooses the decimation */
+ switch (chan_prop->decimation) {
+ case ADC_DECIMATION_TYPE1:
+ data_dig_param |= PM8921_ADC_ARB_USRP_DIG_PARAM_DEC_RATE0;
+ break;
+ case ADC_DECIMATION_TYPE2:
+ data_dig_param |= (PM8921_ADC_ARB_USRP_DIG_PARAM_DEC_RATE0
+ | PM8921_ADC_ARB_USRP_DIG_PARAM_DEC_RATE1);
+ break;
+ default:
+ data_dig_param |= PM8921_ADC_ARB_USRP_DIG_PARAM_DEC_RATE0;
+ break;
+ }
+ rc = pm8921_adc_write_reg(PM8921_ADC_ARB_USRP_DIG_PARAM,
+ PM8921_ADC_ARB_ANA_DIG);
+ if (rc < 0)
+ return rc;
+
+ rc = pm8921_adc_write_reg(PM8921_ADC_ARB_USRP_ANA_PARAM,
+ PM8921_ADC_ARB_ANA_DIG);
+ if (rc < 0)
+ return rc;
+
+ if (!pm8921_adc_calib_first_adc)
+ enable_irq(adc_pmic->adc_irq);
+
+ rc = pm8921_adc_write_reg(PM8921_ADC_ARB_IRQ_BLOCK_SEL_SEC,
+ PM8921_ADC_ARB_IRQ_BLOCK_SEL_DATA);
+ if (rc < 0)
+ return rc;
+
+ rc = pm8921_adc_write_reg(PM8921_ADC_ARB_IRQ_CONFIG_SEC,
+ PM8921_ADC_ARB_IRQ_CONFIG_SEC_DATA);
+ if (rc < 0)
+ return rc;
+
+ rc = pm8921_adc_write_reg(PM8921_ADC_ARB_IRQ_BIT_PERM_USR,
+ PM8921_ADC_ARB_IRQ_BIT_PERM_USR_DATA);
+ if (rc < 0)
+ return rc;
+
+ rc = pm8921_adc_write_reg(PM8921_ADC_ARB_IRQ_BLOCK_SEL_USR,
+ PM8921_ADC_ARB_IRQ_BLOCK_SEL_USR_DATA);
+ if (rc < 0)
+ return rc;
+
+ rc = pm8921_adc_write_reg(PM8921_ADC_ARB_IRQ_CONFIG_USR,
+ PM8921_ADC_ARB_IRQ_CONFIG_USR_DATA);
+ if (rc < 0)
+ return rc;
+
+ rc = pm8921_adc_arb_cntrl(1);
+ if (rc < 0) {
+ pr_err("Configuring ADC Arbiter"
+ "enable failed with %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static uint32_t pm8921_adc_read_adc_code(int32_t *data)
+{
+ struct pm8921_adc *adc_pmic = pmic_adc;
+ uint8_t rslt_lsb, rslt_msb;
+ int32_t rc, max_ideal_adc_code = 1 << adc_pmic->adc_prop->bitresolution;
+
+ rc = pm8xxx_readb(adc_pmic->dev->parent,
+ PM8921_ADC_ARB_USRP_DATA0, &rslt_lsb);
+ if (rc < 0) {
+ pr_err("PM8921 adc result read failed with %d\n", rc);
+ return rc;
+ }
+
+ rc = pm8xxx_readb(adc_pmic->dev->parent,
+ PM8921_ADC_ARB_USRP_DATA1, &rslt_msb);
+ if (rc < 0) {
+ pr_err("PM8921 adc result read failed with %d\n", rc);
+ return rc;
+ }
+
+ *data = (rslt_msb << 8) | rslt_lsb;
+
+ /* Use the midpoint to determine underflow or overflow */
+ if (*data > max_ideal_adc_code + (max_ideal_adc_code >> 1))
+ *data |= ((1 << (8 * sizeof(*data) -
+ adc_pmic->adc_prop->bitresolution)) - 1) <<
+ adc_pmic->adc_prop->bitresolution;
+
+ /* Default value for switching off the arbiter after reading
+ the ADC value. Bit 0 set to 0. */
+ rc = pm8921_adc_arb_cntrl(0);
+ if (rc < 0) {
+ pr_err("%s: Configuring ADC Arbiter disable"
+ "failed\n", __func__);
+ return rc;
+ }
+
+ return 0;
+}
+
+static irqreturn_t pm8921_adc_isr(int irq, void *dev_id)
+{
+ struct pm8921_adc *adc_8921 = dev_id;
+
+ disable_irq_nosync(adc_8921->adc_irq);
+
+ if (pm8921_adc_calib_first_adc)
+ return IRQ_HANDLED;
+ /* TODO Handle spurius interrupt condition */
+ complete(&adc_8921->adc_rslt_completion);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8921_btm_warm_isr(int irq, void *dev_id)
+{
+ struct pm8921_adc *btm_8921 = dev_id;
+
+ disable_irq_nosync(btm_8921->btm_warm_irq);
+
+ if (pm8921_btm_calib_first_adc)
+ return IRQ_HANDLED;
+
+ if (btm_8921->batt->btm_param->btm_warm_fn != NULL)
+ btm_8921->batt->btm_param->btm_warm_fn();
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8921_btm_cold_isr(int irq, void *dev_id)
+{
+ struct pm8921_adc *btm_8921 = dev_id;
+
+ disable_irq_nosync(btm_8921->btm_cold_irq);
+
+ if (pm8921_btm_calib_first_adc)
+ return IRQ_HANDLED;
+
+ if (btm_8921->batt->btm_param->btm_cold_fn != NULL)
+ btm_8921->batt->btm_param->btm_cold_fn();
+
+ return IRQ_HANDLED;
+}
+
+static uint32_t pm8921_adc_calib_device(void)
+{
+ struct pm8921_adc *adc_pmic = pmic_adc;
+ struct pm8921_adc_amux_properties conv;
+ int rc, offset_adc, slope_adc, calib_read_1, calib_read_2;
+ u8 data_arb_usrp_cntrl1 = 0;
+
+ conv.amux_channel = CHANNEL_125V;
+ conv.decimation = ADC_DECIMATION_TYPE2;
+ conv.amux_ip_rsv = AMUX_RSV1;
+ conv.amux_mpp_channel = PREMUX_MPP_SCALE_0;
+ pm8921_adc_calib_first_adc = true;
+ rc = pm8921_adc_configure(&conv);
+ if (rc) {
+ pr_err("pm8921_adc configure failed with %d\n", rc);
+ goto calib_fail;
+ }
+
+ while (data_arb_usrp_cntrl1 != (PM8921_ADC_ARB_USRP_CNTRL1_EOC |
+ PM8921_ADC_ARB_USRP_CNTRL1_EN_ARB)) {
+ rc = pm8921_adc_read_reg(PM8921_ADC_ARB_USRP_CNTRL1,
+ &data_arb_usrp_cntrl1);
+ if (rc < 0)
+ return rc;
+ usleep_range(PM8921_ADC_CONV_TIME_MIN,
+ PM8921_ADC_CONV_TIME_MAX);
+ }
+ data_arb_usrp_cntrl1 = 0;
+
+ rc = pm8921_adc_read_adc_code(&calib_read_1);
+ if (rc) {
+ pr_err("pm8921_adc read adc failed with %d\n", rc);
+ pm8921_adc_calib_first_adc = false;
+ goto calib_fail;
+ }
+ pm8921_adc_calib_first_adc = false;
+
+ conv.amux_channel = CHANNEL_625MV;
+ conv.decimation = ADC_DECIMATION_TYPE2;
+ conv.amux_ip_rsv = AMUX_RSV1;
+ conv.amux_mpp_channel = PREMUX_MPP_SCALE_0;
+ pm8921_adc_calib_first_adc = true;
+ rc = pm8921_adc_configure(&conv);
+ if (rc) {
+ pr_err("pm8921_adc configure failed with %d\n", rc);
+ goto calib_fail;
+ }
+
+ while (data_arb_usrp_cntrl1 != (PM8921_ADC_ARB_USRP_CNTRL1_EOC |
+ PM8921_ADC_ARB_USRP_CNTRL1_EN_ARB)) {
+ rc = pm8921_adc_read_reg(PM8921_ADC_ARB_USRP_CNTRL1,
+ &data_arb_usrp_cntrl1);
+ if (rc < 0)
+ return rc;
+ usleep_range(PM8921_ADC_CONV_TIME_MIN,
+ PM8921_ADC_CONV_TIME_MAX);
+ }
+ data_arb_usrp_cntrl1 = 0;
+
+ rc = pm8921_adc_read_adc_code(&calib_read_2);
+ if (rc) {
+ pr_err("pm8921_adc read adc failed with %d\n", rc);
+ pm8921_adc_calib_first_adc = false;
+ goto calib_fail;
+ }
+ pm8921_adc_calib_first_adc = false;
+
+ slope_adc = (((calib_read_1 - calib_read_2) << PM8921_ADC_MUL)/
+ PM8921_CHANNEL_ADC_625_MV);
+ offset_adc = calib_read_2 -
+ ((slope_adc * PM8921_CHANNEL_ADC_625_MV) >>
+ PM8921_ADC_MUL);
+
+ adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_ABSOLUTE].offset
+ = offset_adc;
+ adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_ABSOLUTE].dy =
+ (calib_read_1 - calib_read_2);
+ adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_ABSOLUTE].dx
+ = PM8921_CHANNEL_ADC_625_MV;
+ rc = pm8921_adc_arb_cntrl(0);
+ if (rc < 0) {
+ pr_err("%s: Configuring ADC Arbiter disable"
+ "failed\n", __func__);
+ return rc;
+ }
+ /* Ratiometric Calibration */
+ conv.amux_channel = CHANNEL_MUXOFF;
+ conv.decimation = ADC_DECIMATION_TYPE2;
+ conv.amux_ip_rsv = AMUX_RSV5;
+ conv.amux_mpp_channel = PREMUX_MPP_SCALE_0;
+ pm8921_adc_calib_first_adc = true;
+ rc = pm8921_adc_configure(&conv);
+ if (rc) {
+ pr_err("pm8921_adc configure failed with %d\n", rc);
+ goto calib_fail;
+ }
+
+ while (data_arb_usrp_cntrl1 != (PM8921_ADC_ARB_USRP_CNTRL1_EOC |
+ PM8921_ADC_ARB_USRP_CNTRL1_EN_ARB)) {
+ rc = pm8921_adc_read_reg(PM8921_ADC_ARB_USRP_CNTRL1,
+ &data_arb_usrp_cntrl1);
+ if (rc < 0)
+ return rc;
+ usleep_range(PM8921_ADC_CONV_TIME_MIN,
+ PM8921_ADC_CONV_TIME_MAX);
+ }
+ data_arb_usrp_cntrl1 = 0;
+
+ rc = pm8921_adc_read_adc_code(&calib_read_1);
+ if (rc) {
+ pr_err("pm8921_adc read adc failed with %d\n", rc);
+ pm8921_adc_calib_first_adc = false;
+ goto calib_fail;
+ }
+ pm8921_adc_calib_first_adc = false;
+
+ conv.amux_channel = CHANNEL_MUXOFF;
+ conv.decimation = ADC_DECIMATION_TYPE2;
+ conv.amux_ip_rsv = AMUX_RSV4;
+ conv.amux_mpp_channel = PREMUX_MPP_SCALE_0;
+ pm8921_adc_calib_first_adc = true;
+ rc = pm8921_adc_configure(&conv);
+ if (rc) {
+ pr_err("pm8921_adc configure failed with %d\n", rc);
+ goto calib_fail;
+ }
+
+ while (data_arb_usrp_cntrl1 != (PM8921_ADC_ARB_USRP_CNTRL1_EOC |
+ PM8921_ADC_ARB_USRP_CNTRL1_EN_ARB)) {
+ rc = pm8921_adc_read_reg(PM8921_ADC_ARB_USRP_CNTRL1,
+ &data_arb_usrp_cntrl1);
+ if (rc < 0)
+ return rc;
+ usleep_range(PM8921_ADC_CONV_TIME_MIN,
+ PM8921_ADC_CONV_TIME_MAX);
+ }
+ data_arb_usrp_cntrl1 = 0;
+
+ rc = pm8921_adc_read_adc_code(&calib_read_2);
+ if (rc) {
+ pr_err("pm8921_adc read adc failed with %d\n", rc);
+ pm8921_adc_calib_first_adc = false;
+ goto calib_fail;
+ }
+ pm8921_adc_calib_first_adc = false;
+
+ slope_adc = (((calib_read_1 - calib_read_2) << PM8921_ADC_MUL)/
+ adc_pmic->adc_prop->adc_vdd_reference);
+ offset_adc = calib_read_2 -
+ ((slope_adc * adc_pmic->adc_prop->adc_vdd_reference)
+ >> PM8921_ADC_MUL);
+
+ adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_RATIOMETRIC].offset
+ = offset_adc;
+ adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_RATIOMETRIC].dy =
+ (calib_read_1 - calib_read_2);
+ adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_RATIOMETRIC].dx =
+ adc_pmic->adc_prop->adc_vdd_reference;
+calib_fail:
+ rc = pm8921_adc_arb_cntrl(0);
+ if (rc < 0) {
+ pr_err("%s: Configuring ADC Arbiter disable"
+ "failed\n", __func__);
+ }
+
+ return rc;
+}
+
+uint32_t pm8921_adc_read(enum pm8921_adc_channels channel,
+ struct pm8921_adc_chan_result *result)
+{
+ return pm8921_adc_mpp_read(channel, result, PREMUX_MPP_SCALE_0);
+}
+EXPORT_SYMBOL_GPL(pm8921_adc_read);
+
+uint32_t pm8921_adc_mpp_read(enum pm8921_adc_mpp_channels channel,
+ struct pm8921_adc_chan_result *result,
+ enum pm8921_adc_premux_mpp_scale_type mpp_scale)
+{
+ struct pm8921_adc *adc_pmic = pmic_adc;
+ int i = 0, rc, amux_prescaling, scale_type;
+
+ if (!pm8921_adc_initialized)
+ return -ENODEV;
+
+ if (!pm8921_adc_calib_device_init) {
+ if (pm8921_adc_calib_device() == 0)
+ pm8921_adc_calib_device_init = true;
+ }
+
+ mutex_lock(&adc_pmic->adc_lock);
+
+ for (i = 0; i < adc_pmic->adc_num_channel; i++) {
+ if (channel == adc_pmic->adc_channel[i].channel_name)
+ break;
+ }
+
+ if (i == adc_pmic->adc_num_channel) {
+ mutex_unlock(&adc_pmic->adc_lock);
+ return -EBADF; /* unknown channel */
+ }
+
+ adc_pmic->conv->amux_channel = i;
+ adc_pmic->conv->amux_mpp_channel = mpp_scale;
+
+ adc_pmic->conv->amux_ip_rsv = adc_pmic->adc_channel[i].adc_rsv;
+ adc_pmic->conv->decimation = adc_pmic->adc_channel[i].adc_decimation;
+ amux_prescaling = adc_pmic->adc_channel[i].chan_path_prescaling;
+
+ adc_pmic->conv->chan_prop->offset_gain_numerator =
+ pm8921_amux_scaling_ratio[amux_prescaling].num;
+ adc_pmic->conv->chan_prop->offset_gain_denominator =
+ pm8921_amux_scaling_ratio[amux_prescaling].den;
+
+ rc = pm8921_adc_configure(adc_pmic->conv);
+ if (rc) {
+ mutex_unlock(&adc_pmic->adc_lock);
+ return -EINVAL;
+ }
+
+ wait_for_completion(&adc_pmic->adc_rslt_completion);
+
+ rc = pm8921_adc_read_adc_code(&result->adc_code);
+ if (rc) {
+ mutex_unlock(&adc_pmic->adc_lock);
+ return -EINVAL;
+ }
+
+ scale_type = adc_pmic->adc_channel[i].adc_scale_fn;
+ if (scale_type >= ADC_SCALE_NONE) {
+ mutex_unlock(&adc_pmic->adc_lock);
+ return -EBADF;
+ }
+
+ adc_scale_fn[scale_type].chan(result->adc_code,
+ adc_pmic->adc_prop, adc_pmic->conv->chan_prop, result);
+
+ mutex_unlock(&adc_pmic->adc_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pm8921_adc_mpp_read);
+
+uint32_t pm8921_adc_btm_configure(struct pm8921_adc_arb_btm_param *btm_param)
+{
+ struct pm8921_adc *adc_pmic = pmic_adc;
+ u8 data_btm_cool_thr0, data_btm_cool_thr1;
+ u8 data_btm_warm_thr0, data_btm_warm_thr1;
+ u8 arb_btm_cntrl1;
+ int rc;
+
+ mutex_lock(&adc_pmic->btm_lock);
+
+ data_btm_cool_thr0 = ((btm_param->low_thr_voltage << 24) >> 24);
+ data_btm_cool_thr1 = ((btm_param->low_thr_voltage << 16) >> 24);
+ data_btm_warm_thr0 = ((btm_param->high_thr_voltage << 24) >> 24);
+ data_btm_warm_thr1 = ((btm_param->high_thr_voltage << 16) >> 24);
+
+ rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_BAT_COOL_THR0,
+ data_btm_cool_thr0);
+ if (rc < 0)
+ goto write_err;
+
+ rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_BAT_COOL_THR1,
+ data_btm_cool_thr0);
+ if (rc < 0)
+ goto write_err;
+
+ rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_BAT_WARM_THR0,
+ data_btm_warm_thr0);
+ if (rc < 0)
+ goto write_err;
+
+ rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_BAT_WARM_THR1,
+ data_btm_warm_thr1);
+ if (rc < 0)
+ goto write_err;
+
+ arb_btm_cntrl1 = btm_param->interval << PM8921_ADC_BTM_INTERVAL_SEL;
+
+ rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_CNTRL1, arb_btm_cntrl1);
+ if (rc < 0)
+ goto write_err;
+
+ adc_pmic->batt->btm_param->btm_warm_fn = btm_param->btm_warm_fn;
+ adc_pmic->batt->btm_param->btm_cold_fn = btm_param->btm_cold_fn;
+
+ mutex_unlock(&adc_pmic->btm_lock);
+
+ return rc;
+
+write_err:
+ mutex_unlock(&adc_pmic->btm_lock);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(pm8921_adc_btm_configure);
+
+static uint32_t pm8921_adc_btm_read(uint32_t channel)
+{
+ struct pm8921_adc *adc_pmic = pmic_adc;
+ int rc, i;
+ u8 arb_btm_dig_param, arb_btm_ana_param, arb_btm_rsv;
+ u8 arb_btm_amux_cntrl, arb_btm_decimation, data_arb_btm_cntrl;
+
+ arb_btm_amux_cntrl = channel << PM8921_ADC_BTM_CHANNEL_SEL;
+ arb_btm_rsv = adc_pmic->adc_channel[channel].adc_rsv;
+ arb_btm_decimation =
+ adc_pmic->adc_channel[channel].adc_decimation;
+ arb_btm_ana_param = PM8921_ADC_ARB_ANA_DIG;
+
+ mutex_lock(&adc_pmic->btm_lock);
+
+ rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_AMUX_CNTRL,
+ arb_btm_amux_cntrl);
+ if (rc < 0)
+ goto write_err;
+
+ rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_RSV, arb_btm_rsv);
+ if (rc < 0)
+ goto write_err;
+
+ arb_btm_dig_param = arb_btm_decimation <<
+ PM8921_ADC_BTM_DECIMATION_SEL;
+ rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_DIG_PARAM,
+ arb_btm_dig_param);
+ if (rc < 0)
+ goto write_err;
+
+ rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_ANA_PARAM,
+ arb_btm_ana_param);
+ if (rc < 0)
+ goto write_err;
+
+ data_arb_btm_cntrl = PM8921_ADC_ARB_BTM_CNTRL1_EOC |
+ PM8921_ADC_ARB_BTM_CNTRL1_EN_BTM;
+
+ /* Write twice to the CNTRL register for the arbiter settings
+ to take into effect */
+ for (i = 0; i < 2; i++) {
+ rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_CNTRL1,
+ data_arb_btm_cntrl);
+ if (rc < 0)
+ goto write_err;
+ }
+
+ mutex_unlock(&adc_pmic->btm_lock);
+
+ return 0;
+
+write_err:
+ mutex_unlock(&adc_pmic->btm_lock);
+ return rc;
+}
+
+uint32_t pm8921_adc_btm_start(void)
+{
+ int rc;
+
+ rc = pm8921_adc_btm_read(CHANNEL_BATT_THERM);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(pm8921_adc_btm_start);
+
+uint32_t pm8921_adc_btm_end(void)
+{
+ struct pm8921_adc *adc_pmic = pmic_adc;
+ int i, rc;
+ u8 data_arb_btm_cntrl;
+
+ /* Set BTM registers to Disable mode */
+ data_arb_btm_cntrl = PM8921_ADC_ARB_BTM_CNTRL1_EOC;
+
+ mutex_lock(&adc_pmic->btm_lock);
+ /* Write twice to the CNTRL register for the arbiter settings
+ to take into effect */
+ for (i = 0; i < 2; i++) {
+ rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_CNTRL1,
+ data_arb_btm_cntrl);
+ if (rc < 0) {
+ mutex_unlock(&adc_pmic->btm_lock);
+ return rc;
+ }
+ }
+ mutex_unlock(&adc_pmic->btm_lock);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(pm8921_adc_btm_end);
+
+static int get_adc(void *data, u64 *val)
+{
+ struct pm8921_adc_chan_result result;
+ int i = (int)data;
+ int rc;
+
+ rc = pm8921_adc_read(i, &result);
+
+ pr_info("ADC value raw:%x physical:%lld\n",
+ result.adc_code, result.physical);
+ *val = result.physical;
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_adc, NULL, "%llu\n");
+
+#ifdef CONFIG_DEBUG_FS
+static void create_debugfs_entries(void)
+{
+ pmic_adc->dent = debugfs_create_dir("pm8921_adc", NULL);
+
+ if (IS_ERR(pmic_adc->dent)) {
+ pr_err("pmic adc debugfs dir not created\n");
+ return;
+ }
+
+ debugfs_create_file("vbat", 0644, pmic_adc->dent,
+ (void *)CHANNEL_VBAT, ®_fops);
+ debugfs_create_file("625mv", 0644, pmic_adc->dent,
+ (void *)CHANNEL_625MV, ®_fops);
+ debugfs_create_file("125v", 0644, pmic_adc->dent,
+ (void *)CHANNEL_125V, ®_fops);
+ debugfs_create_file("die_temp", 0644, pmic_adc->dent,
+ (void *)CHANNEL_DIE_TEMP, ®_fops);
+ debugfs_create_file("vcoin", 0644, pmic_adc->dent,
+ (void *)CHANNEL_VCOIN, ®_fops);
+ debugfs_create_file("dc_in", 0644, pmic_adc->dent,
+ (void *)CHANNEL_DCIN, ®_fops);
+ debugfs_create_file("vph_pwr", 0644, pmic_adc->dent,
+ (void *)CHANNEL_VPH_PWR, ®_fops);
+ debugfs_create_file("usb_in", 0644, pmic_adc->dent,
+ (void *)CHANNEL_USBIN, ®_fops);
+ debugfs_create_file("batt_therm", 0644, pmic_adc->dent,
+ (void *)CHANNEL_BATT_THERM, ®_fops);
+ debugfs_create_file("batt_id", 0644, pmic_adc->dent,
+ (void *)CHANNEL_BATT_ID, ®_fops);
+ debugfs_create_file("chg_temp", 0644, pmic_adc->dent,
+ (void *)CHANNEL_CHG_TEMP, ®_fops);
+ debugfs_create_file("charger_current", 0644, pmic_adc->dent,
+ (void *)CHANNEL_ICHG, ®_fops);
+ debugfs_create_file("ibat", 0644, pmic_adc->dent,
+ (void *)CHANNEL_IBAT, ®_fops);
+}
+#else
+static inline void create_debugfs_entries(void)
+{
+}
+#endif
+
+static int __devexit pm8921_adc_teardown(struct platform_device *pdev)
+{
+ struct pm8921_adc *adc_pmic = pmic_adc;
+
+ device_init_wakeup(&pdev->dev, 0);
+ free_irq(adc_pmic->adc_irq, adc_pmic);
+ free_irq(adc_pmic->btm_warm_irq, adc_pmic);
+ free_irq(adc_pmic->btm_cold_irq, adc_pmic);
+ platform_set_drvdata(pdev, NULL);
+ pmic_adc = NULL;
+ kfree(adc_pmic->conv->chan_prop);
+ kfree(adc_pmic->adc_channel);
+ kfree(adc_pmic);
+ pm8921_adc_initialized = false;
+
+ return 0;
+}
+
+static int __devinit pm8921_adc_probe(struct platform_device *pdev)
+{
+ const struct pm8921_adc_platform_data *pdata = pdev->dev.platform_data;
+ struct pm8921_adc *adc_pmic;
+ struct pm8921_adc_amux_properties *adc_amux_prop;
+ struct pm8921_adc_chan_properties *adc_pmic_chanprop;
+ struct pm8921_adc_amux *adc_amux;
+ int rc = 0;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data?\n");
+ return -EINVAL;
+ }
+
+ adc_pmic = kzalloc(sizeof(struct pm8921_adc),
+ GFP_KERNEL);
+ if (!adc_pmic) {
+ dev_err(&pdev->dev, "Unable to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ adc_amux_prop = kzalloc(sizeof(struct pm8921_adc_amux_properties),
+ GFP_KERNEL);
+ if (!adc_amux_prop) {
+ dev_err(&pdev->dev, "Unable to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ adc_amux = kzalloc(sizeof(struct pm8921_adc_amux),
+ GFP_KERNEL);
+ if (!adc_amux) {
+ dev_err(&pdev->dev, "Unable to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ adc_pmic_chanprop = kzalloc(sizeof(struct pm8921_adc_chan_properties),
+ GFP_KERNEL);
+ if (!adc_pmic_chanprop) {
+ dev_err(&pdev->dev, "Unable to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ adc_pmic->dev = &pdev->dev;
+ adc_pmic->adc_prop = pdata->adc_prop;
+ adc_pmic->conv = adc_amux_prop;
+ adc_pmic->conv->chan_prop = adc_pmic_chanprop;
+
+ init_completion(&adc_pmic->adc_rslt_completion);
+ adc_amux = pdata->adc_channel;
+ adc_pmic->adc_channel = adc_amux;
+ adc_pmic->adc_num_channel = pdata->adc_num_channel;
+
+ mutex_init(&adc_pmic->adc_lock);
+ mutex_init(&adc_pmic->btm_lock);
+
+ adc_pmic->adc_irq = platform_get_irq(pdev, PM8921_ADC_IRQ_0);
+ if (adc_pmic->adc_irq < 0) {
+ rc = -ENXIO;
+ goto err_cleanup;
+ }
+
+ rc = request_irq(adc_pmic->adc_irq,
+ pm8921_adc_isr,
+ IRQF_TRIGGER_RISING, "pm8921_adc_interrupt", adc_pmic);
+ if (rc) {
+ dev_err(&pdev->dev, "failed to request adc irq "
+ "with error %d\n", rc);
+ goto err_cleanup;
+ }
+
+ disable_irq_nosync(adc_pmic->adc_irq);
+
+ adc_pmic->btm_warm_irq = platform_get_irq(pdev, PM8921_ADC_IRQ_1);
+ if (adc_pmic->btm_warm_irq < 0) {
+ rc = -ENXIO;
+ goto err_cleanup;
+ }
+
+ rc = request_irq(adc_pmic->btm_warm_irq,
+ pm8921_btm_warm_isr,
+ IRQF_TRIGGER_RISING, "pm8921_btm_warm_interrupt", adc_pmic);
+ if (rc) {
+ pr_err("btm warm irq failed %d with interrupt number %d\n",
+ rc, adc_pmic->btm_warm_irq);
+ dev_err(&pdev->dev, "failed to request btm irq\n");
+ goto err_cleanup;
+ }
+
+ disable_irq_nosync(adc_pmic->btm_warm_irq);
+
+ adc_pmic->btm_cold_irq = platform_get_irq(pdev, PM8921_ADC_IRQ_2);
+ if (adc_pmic->btm_cold_irq < 0) {
+ rc = -ENXIO;
+ goto err_cleanup;
+ }
+
+ rc = request_irq(adc_pmic->btm_cold_irq,
+ pm8921_btm_cold_isr,
+ IRQF_TRIGGER_RISING, "pm8921_btm_cold_interrupt", adc_pmic);
+ if (rc) {
+ pr_err("btm cold irq failed with return %d and number %d\n",
+ rc, adc_pmic->btm_cold_irq);
+ dev_err(&pdev->dev, "failed to request btm irq\n");
+ goto err_cleanup;
+ }
+
+ disable_irq_nosync(adc_pmic->btm_cold_irq);
+ device_init_wakeup(&pdev->dev, pdata->adc_wakeup);
+ platform_set_drvdata(pdev, adc_pmic);
+ pmic_adc = adc_pmic;
+
+ create_debugfs_entries();
+ pm8921_adc_calib_first_adc = false;
+ pm8921_btm_calib_first_adc = false;
+ pm8921_adc_calib_device_init = false;
+ pm8921_adc_initialized = true;
+ return 0;
+
+err_cleanup:
+ pm8921_adc_teardown(pdev);
+ return rc;
+}
+
+static struct platform_driver pm8921_adc_driver = {
+ .probe = pm8921_adc_probe,
+ .remove = __devexit_p(pm8921_adc_teardown),
+ .driver = {
+ .name = PM8921_ADC_DEV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pm8921_adc_init(void)
+{
+ return platform_driver_register(&pm8921_adc_driver);
+}
+module_init(pm8921_adc_init);
+
+static void __exit pm8921_adc_exit(void)
+{
+ platform_driver_unregister(&pm8921_adc_driver);
+}
+module_exit(pm8921_adc_exit);
+
+MODULE_ALIAS("platform:" PM8921_ADC_DEV_NAME);
+MODULE_DESCRIPTION("PMIC8921 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index e873b15..f088dc1 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
@@ -21,13 +21,38 @@
#include <linux/mfd/core.h>
#include <linux/mfd/pm8xxx/pm8921.h>
#include <linux/mfd/pm8xxx/core.h>
+#include <linux/leds-pm8xxx.h>
#define REG_HWREV 0x002 /* PMIC4 revision */
#define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */
+#define REG_MPP_BASE 0x050
+
+#define REG_TEMP_ALARM_CTRL 0x1B
+#define REG_TEMP_ALARM_PWM 0x9B
+
+#define REG_BATT_ALARM_THRESH 0x023
+#define REG_BATT_ALARM_CTRL1 0x024
+#define REG_BATT_ALARM_CTRL2 0x0AA
+#define REG_BATT_ALARM_PWM_CTRL 0x0A3
+
+#define PM8921_VERSION_MASK 0xFFF0
+#define PM8921_VERSION_VALUE 0x06F0
+#define PM8921_REVISION_MASK 0x000F
+
+#define SINGLE_IRQ_RESOURCE(_name, _irq) \
+{ \
+ .name = _name, \
+ .start = _irq, \
+ .end = _irq, \
+ .flags = IORESOURCE_IRQ, \
+}
+
struct pm8921 {
struct device *dev;
struct pm_irq_chip *irq_chip;
+ struct mfd_cell *mfd_regulators;
+ u32 rev_registers;
};
static int pm8921_readb(const struct device *dev, u16 addr, u8 *val)
@@ -72,25 +97,258 @@
return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
}
+static enum pm8xxx_version pm8921_get_version(const struct device *dev)
+{
+ const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
+ const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
+ enum pm8xxx_version version = -ENODEV;
+
+ if ((pmic->rev_registers & PM8921_VERSION_MASK) == PM8921_VERSION_VALUE)
+ version = PM8XXX_VERSION_8921;
+
+ return version;
+}
+
+static int pm8921_get_revision(const struct device *dev)
+{
+ const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
+ const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
+
+ return pmic->rev_registers & PM8921_REVISION_MASK;
+}
+
static struct pm8xxx_drvdata pm8921_drvdata = {
.pmic_readb = pm8921_readb,
.pmic_writeb = pm8921_writeb,
.pmic_read_buf = pm8921_read_buf,
.pmic_write_buf = pm8921_write_buf,
.pmic_read_irq_stat = pm8921_read_irq_stat,
+ .pmic_get_version = pm8921_get_version,
+ .pmic_get_revision = pm8921_get_revision,
};
-static int __devinit pm8921_add_subdevices(const struct pm8921_platform_data
- *pdata,
- struct pm8921 *pmic,
- u32 rev)
+static const struct resource gpio_cell_resources[] __devinitconst = {
+ [0] = {
+ .start = PM8921_IRQ_BLOCK_BIT(PM8921_GPIO_BLOCK_START, 0),
+ .end = PM8921_IRQ_BLOCK_BIT(PM8921_GPIO_BLOCK_START, 0)
+ + PM8921_NR_GPIOS - 1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell gpio_cell __devinitdata = {
+ .name = PM8XXX_GPIO_DEV_NAME,
+ .id = -1,
+ .resources = gpio_cell_resources,
+ .num_resources = ARRAY_SIZE(gpio_cell_resources),
+};
+
+static const struct resource adc_cell_resources[] __devinitconst = {
+ SINGLE_IRQ_RESOURCE(NULL, PM8921_ADC_EOC_USR_IRQ),
+ SINGLE_IRQ_RESOURCE(NULL, PM8921_ADC_BATT_TEMP_WARM_IRQ),
+ SINGLE_IRQ_RESOURCE(NULL, PM8921_ADC_BATT_TEMP_COLD_IRQ),
+};
+
+static struct mfd_cell adc_cell __devinitdata = {
+ .name = PM8921_ADC_DEV_NAME,
+ .id = -1,
+ .resources = adc_cell_resources,
+ .num_resources = ARRAY_SIZE(adc_cell_resources),
+};
+
+static const struct resource mpp_cell_resources[] __devinitconst = {
+ {
+ .start = PM8921_IRQ_BLOCK_BIT(PM8921_MPP_BLOCK_START, 0),
+ .end = PM8921_IRQ_BLOCK_BIT(PM8921_MPP_BLOCK_START, 0)
+ + PM8921_NR_MPPS - 1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell mpp_cell __devinitdata = {
+ .name = PM8XXX_MPP_DEV_NAME,
+ .id = -1,
+ .resources = mpp_cell_resources,
+ .num_resources = ARRAY_SIZE(mpp_cell_resources),
+};
+
+static const struct resource rtc_cell_resources[] __devinitconst = {
+ [0] = SINGLE_IRQ_RESOURCE(NULL, PM8921_RTC_ALARM_IRQ),
+ [1] = {
+ .name = "pmic_rtc_base",
+ .start = PM8921_RTC_BASE,
+ .end = PM8921_RTC_BASE,
+ .flags = IORESOURCE_IO,
+ },
+};
+
+static struct mfd_cell rtc_cell __devinitdata = {
+ .name = PM8XXX_RTC_DEV_NAME,
+ .id = -1,
+ .resources = rtc_cell_resources,
+ .num_resources = ARRAY_SIZE(rtc_cell_resources),
+};
+
+static const struct resource resources_pwrkey[] __devinitconst = {
+ SINGLE_IRQ_RESOURCE(NULL, PM8921_PWRKEY_REL_IRQ),
+ SINGLE_IRQ_RESOURCE(NULL, PM8921_PWRKEY_PRESS_IRQ),
+};
+
+static struct mfd_cell pwrkey_cell __devinitdata = {
+ .name = PM8XXX_PWRKEY_DEV_NAME,
+ .id = -1,
+ .num_resources = ARRAY_SIZE(resources_pwrkey),
+ .resources = resources_pwrkey,
+};
+
+static const struct resource resources_keypad[] = {
+ SINGLE_IRQ_RESOURCE(NULL, PM8921_KEYPAD_IRQ),
+ SINGLE_IRQ_RESOURCE(NULL, PM8921_KEYSTUCK_IRQ),
+};
+
+static struct mfd_cell keypad_cell __devinitdata = {
+ .name = PM8XXX_KEYPAD_DEV_NAME,
+ .id = -1,
+ .num_resources = ARRAY_SIZE(resources_keypad),
+ .resources = resources_keypad,
+};
+
+static struct mfd_cell debugfs_cell __devinitdata = {
+ .name = "pm8xxx-debug",
+ .id = -1,
+ .platform_data = "pm8921-dbg",
+ .pdata_size = sizeof("pm8921-dbg"),
+};
+
+static struct mfd_cell pwm_cell __devinitdata = {
+ .name = PM8XXX_PWM_DEV_NAME,
+ .id = -1,
+};
+
+static const struct resource charger_cell_resources[] __devinitconst = {
+ SINGLE_IRQ_RESOURCE("USBIN_VALID_IRQ", PM8921_USBIN_VALID_IRQ),
+ SINGLE_IRQ_RESOURCE("USBIN_OV_IRQ", PM8921_USBIN_OV_IRQ),
+ SINGLE_IRQ_RESOURCE("BATT_INSERTED_IRQ", PM8921_BATT_INSERTED_IRQ),
+ SINGLE_IRQ_RESOURCE("VBATDET_LOW_IRQ", PM8921_VBATDET_LOW_IRQ),
+ SINGLE_IRQ_RESOURCE("USBIN_UV_IRQ", PM8921_USBIN_UV_IRQ),
+ SINGLE_IRQ_RESOURCE("VBAT_OV_IRQ", PM8921_VBAT_OV_IRQ),
+ SINGLE_IRQ_RESOURCE("CHGWDOG_IRQ", PM8921_CHGWDOG_IRQ),
+ SINGLE_IRQ_RESOURCE("VCP_IRQ", PM8921_VCP_IRQ),
+ SINGLE_IRQ_RESOURCE("ATCDONE_IRQ", PM8921_ATCDONE_IRQ),
+ SINGLE_IRQ_RESOURCE("ATCFAIL_IRQ", PM8921_ATCFAIL_IRQ),
+ SINGLE_IRQ_RESOURCE("CHGDONE_IRQ", PM8921_CHGDONE_IRQ),
+ SINGLE_IRQ_RESOURCE("CHGFAIL_IRQ", PM8921_CHGFAIL_IRQ),
+ SINGLE_IRQ_RESOURCE("CHGSTATE_IRQ", PM8921_CHGSTATE_IRQ),
+ SINGLE_IRQ_RESOURCE("LOOP_CHANGE_IRQ", PM8921_LOOP_CHANGE_IRQ),
+ SINGLE_IRQ_RESOURCE("FASTCHG_IRQ", PM8921_FASTCHG_IRQ),
+ SINGLE_IRQ_RESOURCE("TRKLCHG_IRQ", PM8921_TRKLCHG_IRQ),
+ SINGLE_IRQ_RESOURCE("BATT_REMOVED_IRQ", PM8921_BATT_REMOVED_IRQ),
+ SINGLE_IRQ_RESOURCE("BATTTEMP_HOT_IRQ", PM8921_BATTTEMP_HOT_IRQ),
+ SINGLE_IRQ_RESOURCE("CHGHOT_IRQ", PM8921_CHGHOT_IRQ),
+ SINGLE_IRQ_RESOURCE("BATTTEMP_COLD_IRQ", PM8921_BATTTEMP_COLD_IRQ),
+ SINGLE_IRQ_RESOURCE("CHG_GONE_IRQ", PM8921_CHG_GONE_IRQ),
+ SINGLE_IRQ_RESOURCE("BAT_TEMP_OK_IRQ", PM8921_BAT_TEMP_OK_IRQ),
+ SINGLE_IRQ_RESOURCE("COARSE_DET_LOW_IRQ", PM8921_COARSE_DET_LOW_IRQ),
+ SINGLE_IRQ_RESOURCE("VDD_LOOP_IRQ", PM8921_VDD_LOOP_IRQ),
+ SINGLE_IRQ_RESOURCE("VREG_OV_IRQ", PM8921_VREG_OV_IRQ),
+ SINGLE_IRQ_RESOURCE("VBATDET_IRQ", PM8921_VBATDET_IRQ),
+ SINGLE_IRQ_RESOURCE("BATFET_IRQ", PM8921_BATFET_IRQ),
+ SINGLE_IRQ_RESOURCE("PSI_IRQ", PM8921_PSI_IRQ),
+ SINGLE_IRQ_RESOURCE("DCIN_VALID_IRQ", PM8921_DCIN_VALID_IRQ),
+ SINGLE_IRQ_RESOURCE("DCIN_OV_IRQ", PM8921_DCIN_OV_IRQ),
+ SINGLE_IRQ_RESOURCE("DCIN_UV_IRQ", PM8921_DCIN_UV_IRQ),
+};
+
+static const struct resource bms_cell_resources[] __devinitconst = {
+ SINGLE_IRQ_RESOURCE("PM8921_BMS_SBI_WRITE_OK", PM8921_BMS_SBI_WRITE_OK),
+ SINGLE_IRQ_RESOURCE("PM8921_BMS_CC_THR", PM8921_BMS_CC_THR),
+ SINGLE_IRQ_RESOURCE("PM8921_BMS_VSENSE_THR", PM8921_BMS_VSENSE_THR),
+ SINGLE_IRQ_RESOURCE("PM8921_BMS_VSENSE_FOR_R", PM8921_BMS_VSENSE_FOR_R),
+ SINGLE_IRQ_RESOURCE("PM8921_BMS_OCV_FOR_R", PM8921_BMS_OCV_FOR_R),
+ SINGLE_IRQ_RESOURCE("PM8921_BMS_GOOD_OCV", PM8921_BMS_GOOD_OCV),
+ SINGLE_IRQ_RESOURCE("PM8921_BMS_VSENSE_AVG", PM8921_BMS_VSENSE_AVG),
+};
+
+static struct mfd_cell charger_cell __devinitdata = {
+ .name = PM8921_CHARGER_DEV_NAME,
+ .id = -1,
+ .resources = charger_cell_resources,
+ .num_resources = ARRAY_SIZE(charger_cell_resources),
+};
+
+static struct mfd_cell bms_cell __devinitdata = {
+ .name = PM8921_BMS_DEV_NAME,
+ .id = -1,
+ .resources = bms_cell_resources,
+ .num_resources = ARRAY_SIZE(bms_cell_resources),
+};
+
+static struct mfd_cell misc_cell __devinitdata = {
+ .name = PM8XXX_MISC_DEV_NAME,
+ .id = -1,
+};
+
+static struct mfd_cell leds_cell __devinitdata = {
+ .name = PM8XXX_LEDS_DEV_NAME,
+ .id = -1,
+};
+
+static const struct resource thermal_alarm_cell_resources[] __devinitconst = {
+ SINGLE_IRQ_RESOURCE("pm8921_tempstat_irq", PM8921_TEMPSTAT_IRQ),
+ SINGLE_IRQ_RESOURCE("pm8921_overtemp_irq", PM8921_OVERTEMP_IRQ),
+};
+
+static struct pm8xxx_tm_core_data thermal_alarm_cdata = {
+ .adc_channel = CHANNEL_DIE_TEMP,
+ .adc_type = PM8XXX_TM_ADC_PM8921_ADC,
+ .reg_addr_temp_alarm_ctrl = REG_TEMP_ALARM_CTRL,
+ .reg_addr_temp_alarm_pwm = REG_TEMP_ALARM_PWM,
+ .tm_name = "pm8921_tz",
+ .irq_name_temp_stat = "pm8921_tempstat_irq",
+ .irq_name_over_temp = "pm8921_overtemp_irq",
+};
+
+static struct mfd_cell thermal_alarm_cell __devinitdata = {
+ .name = PM8XXX_TM_DEV_NAME,
+ .id = -1,
+ .resources = thermal_alarm_cell_resources,
+ .num_resources = ARRAY_SIZE(thermal_alarm_cell_resources),
+ .platform_data = &thermal_alarm_cdata,
+ .pdata_size = sizeof(struct pm8xxx_tm_core_data),
+};
+
+static const struct resource batt_alarm_cell_resources[] __devinitconst = {
+ SINGLE_IRQ_RESOURCE("pm8921_batt_alarm_irq", PM8921_BATT_ALARM_IRQ),
+};
+
+static struct pm8xxx_batt_alarm_core_data batt_alarm_cdata = {
+ .irq_name = "pm8921_batt_alarm_irq",
+ .reg_addr_threshold = REG_BATT_ALARM_THRESH,
+ .reg_addr_ctrl1 = REG_BATT_ALARM_CTRL1,
+ .reg_addr_ctrl2 = REG_BATT_ALARM_CTRL2,
+ .reg_addr_pwm_ctrl = REG_BATT_ALARM_PWM_CTRL,
+};
+
+static struct mfd_cell batt_alarm_cell __devinitdata = {
+ .name = PM8XXX_BATT_ALARM_DEV_NAME,
+ .id = -1,
+ .resources = batt_alarm_cell_resources,
+ .num_resources = ARRAY_SIZE(batt_alarm_cell_resources),
+ .platform_data = &batt_alarm_cdata,
+ .pdata_size = sizeof(struct pm8xxx_batt_alarm_core_data),
+};
+
+static int __devinit
+pm8921_add_subdevices(const struct pm8921_platform_data *pdata,
+ struct pm8921 *pmic)
{
int ret = 0, irq_base = 0;
struct pm_irq_chip *irq_chip;
+ static struct mfd_cell *mfd_regulators;
+ int i;
if (pdata->irq_pdata) {
pdata->irq_pdata->irq_cdata.nirqs = PM8921_NR_IRQS;
- pdata->irq_pdata->irq_cdata.rev = rev;
irq_base = pdata->irq_pdata->irq_base;
irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
@@ -101,16 +359,217 @@
}
pmic->irq_chip = irq_chip;
}
+
+ if (pdata->gpio_pdata) {
+ pdata->gpio_pdata->gpio_cdata.ngpios = PM8921_NR_GPIOS;
+ gpio_cell.platform_data = pdata->gpio_pdata;
+ gpio_cell.pdata_size = sizeof(struct pm8xxx_gpio_platform_data);
+ ret = mfd_add_devices(pmic->dev, 0, &gpio_cell, 1,
+ NULL, irq_base);
+ if (ret) {
+ pr_err("Failed to add gpio subdevice ret=%d\n", ret);
+ goto bail;
+ }
+ }
+
+ if (pdata->mpp_pdata) {
+ pdata->mpp_pdata->core_data.nmpps = PM8921_NR_MPPS;
+ pdata->mpp_pdata->core_data.base_addr = REG_MPP_BASE;
+ mpp_cell.platform_data = pdata->mpp_pdata;
+ mpp_cell.pdata_size = sizeof(struct pm8xxx_mpp_platform_data);
+ ret = mfd_add_devices(pmic->dev, 0, &mpp_cell, 1, NULL,
+ irq_base);
+ if (ret) {
+ pr_err("Failed to add mpp subdevice ret=%d\n", ret);
+ goto bail;
+ }
+ }
+
+ if (pdata->rtc_pdata) {
+ rtc_cell.platform_data = pdata->rtc_pdata;
+ rtc_cell.pdata_size = sizeof(struct pm8xxx_rtc_platform_data);
+ ret = mfd_add_devices(pmic->dev, 0, &rtc_cell, 1, NULL,
+ irq_base);
+ if (ret) {
+ pr_err("Failed to add rtc subdevice ret=%d\n", ret);
+ goto bail;
+ }
+ }
+
+ if (pdata->pwrkey_pdata) {
+ pwrkey_cell.platform_data = pdata->pwrkey_pdata;
+ pwrkey_cell.pdata_size =
+ sizeof(struct pm8xxx_pwrkey_platform_data);
+ ret = mfd_add_devices(pmic->dev, 0, &pwrkey_cell, 1, NULL,
+ irq_base);
+ if (ret) {
+ pr_err("Failed to add pwrkey subdevice ret=%d\n", ret);
+ goto bail;
+ }
+ }
+
+ if (pdata->keypad_pdata) {
+ keypad_cell.platform_data = pdata->keypad_pdata;
+ keypad_cell.pdata_size =
+ sizeof(struct pm8xxx_keypad_platform_data);
+ ret = mfd_add_devices(pmic->dev, 0, &keypad_cell, 1, NULL,
+ irq_base);
+ if (ret) {
+ pr_err("Failed to add keypad subdevice ret=%d\n", ret);
+ goto bail;
+ }
+ }
+
+ if (pdata->charger_pdata) {
+ pdata->charger_pdata->charger_cdata.vbat_channel = CHANNEL_VBAT;
+ charger_cell.platform_data = pdata->charger_pdata;
+ charger_cell.pdata_size =
+ sizeof(struct pm8921_charger_platform_data);
+ ret = mfd_add_devices(pmic->dev, 0, &charger_cell, 1, NULL,
+ irq_base);
+ if (ret) {
+ pr_err("Failed to add charger subdevice ret=%d\n", ret);
+ goto bail;
+ }
+ }
+
+ if (pdata->adc_pdata) {
+ adc_cell.platform_data = pdata->adc_pdata;
+ adc_cell.pdata_size =
+ sizeof(struct pm8921_adc_platform_data);
+ ret = mfd_add_devices(pmic->dev, 0, &adc_cell, 1, NULL,
+ irq_base);
+ if (ret) {
+ pr_err("Failed to add regulator subdevices ret=%d\n",
+ ret);
+ }
+ }
+
+ if (pdata->bms_pdata) {
+ bms_cell.platform_data = pdata->bms_pdata;
+ bms_cell.pdata_size =
+ sizeof(struct pm8921_bms_platform_data);
+ ret = mfd_add_devices(pmic->dev, 0, &bms_cell, 1, NULL,
+ irq_base);
+ if (ret) {
+ pr_err("Failed to add bms subdevice ret=%d\n", ret);
+ goto bail;
+ }
+ }
+
+ /* Add one device for each regulator used by the board. */
+ if (pdata->num_regulators > 0 && pdata->regulator_pdatas) {
+ mfd_regulators = kzalloc(sizeof(struct mfd_cell)
+ * (pdata->num_regulators), GFP_KERNEL);
+ if (!mfd_regulators) {
+ pr_err("Cannot allocate %d bytes for pm8921 regulator "
+ "mfd cells\n", sizeof(struct mfd_cell)
+ * (pdata->num_regulators));
+ ret = -ENOMEM;
+ goto bail;
+ }
+ for (i = 0; i < pdata->num_regulators; i++) {
+ mfd_regulators[i].name = PM8921_REGULATOR_DEV_NAME;
+ mfd_regulators[i].id = pdata->regulator_pdatas[i].id;
+ mfd_regulators[i].platform_data =
+ &(pdata->regulator_pdatas[i]);
+ mfd_regulators[i].pdata_size =
+ sizeof(struct pm8921_regulator_platform_data);
+ }
+ ret = mfd_add_devices(pmic->dev, 0, mfd_regulators,
+ pdata->num_regulators, NULL, irq_base);
+ if (ret) {
+ pr_err("Failed to add regulator subdevices ret=%d\n",
+ ret);
+ kfree(mfd_regulators);
+ goto bail;
+ }
+ pmic->mfd_regulators = mfd_regulators;
+ }
+
+ ret = mfd_add_devices(pmic->dev, 0, &debugfs_cell, 1, NULL, irq_base);
+ if (ret) {
+ pr_err("Failed to add debugfs subdevice ret=%d\n", ret);
+ goto bail;
+ }
+
+ ret = mfd_add_devices(pmic->dev, 0, &pwm_cell, 1, NULL, 0);
+ if (ret) {
+ pr_err("Failed to add pwm subdevice ret=%d\n", ret);
+ goto bail;
+ }
+
+ if (pdata->misc_pdata) {
+ misc_cell.platform_data = pdata->misc_pdata;
+ misc_cell.pdata_size = sizeof(struct pm8xxx_misc_platform_data);
+ ret = mfd_add_devices(pmic->dev, 0, &misc_cell, 1, NULL,
+ irq_base);
+ if (ret) {
+ pr_err("Failed to add misc subdevice ret=%d\n", ret);
+ goto bail;
+ }
+ }
+
+ if (pdata->leds_pdata) {
+ /* PM8921 supports only 4 LED DRVs */
+ for (i = 0; i < pdata->leds_pdata->num_leds; i++) {
+ if (pdata->leds_pdata->leds[i].flags >
+ PM8XXX_ID_LED_2) {
+ pr_err("%s: LED %d not supported\n", __func__,
+ pdata->leds_pdata->leds[i].flags);
+ goto bail;
+ }
+ }
+ leds_cell.platform_data = pdata->leds_pdata;
+ leds_cell.pdata_size = sizeof(struct led_platform_data);
+ ret = mfd_add_devices(pmic->dev, 0, &leds_cell, 1, NULL, 0);
+ if (ret) {
+ pr_err("Failed to add leds subdevice ret=%d\n", ret);
+ goto bail;
+ }
+ }
+
+ ret = mfd_add_devices(pmic->dev, 0, &thermal_alarm_cell, 1, NULL,
+ irq_base);
+ if (ret) {
+ pr_err("Failed to add thermal alarm subdevice ret=%d\n",
+ ret);
+ goto bail;
+ }
+
+ ret = mfd_add_devices(pmic->dev, 0, &batt_alarm_cell, 1, NULL,
+ irq_base);
+ if (ret) {
+ pr_err("Failed to add battery alarm subdevice ret=%d\n",
+ ret);
+ goto bail;
+ }
+
+ return 0;
+bail:
+ if (pmic->irq_chip) {
+ pm8xxx_irq_exit(pmic->irq_chip);
+ pmic->irq_chip = NULL;
+ }
return ret;
}
+static const char * const pm8921_rev_names[] = {
+ [PM8XXX_REVISION_8921_TEST] = "test",
+ [PM8XXX_REVISION_8921_1p0] = "1.0",
+ [PM8XXX_REVISION_8921_1p1] = "1.1",
+ [PM8XXX_REVISION_8921_2p0] = "2.0",
+};
+
static int __devinit pm8921_probe(struct platform_device *pdev)
{
const struct pm8921_platform_data *pdata = pdev->dev.platform_data;
+ const char *revision_name = "unknown";
struct pm8921 *pmic;
+ enum pm8xxx_version version;
+ int revision;
int rc;
u8 val;
- u32 rev;
if (!pdata) {
pr_err("missing platform data\n");
@@ -130,7 +589,7 @@
goto err_read_rev;
}
pr_info("PMIC revision 1: %02X\n", val);
- rev = val;
+ pmic->rev_registers = val;
/* Read PMIC chip revision 2 */
rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val));
@@ -140,13 +599,24 @@
goto err_read_rev;
}
pr_info("PMIC revision 2: %02X\n", val);
- rev |= val << BITS_PER_BYTE;
+ pmic->rev_registers |= val << BITS_PER_BYTE;
pmic->dev = &pdev->dev;
pm8921_drvdata.pm_chip_data = pmic;
platform_set_drvdata(pdev, &pm8921_drvdata);
- rc = pm8921_add_subdevices(pdata, pmic, rev);
+ /* Print out human readable version and revision names. */
+ version = pm8xxx_get_version(pmic->dev);
+ if (version == PM8XXX_VERSION_8921) {
+ revision = pm8xxx_get_revision(pmic->dev);
+ if (revision >= 0 && revision < ARRAY_SIZE(pm8921_rev_names))
+ revision_name = pm8921_rev_names[revision];
+ pr_info("PMIC version: PM8921 rev %s\n", revision_name);
+ } else {
+ WARN_ON(version != PM8XXX_VERSION_8921);
+ }
+
+ rc = pm8921_add_subdevices(pdata, pmic);
if (rc) {
pr_err("Cannot add subdevices rc=%d\n", rc);
goto err;
@@ -180,6 +650,7 @@
pmic->irq_chip = NULL;
}
platform_set_drvdata(pdev, NULL);
+ kfree(pmic->mfd_regulators);
kfree(pmic);
return 0;
@@ -198,7 +669,7 @@
{
return platform_driver_register(&pm8921_driver);
}
-subsys_initcall(pm8921_init);
+postcore_initcall(pm8921_init);
static void __exit pm8921_exit(void)
{
diff --git a/drivers/mfd/pm8xxx-batt-alarm.c b/drivers/mfd/pm8xxx-batt-alarm.c
new file mode 100644
index 0000000..92ade1c
--- /dev/null
+++ b/drivers/mfd/pm8xxx-batt-alarm.c
@@ -0,0 +1,805 @@
+/* Copyright (c) 2011, Code Aurora Forum. 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 and
+ * only 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.
+ */
+/*
+ * Qualcomm PMIC PM8xxx Battery Alarm driver
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/batt-alarm.h>
+
+/* Available voltage threshold values */
+#define THRESHOLD_MIN_MV 2500
+#define THRESHOLD_MAX_MV 5675
+#define THRESHOLD_STEP_MV 25
+
+/* Register bit definitions */
+
+/* Threshold register */
+#define THRESHOLD_UPPER_MASK 0xF0
+#define THRESHOLD_LOWER_MASK 0x0F
+#define THRESHOLD_UPPER_SHIFT 4
+#define THRESHOLD_LOWER_SHIFT 0
+
+/* CTRL 1 register */
+#define CTRL1_BATT_ALARM_ENABLE_MASK 0x80
+#define CTRL1_BATT_ALARM_ENABLE 0x80
+#define CTRL1_BATT_ALARM_DISABLE 0x00
+#define CTRL1_HOLD_TIME_MASK 0x70
+#define CTRL1_STATUS_UPPER_MASK 0x02
+#define CTRL1_STATUS_LOWER_MASK 0x01
+#define CTRL1_HOLD_TIME_SHIFT 4
+#define CTRL1_HOLD_TIME_MIN 0
+#define CTRL1_HOLD_TIME_MAX 7
+
+/* CTRL 2 register */
+#define CTRL2_COMP_UPPER_DISABLE_MASK 0x80
+#define CTRL2_COMP_UPPER_ENABLE 0x00
+#define CTRL2_COMP_UPPER_DISABLE 0x80
+#define CTRL2_COMP_LOWER_DISABLE_MASK 0x40
+#define CTRL2_COMP_LOWER_ENABLE 0x00
+#define CTRL2_COMP_LOWER_DISABLE 0x40
+#define CTRL2_FINE_STEP_UPPER_MASK 0x30
+#define CTRL2_RANGE_EXT_UPPER_MASK 0x08
+#define CTRL2_FINE_STEP_LOWER_MASK 0x06
+#define CTRL2_RANGE_EXT_LOWER_MASK 0x01
+#define CTRL2_FINE_STEP_UPPER_SHIFT 4
+#define CTRL2_FINE_STEP_LOWER_SHIFT 1
+
+/* PWM control register */
+#define PWM_CTRL_ALARM_EN_MASK 0xC0
+#define PWM_CTRL_ALARM_EN_NEVER 0x00
+#define PWM_CTRL_ALARM_EN_TCXO 0x40
+#define PWM_CTRL_ALARM_EN_PWM 0x80
+#define PWM_CTRL_ALARM_EN_ALWAYS 0xC0
+#define PWM_CTRL_PRE_MASK 0x38
+#define PWM_CTRL_DIV_MASK 0x07
+#define PWM_CTRL_PRE_SHIFT 3
+#define PWM_CTRL_DIV_SHIFT 0
+#define PWM_CTRL_PRE_MIN 0
+#define PWM_CTRL_PRE_MAX 7
+#define PWM_CTRL_DIV_MIN 1
+#define PWM_CTRL_DIV_MAX 7
+
+/* PWM control input range */
+#define PWM_CTRL_PRE_INPUT_MIN 2
+#define PWM_CTRL_PRE_INPUT_MAX 9
+#define PWM_CTRL_DIV_INPUT_MIN 2
+#define PWM_CTRL_DIV_INPUT_MAX 8
+
+/* Available voltage threshold values */
+#define THRESHOLD_BASIC_MIN_MV 2800
+#define THRESHOLD_EXT_MIN_MV 4400
+
+/*
+ * Default values used during initialization:
+ * Slowest PWM rate to ensure minimal status jittering when crossing thresholds.
+ * Largest hold time also helps reduce status value jittering. Comparators
+ * are disabled by default and must be turned on by calling
+ * pm8xxx_batt_alarm_state_set.
+ */
+#define DEFAULT_THRESHOLD_LOWER 3200
+#define DEFAULT_THRESHOLD_UPPER 4300
+#define DEFAULT_HOLD_TIME PM8XXX_BATT_ALARM_HOLD_TIME_16_MS
+#define DEFAULT_USE_PWM 1
+#define DEFAULT_PWM_SCALER 9
+#define DEFAULT_PWM_DIVIDER 8
+#define DEFAULT_LOWER_ENABLE 0
+#define DEFAULT_UPPER_ENABLE 0
+
+struct pm8xxx_batt_alarm_chip {
+ struct pm8xxx_batt_alarm_core_data cdata;
+ struct srcu_notifier_head irq_notifier_list;
+ struct work_struct irq_work;
+ struct device *dev;
+ struct mutex lock;
+ unsigned int irq;
+ int notifier_count;
+ u8 reg_threshold;
+ u8 reg_ctrl1;
+ u8 reg_ctrl2;
+ u8 reg_pwm_ctrl;
+};
+static struct pm8xxx_batt_alarm_chip *the_battalarm;
+
+static int pm8xxx_reg_write(struct pm8xxx_batt_alarm_chip *chip, u16 addr,
+ u8 val, u8 mask, u8 *reg_save)
+{
+ int rc = 0;
+ u8 reg;
+
+ reg = (*reg_save & ~mask) | (val & mask);
+ if (reg != *reg_save)
+ rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
+ if (rc)
+ pr_err("pm8xxx_writeb failed; addr=%03X, rc=%d\n", addr, rc);
+ else
+ *reg_save = reg;
+ return rc;
+}
+
+/**
+ * pm8xxx_batt_alarm_enable - enable one of the battery voltage threshold
+ * comparators
+ * @comparator: selects which comparator to enable
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_batt_alarm_enable(enum pm8xxx_batt_alarm_comparator comparator)
+{
+ struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+ int rc;
+ u8 val_ctrl2 = 0, mask_ctrl2 = 0;
+
+ if (!chip) {
+ pr_err("no battery alarm device found.\n");
+ return -ENODEV;
+ }
+
+ if (comparator < 0 || comparator > PM8XXX_BATT_ALARM_UPPER_COMPARATOR) {
+ pr_err("invalid comparator ID number: %d\n", comparator);
+ return -EINVAL;
+ }
+
+ if (comparator == PM8XXX_BATT_ALARM_LOWER_COMPARATOR) {
+ val_ctrl2 = CTRL2_COMP_LOWER_ENABLE;
+ mask_ctrl2 = CTRL2_COMP_LOWER_DISABLE_MASK;
+ } else {
+ val_ctrl2 = CTRL2_COMP_UPPER_ENABLE;
+ mask_ctrl2 = CTRL2_COMP_UPPER_DISABLE_MASK;
+ }
+
+ mutex_lock(&chip->lock);
+
+ /* Enable the battery alarm block. */
+ rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_ctrl1,
+ CTRL1_BATT_ALARM_ENABLE,
+ CTRL1_BATT_ALARM_ENABLE_MASK, &chip->reg_ctrl1);
+ if (rc)
+ goto bail;
+
+ /* Enable the individual comparators. */
+ rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_ctrl2, val_ctrl2,
+ mask_ctrl2, &chip->reg_ctrl2);
+
+bail:
+ mutex_unlock(&chip->lock);
+ return rc;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_enable);
+
+/**
+ * pm8xxx_batt_alarm_disable - disable one of the battery voltage threshold
+ * comparators
+ * @comparator: selects which comparator to disable
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_batt_alarm_disable(enum pm8xxx_batt_alarm_comparator comparator)
+{
+ struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+ int rc;
+ u8 val_ctrl1 = 0, val_ctrl2 = 0, mask_ctrl2 = 0;
+
+ if (!chip) {
+ pr_err("no battery alarm device found.\n");
+ return -ENODEV;
+ }
+
+ if (comparator < 0 || comparator > PM8XXX_BATT_ALARM_UPPER_COMPARATOR) {
+ pr_err("invalid comparator ID number: %d\n", comparator);
+ return -EINVAL;
+ }
+
+ if (comparator == PM8XXX_BATT_ALARM_LOWER_COMPARATOR) {
+ val_ctrl2 = CTRL2_COMP_LOWER_DISABLE;
+ mask_ctrl2 = CTRL2_COMP_LOWER_DISABLE_MASK;
+ } else {
+ val_ctrl2 = CTRL2_COMP_UPPER_DISABLE;
+ mask_ctrl2 = CTRL2_COMP_UPPER_DISABLE_MASK;
+ }
+
+ mutex_lock(&chip->lock);
+
+ /* Disable the specified comparator. */
+ rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_ctrl2, val_ctrl2,
+ mask_ctrl2, &chip->reg_ctrl2);
+ if (rc)
+ goto bail;
+
+ /* Disable the battery alarm block if both comparators are disabled. */
+ val_ctrl2 = chip->reg_ctrl2
+ & (CTRL2_COMP_LOWER_DISABLE_MASK | CTRL2_COMP_UPPER_DISABLE_MASK);
+ if (val_ctrl2 == (CTRL2_COMP_LOWER_DISABLE | CTRL2_COMP_UPPER_DISABLE))
+ val_ctrl1 = CTRL1_BATT_ALARM_DISABLE;
+ else
+ val_ctrl1 = CTRL1_BATT_ALARM_ENABLE;
+
+ rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_ctrl1, val_ctrl1,
+ CTRL1_BATT_ALARM_ENABLE_MASK, &chip->reg_ctrl1);
+
+bail:
+ mutex_unlock(&chip->lock);
+ return rc;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_disable);
+
+/**
+ * pm8xxx_batt_alarm_threshold_set - set the lower and upper alarm thresholds
+ * @comparator: selects which comparator to set the threshold of
+ * @threshold_mV: battery voltage threshold in millivolts
+ * set points = 2500-5675 mV in 25 mV steps
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_batt_alarm_threshold_set(
+ enum pm8xxx_batt_alarm_comparator comparator, int threshold_mV)
+{
+ struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+ int step, fine_step, rc;
+ u8 val_threshold = 0, val_ctrl2 = 0;
+ int threshold_mask, threshold_shift, range_ext_mask, fine_step_mask;
+ int fine_step_shift;
+
+ if (!chip) {
+ pr_err("no battery alarm device found.\n");
+ return -ENXIO;
+ }
+
+ if (comparator < 0 || comparator > PM8XXX_BATT_ALARM_UPPER_COMPARATOR) {
+ pr_err("invalid comparator ID number: %d\n", comparator);
+ return -EINVAL;
+ }
+
+ if (threshold_mV < THRESHOLD_MIN_MV
+ || threshold_mV > THRESHOLD_MAX_MV) {
+ pr_err("threshold value, %d mV, is outside of allowable "
+ "range: [%d, %d] mV\n", threshold_mV,
+ THRESHOLD_MIN_MV, THRESHOLD_MAX_MV);
+ return -EINVAL;
+ }
+
+ if (comparator == PM8XXX_BATT_ALARM_LOWER_COMPARATOR) {
+ threshold_mask = THRESHOLD_LOWER_MASK;
+ threshold_shift = THRESHOLD_LOWER_SHIFT;
+ range_ext_mask = CTRL2_RANGE_EXT_LOWER_MASK;
+ fine_step_mask = CTRL2_FINE_STEP_LOWER_MASK;
+ fine_step_shift = CTRL2_FINE_STEP_LOWER_SHIFT;
+ } else {
+ threshold_mask = THRESHOLD_UPPER_MASK;
+ threshold_shift = THRESHOLD_UPPER_SHIFT;
+ range_ext_mask = CTRL2_RANGE_EXT_UPPER_MASK;
+ fine_step_mask = CTRL2_FINE_STEP_UPPER_MASK;
+ fine_step_shift = CTRL2_FINE_STEP_UPPER_SHIFT;
+ }
+
+ /* Determine register settings to achieve the threshold. */
+ if (threshold_mV < THRESHOLD_BASIC_MIN_MV) {
+ /* Extended low range */
+ val_ctrl2 |= range_ext_mask;
+
+ step = (threshold_mV - THRESHOLD_MIN_MV) / THRESHOLD_STEP_MV;
+
+ fine_step = step & 0x3;
+ /* Extended low range is for steps 0 to 2 */
+ step >>= 2;
+ } else if (threshold_mV >= THRESHOLD_EXT_MIN_MV) {
+ /* Extended high range */
+ val_ctrl2 |= range_ext_mask;
+
+ step = (threshold_mV - THRESHOLD_EXT_MIN_MV)
+ / THRESHOLD_STEP_MV;
+
+ fine_step = step & 0x3;
+ /* Extended high range is for steps 3 to 15 */
+ step = (step >> 2) + 3;
+ } else {
+ /* Basic range */
+ step = (threshold_mV - THRESHOLD_BASIC_MIN_MV)
+ / THRESHOLD_STEP_MV;
+
+ fine_step = step & 0x3;
+ step >>= 2;
+ }
+ val_threshold |= step << threshold_shift;
+ val_ctrl2 |= (fine_step << fine_step_shift) & fine_step_mask;
+
+ mutex_lock(&chip->lock);
+ rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_threshold,
+ val_threshold, threshold_mask, &chip->reg_threshold);
+ if (rc)
+ goto bail;
+
+ rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_ctrl2, val_ctrl2,
+ range_ext_mask | fine_step_mask, &chip->reg_ctrl2);
+
+bail:
+ mutex_unlock(&chip->lock);
+ return rc;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_threshold_set);
+
+/**
+ * pm8xxx_batt_alarm_status_read - get status of both threshold comparators
+ *
+ * RETURNS: < 0 = error
+ * 0 = battery voltage ok
+ * BIT(0) set = battery voltage below lower threshold
+ * BIT(1) set = battery voltage above upper threshold
+ */
+int pm8xxx_batt_alarm_status_read(void)
+{
+ struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+ int status, rc;
+
+ if (!chip) {
+ pr_err("no battery alarm device found.\n");
+ return -ENXIO;
+ }
+
+ mutex_lock(&chip->lock);
+ rc = pm8xxx_readb(chip->dev->parent, chip->cdata.reg_addr_ctrl1,
+ &chip->reg_ctrl1);
+
+ status = ((chip->reg_ctrl1 & CTRL1_STATUS_LOWER_MASK)
+ ? PM8XXX_BATT_ALARM_STATUS_BELOW_LOWER : 0)
+ | ((chip->reg_ctrl1 & CTRL1_STATUS_UPPER_MASK)
+ ? PM8XXX_BATT_ALARM_STATUS_ABOVE_UPPER : 0);
+ mutex_unlock(&chip->lock);
+
+ if (rc) {
+ pr_err("pm8xxx_readb failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ return status;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_status_read);
+
+/**
+ * pm8xxx_batt_alarm_hold_time_set - set hold time of interrupt output *
+ * @hold_time: amount of time that battery voltage must remain outside of the
+ * threshold range before the battery alarm interrupt triggers
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_batt_alarm_hold_time_set(enum pm8xxx_batt_alarm_hold_time hold_time)
+{
+ struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+ int rc;
+ u8 reg_ctrl1 = 0;
+
+ if (!chip) {
+ pr_err("no battery alarm device found.\n");
+ return -ENXIO;
+ }
+
+ if (hold_time < CTRL1_HOLD_TIME_MIN
+ || hold_time > CTRL1_HOLD_TIME_MAX) {
+
+ pr_err("hold time, %d, is outside of allowable range: "
+ "[%d, %d]\n", hold_time, CTRL1_HOLD_TIME_MIN,
+ CTRL1_HOLD_TIME_MAX);
+ return -EINVAL;
+ }
+
+ reg_ctrl1 = hold_time << CTRL1_HOLD_TIME_SHIFT;
+
+ mutex_lock(&chip->lock);
+ rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_ctrl1, reg_ctrl1,
+ CTRL1_HOLD_TIME_MASK, &chip->reg_ctrl1);
+ mutex_unlock(&chip->lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_hold_time_set);
+
+/**
+ * pm8xxx_batt_alarm_pwm_rate_set - set battery alarm update rate *
+ * @use_pwm: 1 = use PWM update rate, 0 = comparators always active
+ * @clock_scaler: PWM clock scaler = 2 to 9
+ * @clock_divider: PWM clock divider = 2 to 8
+ *
+ * This function sets the rate at which the battery alarm module enables
+ * the threshold comparators. The rate is determined by the following equation:
+ *
+ * f_update = (1024 Hz) / (clock_divider * (2 ^ clock_scaler))
+ *
+ * Thus, the update rate can range from 0.25 Hz to 128 Hz.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_batt_alarm_pwm_rate_set(int use_pwm, int clock_scaler,
+ int clock_divider)
+{
+ struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+ int rc;
+ u8 reg_pwm_ctrl = 0, mask = 0;
+
+ if (!chip) {
+ pr_err("no battery alarm device found.\n");
+ return -ENXIO;
+ }
+
+ if (use_pwm && (clock_scaler < PWM_CTRL_PRE_INPUT_MIN
+ || clock_scaler > PWM_CTRL_PRE_INPUT_MAX)) {
+ pr_err("PWM clock scaler, %d, is outside of allowable range: "
+ "[%d, %d]\n", clock_scaler, PWM_CTRL_PRE_INPUT_MIN,
+ PWM_CTRL_PRE_INPUT_MAX);
+ return -EINVAL;
+ }
+
+ if (use_pwm && (clock_divider < PWM_CTRL_DIV_INPUT_MIN
+ || clock_divider > PWM_CTRL_DIV_INPUT_MAX)) {
+ pr_err("PWM clock divider, %d, is outside of allowable range: "
+ "[%d, %d]\n", clock_divider, PWM_CTRL_DIV_INPUT_MIN,
+ PWM_CTRL_DIV_INPUT_MAX);
+ return -EINVAL;
+ }
+
+ if (!use_pwm) {
+ /* Turn off PWM control and always enable. */
+ reg_pwm_ctrl = PWM_CTRL_ALARM_EN_ALWAYS;
+ mask = PWM_CTRL_ALARM_EN_MASK;
+ } else {
+ /* Use PWM control. */
+ reg_pwm_ctrl = PWM_CTRL_ALARM_EN_PWM;
+ mask = PWM_CTRL_ALARM_EN_MASK | PWM_CTRL_PRE_MASK
+ | PWM_CTRL_DIV_MASK;
+
+ clock_scaler -= PWM_CTRL_PRE_INPUT_MIN - PWM_CTRL_PRE_MIN;
+ clock_divider -= PWM_CTRL_DIV_INPUT_MIN - PWM_CTRL_DIV_MIN;
+
+ reg_pwm_ctrl |= (clock_scaler << PWM_CTRL_PRE_SHIFT)
+ & PWM_CTRL_PRE_MASK;
+ reg_pwm_ctrl |= (clock_divider << PWM_CTRL_DIV_SHIFT)
+ & PWM_CTRL_DIV_MASK;
+ }
+
+ mutex_lock(&chip->lock);
+ rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_pwm_ctrl, reg_pwm_ctrl,
+ mask, &chip->reg_pwm_ctrl);
+ mutex_unlock(&chip->lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_pwm_rate_set);
+
+/*
+ * Handle the BATT_ALARM interrupt:
+ * Battery voltage is above or below threshold range.
+ */
+static irqreturn_t pm8xxx_batt_alarm_isr(int irq, void *data)
+{
+ struct pm8xxx_batt_alarm_chip *chip = data;
+
+ disable_irq_nosync(chip->irq);
+ schedule_work(&chip->irq_work);
+
+ return IRQ_HANDLED;
+}
+
+static void pm8xxx_batt_alarm_isr_work(struct work_struct *work)
+{
+ struct pm8xxx_batt_alarm_chip *chip
+ = container_of(work, struct pm8xxx_batt_alarm_chip, irq_work);
+ int status;
+
+ if (chip) {
+ status = pm8xxx_batt_alarm_status_read();
+
+ if (status < 0)
+ pr_err("failed to read status, rc=%d\n", status);
+ else
+ srcu_notifier_call_chain(&chip->irq_notifier_list,
+ status, NULL);
+ }
+
+ enable_irq(chip->irq);
+}
+
+/**
+ * pm8xxx_batt_alarm_register_notifier - register a notifier to run when a
+ * battery voltage change interrupt fires
+ * @nb: notifier block containing callback function to register
+ *
+ * nb->notifier_call must point to a function of this form -
+ * int (*notifier_call)(struct notifier_block *nb, unsigned long status,
+ * void *unused);
+ * "status" will receive the battery alarm status; "unused" will be NULL.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_batt_alarm_register_notifier(struct notifier_block *nb)
+{
+ struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+ int rc;
+
+ if (!chip) {
+ pr_err("no battery alarm device found.\n");
+ return -ENXIO;
+ }
+
+ rc = srcu_notifier_chain_register(&chip->irq_notifier_list, nb);
+ mutex_lock(&chip->lock);
+ if (rc == 0) {
+ if (chip->notifier_count == 0) {
+ enable_irq(chip->irq);
+ rc = irq_set_irq_wake(chip->irq, 1);
+ }
+
+ chip->notifier_count++;
+ }
+
+ mutex_unlock(&chip->lock);
+ return rc;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_register_notifier);
+
+/**
+ * pm8xxx_batt_alarm_unregister_notifier - unregister a notifier that is run
+ * when a battery voltage change interrupt fires
+ * @nb: notifier block containing callback function to unregister
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_batt_alarm_unregister_notifier(struct notifier_block *nb)
+{
+ struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+ int rc;
+
+ if (!chip) {
+ pr_err("no battery alarm device found.\n");
+ return -ENXIO;
+ }
+
+ rc = srcu_notifier_chain_unregister(&chip->irq_notifier_list, nb);
+ if (rc == 0) {
+ mutex_lock(&chip->lock);
+
+ chip->notifier_count--;
+
+ if (chip->notifier_count == 0) {
+ rc = irq_set_irq_wake(chip->irq, 0);
+ disable_irq(chip->irq);
+ }
+
+ WARN_ON(chip->notifier_count < 0);
+
+ mutex_unlock(&chip->lock);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_unregister_notifier);
+
+static int pm8xxx_batt_alarm_reg_init(struct pm8xxx_batt_alarm_chip *chip)
+{
+ int rc = 0;
+
+ /* save the current register states */
+ rc = pm8xxx_readb(chip->dev->parent, chip->cdata.reg_addr_threshold,
+ &chip->reg_threshold);
+ if (rc)
+ goto bail;
+
+ rc = pm8xxx_readb(chip->dev->parent, chip->cdata.reg_addr_ctrl1,
+ &chip->reg_ctrl1);
+ if (rc)
+ goto bail;
+
+ rc = pm8xxx_readb(chip->dev->parent, chip->cdata.reg_addr_ctrl2,
+ &chip->reg_ctrl2);
+ if (rc)
+ goto bail;
+
+ rc = pm8xxx_readb(chip->dev->parent, chip->cdata.reg_addr_pwm_ctrl,
+ &chip->reg_pwm_ctrl);
+ if (rc)
+ goto bail;
+
+bail:
+ if (rc)
+ pr_err("pm8xxx_readb failed; initial register states "
+ "unknown, rc=%d\n", rc);
+ return rc;
+}
+
+/* TODO: should this default setting function be removed? */
+static int pm8xxx_batt_alarm_config_defaults(void)
+{
+ int rc = 0;
+
+ /* Use default values when no platform data is provided. */
+ rc = pm8xxx_batt_alarm_threshold_set(PM8XXX_BATT_ALARM_LOWER_COMPARATOR,
+ DEFAULT_THRESHOLD_LOWER);
+ if (rc) {
+ pr_err("threshold_set failed, rc=%d\n", rc);
+ goto done;
+ }
+
+ rc = pm8xxx_batt_alarm_threshold_set(PM8XXX_BATT_ALARM_UPPER_COMPARATOR,
+ DEFAULT_THRESHOLD_UPPER);
+ if (rc) {
+ pr_err("threshold_set failed, rc=%d\n", rc);
+ goto done;
+ }
+
+ rc = pm8xxx_batt_alarm_hold_time_set(DEFAULT_HOLD_TIME);
+ if (rc) {
+ pr_err("hold_time_set failed, rc=%d\n", rc);
+ goto done;
+ }
+
+ rc = pm8xxx_batt_alarm_pwm_rate_set(DEFAULT_USE_PWM,
+ DEFAULT_PWM_SCALER, DEFAULT_PWM_DIVIDER);
+ if (rc) {
+ pr_err("pwm_rate_set failed, rc=%d\n", rc);
+ goto done;
+ }
+
+ rc = pm8xxx_batt_alarm_disable(PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
+ if (rc) {
+ pr_err("disable lower failed, rc=%d\n", rc);
+ goto done;
+ }
+
+ rc = pm8xxx_batt_alarm_disable(PM8XXX_BATT_ALARM_UPPER_COMPARATOR);
+ if (rc) {
+ pr_err("disable upper failed, rc=%d\n", rc);
+ goto done;
+ }
+
+done:
+ return rc;
+}
+
+static int __devinit pm8xxx_batt_alarm_probe(struct platform_device *pdev)
+{
+ const struct pm8xxx_batt_alarm_core_data *cdata
+ = pdev->dev.platform_data;
+ struct pm8xxx_batt_alarm_chip *chip;
+ struct resource *res;
+ int rc;
+
+ if (the_battalarm) {
+ pr_err("A PMIC battery alarm device has already probed.\n");
+ return -ENODEV;
+ }
+
+ if (!cdata) {
+ pr_err("missing core data\n");
+ return -EINVAL;
+ }
+
+ if (!cdata->irq_name) {
+ pr_err("missing IRQ name\n");
+ return -EINVAL;
+ }
+
+ chip = kzalloc(sizeof(struct pm8xxx_batt_alarm_chip), GFP_KERNEL);
+ if (chip == NULL) {
+ pr_err("kzalloc() failed.\n");
+ return -ENOMEM;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+ cdata->irq_name);
+ if (res) {
+ chip->irq = res->start;
+ } else {
+ pr_err("Battery alarm IRQ not specified\n");
+ rc = -EINVAL;
+ goto err_free_chip;
+ }
+
+ chip->dev = &pdev->dev;
+ memcpy(&(chip->cdata), cdata,
+ sizeof(struct pm8xxx_batt_alarm_core_data));
+
+ srcu_init_notifier_head(&chip->irq_notifier_list);
+
+ chip->notifier_count = 0;
+ mutex_init(&chip->lock);
+
+ the_battalarm = chip;
+
+ rc = pm8xxx_batt_alarm_reg_init(chip);
+ if (rc)
+ goto err_free_mutex;
+
+ rc = pm8xxx_batt_alarm_config_defaults();
+ if (rc)
+ goto err_free_mutex;
+
+ INIT_WORK(&chip->irq_work, pm8xxx_batt_alarm_isr_work);
+
+/* TODO: Is it best to trigger on both edges? Should this be configurable? */
+ rc = request_irq(chip->irq, pm8xxx_batt_alarm_isr,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, cdata->irq_name,
+ chip);
+ if (rc < 0) {
+ pr_err("request_irq(%d) failed, rc=%d\n", chip->irq, rc);
+ goto err_cancel_work;
+ }
+
+ /* Disable the IRQ until a notifier is registered. */
+ disable_irq(chip->irq);
+
+ platform_set_drvdata(pdev, chip);
+
+ return 0;
+
+err_cancel_work:
+ cancel_work_sync(&chip->irq_work);
+err_free_mutex:
+ mutex_destroy(&chip->lock);
+ srcu_cleanup_notifier_head(&chip->irq_notifier_list);
+err_free_chip:
+ kfree(chip);
+ the_battalarm = NULL;
+
+ return rc;
+}
+
+static int __devexit pm8xxx_batt_alarm_remove(struct platform_device *pdev)
+{
+ struct pm8xxx_batt_alarm_chip *chip = platform_get_drvdata(pdev);
+
+ if (chip) {
+ platform_set_drvdata(pdev, NULL);
+ irq_set_irq_wake(chip->irq, 0);
+ free_irq(chip->irq, chip);
+ cancel_work_sync(&chip->irq_work);
+ srcu_cleanup_notifier_head(&chip->irq_notifier_list);
+ mutex_destroy(&chip->lock);
+ kfree(chip);
+ the_battalarm = NULL;
+ }
+
+ return 0;
+}
+
+static struct platform_driver pm8xxx_batt_alarm_driver = {
+ .probe = pm8xxx_batt_alarm_probe,
+ .remove = __devexit_p(pm8xxx_batt_alarm_remove),
+ .driver = {
+ .name = PM8XXX_BATT_ALARM_DEV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pm8xxx_batt_alarm_init(void)
+{
+ return platform_driver_register(&pm8xxx_batt_alarm_driver);
+}
+
+static void __exit pm8xxx_batt_alarm_exit(void)
+{
+ platform_driver_unregister(&pm8xxx_batt_alarm_driver);
+}
+
+module_init(pm8xxx_batt_alarm_init);
+module_exit(pm8xxx_batt_alarm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC PM8xxx Battery Alarm");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_BATT_ALARM_DEV_NAME);
diff --git a/drivers/mfd/pm8xxx-debug.c b/drivers/mfd/pm8xxx-debug.c
new file mode 100644
index 0000000..3b69121
--- /dev/null
+++ b/drivers/mfd/pm8xxx-debug.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. 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 and
+ * only 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.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/debugfs.h>
+
+#define PM8XXX_DEBUG_DEV_NAME "pm8xxx-debug"
+
+struct pm8xxx_debug_device {
+ struct mutex debug_mutex;
+ struct device *parent;
+ struct dentry *dir;
+ int addr;
+};
+
+static bool pm8xxx_debug_addr_is_valid(int addr)
+{
+ if (addr < 0 || addr > 0x3FF) {
+ pr_err("PMIC register address is invalid: %d\n", addr);
+ return false;
+ }
+ return true;
+}
+
+static int pm8xxx_debug_data_set(void *data, u64 val)
+{
+ struct pm8xxx_debug_device *debugdev = data;
+ u8 reg = val;
+ int rc;
+
+ mutex_lock(&debugdev->debug_mutex);
+
+ if (pm8xxx_debug_addr_is_valid(debugdev->addr)) {
+ rc = pm8xxx_writeb(debugdev->parent, debugdev->addr, reg);
+
+ if (rc)
+ pr_err("pm8xxx_writeb(0x%03X)=0x%02X failed: rc=%d\n",
+ debugdev->addr, reg, rc);
+ }
+
+ mutex_unlock(&debugdev->debug_mutex);
+ return 0;
+}
+
+static int pm8xxx_debug_data_get(void *data, u64 *val)
+{
+ struct pm8xxx_debug_device *debugdev = data;
+ int rc;
+ u8 reg;
+
+ mutex_lock(&debugdev->debug_mutex);
+
+ if (pm8xxx_debug_addr_is_valid(debugdev->addr)) {
+ rc = pm8xxx_readb(debugdev->parent, debugdev->addr, ®);
+
+ if (rc)
+ pr_err("pm8xxx_readb(0x%03X) failed: rc=%d\n",
+ debugdev->addr, rc);
+ else
+ *val = reg;
+ }
+
+ mutex_unlock(&debugdev->debug_mutex);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_data_fops, pm8xxx_debug_data_get,
+ pm8xxx_debug_data_set, "0x%02llX\n");
+
+static int pm8xxx_debug_addr_set(void *data, u64 val)
+{
+ struct pm8xxx_debug_device *debugdev = data;
+
+ if (pm8xxx_debug_addr_is_valid(val)) {
+ mutex_lock(&debugdev->debug_mutex);
+ debugdev->addr = val;
+ mutex_unlock(&debugdev->debug_mutex);
+ }
+
+ return 0;
+}
+
+static int pm8xxx_debug_addr_get(void *data, u64 *val)
+{
+ struct pm8xxx_debug_device *debugdev = data;
+
+ mutex_lock(&debugdev->debug_mutex);
+
+ if (pm8xxx_debug_addr_is_valid(debugdev->addr))
+ *val = debugdev->addr;
+
+ mutex_unlock(&debugdev->debug_mutex);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_addr_fops, pm8xxx_debug_addr_get,
+ pm8xxx_debug_addr_set, "0x%03llX\n");
+
+static int __devinit pm8xxx_debug_probe(struct platform_device *pdev)
+{
+ char *name = pdev->dev.platform_data;
+ struct pm8xxx_debug_device *debugdev;
+ struct dentry *dir;
+ struct dentry *temp;
+ int rc;
+
+ if (name == NULL) {
+ pr_err("debugfs directory name must be specified in "
+ "platform_data pointer\n");
+ return -EINVAL;
+ }
+
+ debugdev = kzalloc(sizeof(struct pm8xxx_debug_device), GFP_KERNEL);
+ if (debugdev == NULL) {
+ pr_err("kzalloc failed\n");
+ return -ENOMEM;
+ }
+
+ mutex_init(&debugdev->debug_mutex);
+
+ debugdev->parent = pdev->dev.parent;
+ debugdev->addr = -1;
+
+ dir = debugfs_create_dir(name, NULL);
+ if (dir == NULL || IS_ERR(dir)) {
+ pr_err("debugfs_create_dir failed: rc=%ld\n", PTR_ERR(dir));
+ rc = PTR_ERR(dir);
+ goto dir_error;
+ }
+
+ temp = debugfs_create_file("addr", S_IRUSR | S_IWUSR, dir, debugdev,
+ &debug_addr_fops);
+ if (temp == NULL || IS_ERR(temp)) {
+ pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
+ rc = PTR_ERR(temp);
+ goto file_error;
+ }
+
+ temp = debugfs_create_file("data", S_IRUSR | S_IWUSR, dir, debugdev,
+ &debug_data_fops);
+ if (temp == NULL || IS_ERR(temp)) {
+ pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
+ rc = PTR_ERR(temp);
+ goto file_error;
+ }
+
+ debugdev->dir = dir;
+ platform_set_drvdata(pdev, debugdev);
+
+ return 0;
+
+file_error:
+ debugfs_remove_recursive(dir);
+dir_error:
+ kfree(debugdev);
+
+ return rc;
+}
+
+static int __devexit pm8xxx_debug_remove(struct platform_device *pdev)
+{
+ struct pm8xxx_debug_device *debugdev = platform_get_drvdata(pdev);
+
+ if (debugdev) {
+ debugfs_remove_recursive(debugdev->dir);
+ kfree(debugdev);
+ }
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver pm8xxx_debug_driver = {
+ .probe = pm8xxx_debug_probe,
+ .remove = __devexit_p(pm8xxx_debug_remove),
+ .driver = {
+ .name = PM8XXX_DEBUG_DEV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pm8xxx_debug_init(void)
+{
+ return platform_driver_register(&pm8xxx_debug_driver);
+}
+subsys_initcall(pm8xxx_debug_init);
+
+static void __exit pm8xxx_debug_exit(void)
+{
+ platform_driver_unregister(&pm8xxx_debug_driver);
+}
+module_exit(pm8xxx_debug_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PM8XXX Debug driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_DEBUG_DEV_NAME);
diff --git a/drivers/mfd/pm8xxx-irq.c b/drivers/mfd/pm8xxx-irq.c
index d452dd0..c04d0d1 100644
--- a/drivers/mfd/pm8xxx-irq.c
+++ b/drivers/mfd/pm8xxx-irq.c
@@ -247,12 +247,20 @@
return 0;
}
+static int pm8xxx_irq_read_line(struct irq_data *d)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+
+ return pm8xxx_get_irq_stat(chip, d->irq);
+}
+
static struct irq_chip pm8xxx_irq_chip = {
.name = "pm8xxx",
.irq_mask_ack = pm8xxx_irq_mask_ack,
.irq_unmask = pm8xxx_irq_unmask,
.irq_set_type = pm8xxx_irq_set_type,
.irq_set_wake = pm8xxx_irq_set_wake,
+ .irq_read_line = pm8xxx_irq_read_line,
.flags = IRQCHIP_MASK_ON_SUSPEND,
};
@@ -358,7 +366,7 @@
irq_set_irq_type(devirq, pdata->irq_trigger_flag);
irq_set_handler_data(devirq, chip);
irq_set_chained_handler(devirq, pm8xxx_irq_handler);
- set_irq_wake(devirq, 1);
+ irq_set_irq_wake(devirq, 1);
return chip;
}
diff --git a/drivers/mfd/pm8xxx-misc.c b/drivers/mfd/pm8xxx-misc.c
new file mode 100644
index 0000000..cd5624f
--- /dev/null
+++ b/drivers/mfd/pm8xxx-misc.c
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. 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 and
+ * only 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.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/misc.h>
+
+/* PON CTRL 1 register */
+#define REG_PM8058_PON_CTRL_1 0x01C
+#define REG_PM8921_PON_CTRL_1 0x01C
+
+#define PON_CTRL_1_PULL_UP_MASK 0xE0
+#define PON_CTRL_1_USB_PWR_EN 0x10
+
+#define PON_CTRL_1_WD_EN_MASK 0x08
+#define PON_CTRL_1_WD_EN_RESET 0x08
+#define PON_CTRL_1_WD_EN_PWR_OFF 0x00
+
+/* Regulator L22 control register */
+#define REG_PM8058_L22_CTRL 0x121
+
+/* SLEEP CTRL register */
+#define REG_PM8058_SLEEP_CTRL 0x02B
+#define REG_PM8921_SLEEP_CTRL 0x10A
+
+#define SLEEP_CTRL_SMPL_EN_MASK 0x04
+#define SLEEP_CTRL_SMPL_EN_RESET 0x04
+#define SLEEP_CTRL_SMPL_EN_PWR_OFF 0x00
+
+/* FTS regulator PMR registers */
+#define REG_PM8901_REGULATOR_S1_PMR 0xA7
+#define REG_PM8901_REGULATOR_S2_PMR 0xA8
+#define REG_PM8901_REGULATOR_S3_PMR 0xA9
+#define REG_PM8901_REGULATOR_S4_PMR 0xAA
+
+#define PM8901_REGULATOR_PMR_STATE_MASK 0x60
+#define PM8901_REGULATOR_PMR_STATE_OFF 0x20
+
+struct pm8xxx_misc_chip {
+ struct list_head link;
+ struct pm8xxx_misc_platform_data pdata;
+ struct device *dev;
+ enum pm8xxx_version version;
+};
+
+static LIST_HEAD(pm8xxx_misc_chips);
+static DEFINE_SPINLOCK(pm8xxx_misc_chips_lock);
+
+static int pm8xxx_misc_masked_write(struct pm8xxx_misc_chip *chip, u16 addr,
+ u8 mask, u8 val)
+{
+ int rc;
+ u8 reg;
+
+ rc = pm8xxx_readb(chip->dev->parent, addr, ®);
+ if (rc) {
+ pr_err("pm8xxx_readb(0x%03X) failed, rc=%d\n", addr, rc);
+ return rc;
+ }
+ reg &= ~mask;
+ reg |= val & mask;
+ rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
+ if (rc)
+ pr_err("pm8xxx_writeb(0x%03X)=0x%02X failed, rc=%d\n", addr,
+ reg, rc);
+ return rc;
+}
+
+static int __pm8058_reset_pwr_off(struct pm8xxx_misc_chip *chip, int reset)
+{
+ int rc;
+
+ /*
+ * Fix-up: Set regulator LDO22 to 1.225 V in high power mode. Leave its
+ * pull-down state intact. This ensures a safe shutdown.
+ */
+ rc = pm8xxx_misc_masked_write(chip, REG_PM8058_L22_CTRL, 0xBF, 0x93);
+ if (rc) {
+ pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
+ goto read_write_err;
+ }
+
+ /* Enable SMPL if resetting is desired. */
+ rc = pm8xxx_misc_masked_write(chip, REG_PM8058_SLEEP_CTRL,
+ SLEEP_CTRL_SMPL_EN_MASK,
+ (reset ? SLEEP_CTRL_SMPL_EN_RESET : SLEEP_CTRL_SMPL_EN_PWR_OFF));
+ if (rc) {
+ pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
+ goto read_write_err;
+ }
+
+ /*
+ * Select action to perform (reset or shutdown) when PS_HOLD goes low.
+ * Also ensure that KPD, CBL0, and CBL1 pull ups are enabled and that
+ * USB charging is enabled.
+ */
+ rc = pm8xxx_misc_masked_write(chip, REG_PM8058_PON_CTRL_1,
+ PON_CTRL_1_PULL_UP_MASK | PON_CTRL_1_USB_PWR_EN
+ | PON_CTRL_1_WD_EN_MASK,
+ PON_CTRL_1_PULL_UP_MASK | PON_CTRL_1_USB_PWR_EN
+ | (reset ? PON_CTRL_1_WD_EN_RESET : PON_CTRL_1_WD_EN_PWR_OFF));
+ if (rc) {
+ pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
+ goto read_write_err;
+ }
+
+read_write_err:
+ return rc;
+}
+
+static int __pm8901_reset_pwr_off(struct pm8xxx_misc_chip *chip, int reset)
+{
+ int rc = 0, i;
+ u8 pmr_addr[4] = {
+ REG_PM8901_REGULATOR_S2_PMR,
+ REG_PM8901_REGULATOR_S3_PMR,
+ REG_PM8901_REGULATOR_S4_PMR,
+ REG_PM8901_REGULATOR_S1_PMR,
+ };
+
+ /* Fix-up: Turn off regulators S1, S2, S3, S4 when shutting down. */
+ if (!reset) {
+ for (i = 0; i < 4; i++) {
+ rc = pm8xxx_misc_masked_write(chip, pmr_addr[i],
+ PM8901_REGULATOR_PMR_STATE_MASK,
+ PM8901_REGULATOR_PMR_STATE_OFF);
+ if (rc) {
+ pr_err("pm8xxx_misc_masked_write failed, "
+ "rc=%d\n", rc);
+ goto read_write_err;
+ }
+ }
+ }
+
+read_write_err:
+ return rc;
+}
+
+static int __pm8921_reset_pwr_off(struct pm8xxx_misc_chip *chip, int reset)
+{
+ int rc;
+
+ /* Enable SMPL if resetting is desired. */
+ rc = pm8xxx_misc_masked_write(chip, REG_PM8921_SLEEP_CTRL,
+ SLEEP_CTRL_SMPL_EN_MASK,
+ (reset ? SLEEP_CTRL_SMPL_EN_RESET : SLEEP_CTRL_SMPL_EN_PWR_OFF));
+ if (rc) {
+ pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
+ goto read_write_err;
+ }
+
+ /*
+ * Select action to perform (reset or shutdown) when PS_HOLD goes low.
+ * Also ensure that KPD, CBL0, and CBL1 pull ups are enabled and that
+ * USB charging is enabled.
+ */
+ rc = pm8xxx_misc_masked_write(chip, REG_PM8921_PON_CTRL_1,
+ PON_CTRL_1_PULL_UP_MASK | PON_CTRL_1_USB_PWR_EN
+ | PON_CTRL_1_WD_EN_MASK,
+ PON_CTRL_1_PULL_UP_MASK | PON_CTRL_1_USB_PWR_EN
+ | (reset ? PON_CTRL_1_WD_EN_RESET : PON_CTRL_1_WD_EN_PWR_OFF));
+ if (rc) {
+ pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
+ goto read_write_err;
+ }
+
+read_write_err:
+ return rc;
+}
+
+/**
+ * pm8xxx_reset_pwr_off - switch all PM8XXX PMIC chips attached to the system to
+ * either reset or shutdown when they are turned off
+ * @reset: 0 = shudown the PMICs, 1 = shutdown and then restart the PMICs
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_reset_pwr_off(int reset)
+{
+ struct pm8xxx_misc_chip *chip;
+ unsigned long flags;
+ int rc = 0;
+
+ spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+ /* Loop over all attached PMICs and call specific functions for them. */
+ list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+ switch (chip->version) {
+ case PM8XXX_VERSION_8058:
+ rc = __pm8058_reset_pwr_off(chip, reset);
+ break;
+ case PM8XXX_VERSION_8901:
+ rc = __pm8901_reset_pwr_off(chip, reset);
+ break;
+ case PM8XXX_VERSION_8921:
+ rc = __pm8921_reset_pwr_off(chip, reset);
+ break;
+ default:
+ /* PMIC doesn't have reset_pwr_off; do nothing. */
+ break;
+ }
+ if (rc) {
+ pr_err("reset_pwr_off failed, rc=%d\n", rc);
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_reset_pwr_off);
+
+static int __devinit pm8xxx_misc_probe(struct platform_device *pdev)
+{
+ const struct pm8xxx_misc_platform_data *pdata = pdev->dev.platform_data;
+ struct pm8xxx_misc_chip *chip;
+ struct pm8xxx_misc_chip *sibling;
+ struct list_head *prev;
+ unsigned long flags;
+ int rc = 0;
+
+ if (!pdata) {
+ pr_err("missing platform data\n");
+ return -EINVAL;
+ }
+
+ chip = kzalloc(sizeof(struct pm8xxx_misc_chip), GFP_KERNEL);
+ if (!chip) {
+ pr_err("Cannot allocate %d bytes\n",
+ sizeof(struct pm8xxx_misc_chip));
+ return -ENOMEM;
+ }
+
+ chip->dev = &pdev->dev;
+ chip->version = pm8xxx_get_version(chip->dev->parent);
+ memcpy(&(chip->pdata), pdata, sizeof(struct pm8xxx_misc_platform_data));
+
+ /* Insert PMICs in priority order (lowest value first). */
+ spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+ prev = &pm8xxx_misc_chips;
+ list_for_each_entry(sibling, &pm8xxx_misc_chips, link) {
+ if (chip->pdata.priority < sibling->pdata.priority)
+ break;
+ else
+ prev = &sibling->link;
+ }
+ list_add(&chip->link, prev);
+ spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+ platform_set_drvdata(pdev, chip);
+
+ return rc;
+}
+
+static int __devexit pm8xxx_misc_remove(struct platform_device *pdev)
+{
+ struct pm8xxx_misc_chip *chip = platform_get_drvdata(pdev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+ list_del(&chip->link);
+ spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(chip);
+
+ return 0;
+}
+
+static struct platform_driver pm8xxx_misc_driver = {
+ .probe = pm8xxx_misc_probe,
+ .remove = __devexit_p(pm8xxx_misc_remove),
+ .driver = {
+ .name = PM8XXX_MISC_DEV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pm8xxx_misc_init(void)
+{
+ return platform_driver_register(&pm8xxx_misc_driver);
+}
+postcore_initcall(pm8xxx_misc_init);
+
+static void __exit pm8xxx_misc_exit(void)
+{
+ platform_driver_unregister(&pm8xxx_misc_driver);
+}
+module_exit(pm8xxx_misc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC 8XXX misc driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_MISC_DEV_NAME);
diff --git a/drivers/mfd/pm8xxx-pwm.c b/drivers/mfd/pm8xxx-pwm.c
new file mode 100644
index 0000000..cdddd98
--- /dev/null
+++ b/drivers/mfd/pm8xxx-pwm.c
@@ -0,0 +1,1082 @@
+/* Copyright (c) 2011, Code Aurora Forum. 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 and
+ * only 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.
+ */
+/*
+ * Qualcomm PM8XXX Pulse Width Modulation (PWM) driver
+ *
+ * The HW module is also called LPG (Light Pulse Generator).
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/pwm.h>
+
+#define PM8XXX_LPG_BANKS 8
+#define PM8XXX_PWM_CHANNELS PM8XXX_LPG_BANKS
+
+#define PM8XXX_LPG_CTL_REGS 7
+
+/* PM8XXX PWM */
+#define SSBI_REG_ADDR_LPG_CTL_BASE 0x13C
+#define SSBI_REG_ADDR_LPG_CTL(n) (SSBI_REG_ADDR_LPG_CTL_BASE + (n))
+#define SSBI_REG_ADDR_LPG_BANK_SEL 0x143
+#define SSBI_REG_ADDR_LPG_BANK_EN 0x144
+#define SSBI_REG_ADDR_LPG_LUT_CFG0 0x145
+#define SSBI_REG_ADDR_LPG_LUT_CFG1 0x146
+
+/* Control 0 */
+#define PM8XXX_PWM_1KHZ_COUNT_MASK 0xF0
+#define PM8XXX_PWM_1KHZ_COUNT_SHIFT 4
+
+#define PM8XXX_PWM_1KHZ_COUNT_MAX 15
+
+#define PM8XXX_PWM_OUTPUT_EN 0x08
+#define PM8XXX_PWM_PWM_EN 0x04
+#define PM8XXX_PWM_RAMP_GEN_EN 0x02
+#define PM8XXX_PWM_RAMP_START 0x01
+
+#define PM8XXX_PWM_PWM_START (PM8XXX_PWM_OUTPUT_EN \
+ | PM8XXX_PWM_PWM_EN)
+#define PM8XXX_PWM_RAMP_GEN_START (PM8XXX_PWM_RAMP_GEN_EN \
+ | PM8XXX_PWM_RAMP_START)
+
+/* Control 1 */
+#define PM8XXX_PWM_REVERSE_EN 0x80
+#define PM8XXX_PWM_BYPASS_LUT 0x40
+#define PM8XXX_PWM_HIGH_INDEX_MASK 0x3F
+
+/* Control 2 */
+#define PM8XXX_PWM_LOOP_EN 0x80
+#define PM8XXX_PWM_RAMP_UP 0x40
+#define PM8XXX_PWM_LOW_INDEX_MASK 0x3F
+
+/* Control 3 */
+#define PM8XXX_PWM_VALUE_BIT7_0 0xFF
+#define PM8XXX_PWM_VALUE_BIT5_0 0x3F
+
+/* Control 4 */
+#define PM8XXX_PWM_VALUE_BIT8 0x80
+
+#define PM8XXX_PWM_CLK_SEL_MASK 0x60
+#define PM8XXX_PWM_CLK_SEL_SHIFT 5
+
+#define PM8XXX_PWM_CLK_SEL_NO 0
+#define PM8XXX_PWM_CLK_SEL_1KHZ 1
+#define PM8XXX_PWM_CLK_SEL_32KHZ 2
+#define PM8XXX_PWM_CLK_SEL_19P2MHZ 3
+
+#define PM8XXX_PWM_PREDIVIDE_MASK 0x18
+#define PM8XXX_PWM_PREDIVIDE_SHIFT 3
+
+#define PM8XXX_PWM_PREDIVIDE_2 0
+#define PM8XXX_PWM_PREDIVIDE_3 1
+#define PM8XXX_PWM_PREDIVIDE_5 2
+#define PM8XXX_PWM_PREDIVIDE_6 3
+
+#define PM8XXX_PWM_M_MASK 0x07
+#define PM8XXX_PWM_M_MIN 0
+#define PM8XXX_PWM_M_MAX 7
+
+/* Control 5 */
+#define PM8XXX_PWM_PAUSE_COUNT_HI_MASK 0xFC
+#define PM8XXX_PWM_PAUSE_COUNT_HI_SHIFT 2
+
+#define PM8XXX_PWM_PAUSE_ENABLE_HIGH 0x02
+#define PM8XXX_PWM_SIZE_9_BIT 0x01
+
+/* Control 6 */
+#define PM8XXX_PWM_PAUSE_COUNT_LO_MASK 0xFC
+#define PM8XXX_PWM_PAUSE_COUNT_LO_SHIFT 2
+
+#define PM8XXX_PWM_PAUSE_ENABLE_LOW 0x02
+#define PM8XXX_PWM_RESERVED 0x01
+
+#define PM8XXX_PWM_PAUSE_COUNT_MAX 56 /* < 2^6 = 64 */
+
+/* LUT_CFG1 */
+#define PM8XXX_PWM_LUT_READ 0x40
+
+/*
+ * PWM Frequency = Clock Frequency / (N * T)
+ * or
+ * PWM Period = Clock Period * (N * T)
+ * where
+ * N = 2^9 or 2^6 for 9-bit or 6-bit PWM size
+ * T = Pre-divide * 2^m, where m = 0..7 (exponent)
+ *
+ * This is the formula to figure out m for the best pre-divide and clock:
+ * (PWM Period / N) / 2^m = (Pre-divide * Clock Period)
+ */
+#define NUM_CLOCKS 3
+
+#define NSEC_1000HZ (NSEC_PER_SEC / 1000)
+#define NSEC_32768HZ (NSEC_PER_SEC / 32768)
+#define NSEC_19P2MHZ (NSEC_PER_SEC / 19200000)
+
+#define CLK_PERIOD_MIN NSEC_19P2MHZ
+#define CLK_PERIOD_MAX NSEC_1000HZ
+
+#define NUM_PRE_DIVIDE 3 /* No default support for pre-divide = 6 */
+
+#define PRE_DIVIDE_0 2
+#define PRE_DIVIDE_1 3
+#define PRE_DIVIDE_2 5
+
+#define PRE_DIVIDE_MIN PRE_DIVIDE_0
+#define PRE_DIVIDE_MAX PRE_DIVIDE_2
+
+static unsigned int pt_t[NUM_PRE_DIVIDE][NUM_CLOCKS] = {
+ { PRE_DIVIDE_0 * NSEC_1000HZ,
+ PRE_DIVIDE_0 * NSEC_32768HZ,
+ PRE_DIVIDE_0 * NSEC_19P2MHZ,
+ },
+ { PRE_DIVIDE_1 * NSEC_1000HZ,
+ PRE_DIVIDE_1 * NSEC_32768HZ,
+ PRE_DIVIDE_1 * NSEC_19P2MHZ,
+ },
+ { PRE_DIVIDE_2 * NSEC_1000HZ,
+ PRE_DIVIDE_2 * NSEC_32768HZ,
+ PRE_DIVIDE_2 * NSEC_19P2MHZ,
+ },
+};
+
+#define MIN_MPT ((PRE_DIVIDE_MIN * CLK_PERIOD_MIN) << PM8XXX_PWM_M_MIN)
+#define MAX_MPT ((PRE_DIVIDE_MAX * CLK_PERIOD_MAX) << PM8XXX_PWM_M_MAX)
+
+/* Private data */
+struct pm8xxx_pwm_chip;
+
+struct pwm_device {
+ int pwm_id; /* = bank/channel id */
+ int in_use;
+ const char *label;
+ int pwm_period;
+ int pwm_duty;
+ u8 pwm_ctl[PM8XXX_LPG_CTL_REGS];
+ int irq;
+ struct pm8xxx_pwm_chip *chip;
+};
+
+struct pm8xxx_pwm_chip {
+ struct pwm_device pwm_dev[PM8XXX_PWM_CHANNELS];
+ u8 bank_mask;
+ struct mutex pwm_mutex;
+ struct device *dev;
+};
+
+static struct pm8xxx_pwm_chip *pwm_chip;
+
+struct pm8xxx_pwm_config {
+ int pwm_size; /* round up to 6 or 9 for 6/9-bit PWM SIZE */
+ int clk;
+ int pre_div;
+ int pre_div_exp;
+ int pwm_value;
+ int bypass_lut;
+
+ /* LUT parameters when bypass_lut is 0 */
+ int lut_duty_ms;
+ int lut_lo_index;
+ int lut_hi_index;
+ int lut_pause_hi;
+ int lut_pause_lo;
+ int flags;
+};
+
+static const u16 duty_msec[PM8XXX_PWM_1KHZ_COUNT_MAX + 1] = {
+ 0, 1, 2, 3, 4, 6, 8, 16, 18, 24, 32, 36, 64, 128, 256, 512
+};
+
+static const u16 pause_count[PM8XXX_PWM_PAUSE_COUNT_MAX + 1] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 23, 28, 31, 42, 47, 56, 63, 83, 94, 111, 125, 167, 188, 222, 250, 333,
+ 375, 500, 667, 750, 800, 900, 1000, 1100,
+ 1200, 1300, 1400, 1500, 1600, 1800, 2000, 2500,
+ 3000, 3500, 4000, 4500, 5000, 5500, 6000, 6500,
+ 7000
+};
+
+/* Internal functions */
+static int pm8xxx_pwm_bank_enable(struct pwm_device *pwm, int enable)
+{
+ int rc;
+ u8 reg;
+ struct pm8xxx_pwm_chip *chip;
+
+ chip = pwm->chip;
+
+ if (enable)
+ reg = chip->bank_mask | (1 << pwm->pwm_id);
+ else
+ reg = chip->bank_mask & ~(1 << pwm->pwm_id);
+
+ rc = pm8xxx_writeb(chip->dev->parent, SSBI_REG_ADDR_LPG_BANK_EN, reg);
+ if (rc) {
+ pr_err("pm8xxx_write(): rc=%d (Enable LPG Bank)\n", rc);
+ return rc;
+ }
+ chip->bank_mask = reg;
+
+ return 0;
+}
+
+static int pm8xxx_pwm_bank_sel(struct pwm_device *pwm)
+{
+ int rc;
+
+ rc = pm8xxx_writeb(pwm->chip->dev->parent, SSBI_REG_ADDR_LPG_BANK_SEL,
+ pwm->pwm_id);
+ if (rc)
+ pr_err("pm8xxx_write(): rc=%d (Select PWM Bank)\n", rc);
+ return rc;
+}
+
+static int pm8xxx_pwm_start(struct pwm_device *pwm, int start, int ramp_start)
+{
+ int rc;
+ u8 reg;
+
+ if (start) {
+ reg = pwm->pwm_ctl[0] | PM8XXX_PWM_PWM_START;
+ if (ramp_start)
+ reg |= PM8XXX_PWM_RAMP_GEN_START;
+ else
+ reg &= ~PM8XXX_PWM_RAMP_GEN_START;
+ } else {
+ reg = pwm->pwm_ctl[0] & ~PM8XXX_PWM_PWM_START;
+ reg &= ~PM8XXX_PWM_RAMP_GEN_START;
+ }
+
+ rc = pm8xxx_writeb(pwm->chip->dev->parent, SSBI_REG_ADDR_LPG_CTL(0),
+ reg);
+ if (rc)
+ pr_err("pm8xxx_write(): rc=%d (Enable PWM Ctl 0)\n", rc);
+ else
+ pwm->pwm_ctl[0] = reg;
+ return rc;
+}
+
+static void pm8xxx_pwm_calc_period(unsigned int period_us,
+ struct pm8xxx_pwm_config *pwm_conf)
+{
+ int n, m, clk, div;
+ int best_m, best_div, best_clk;
+ int last_err, cur_err, better_err, better_m;
+ unsigned int tmp_p, last_p, min_err, period_n;
+
+ /* PWM Period / N */
+ if (period_us < (40 * USEC_PER_SEC)) { /* ~6-bit max */
+ period_n = (period_us * NSEC_PER_USEC) >> 6;
+ n = 6;
+ } else if (period_us < (274 * USEC_PER_SEC)) { /* overflow threshold */
+ period_n = (period_us >> 6) * NSEC_PER_USEC;
+ if (period_n >= MAX_MPT) {
+ n = 9;
+ period_n >>= 3;
+ } else {
+ n = 6;
+ }
+ } else {
+ period_n = (period_us >> 9) * NSEC_PER_USEC;
+ n = 9;
+ }
+
+ min_err = MAX_MPT;
+ best_m = 0;
+ best_clk = 0;
+ best_div = 0;
+ for (clk = 0; clk < NUM_CLOCKS; clk++) {
+ for (div = 0; div < NUM_PRE_DIVIDE; div++) {
+ tmp_p = period_n;
+ last_p = tmp_p;
+ for (m = 0; m <= PM8XXX_PWM_M_MAX; m++) {
+ if (tmp_p <= pt_t[div][clk]) {
+ /* Found local best */
+ if (!m) {
+ better_err = pt_t[div][clk] -
+ tmp_p;
+ better_m = m;
+ } else {
+ last_err = last_p -
+ pt_t[div][clk];
+ cur_err = pt_t[div][clk] -
+ tmp_p;
+
+ if (cur_err < last_err) {
+ better_err = cur_err;
+ better_m = m;
+ } else {
+ better_err = last_err;
+ better_m = m - 1;
+ }
+ }
+
+ if (better_err < min_err) {
+ min_err = better_err;
+ best_m = better_m;
+ best_clk = clk;
+ best_div = div;
+ }
+ break;
+ } else {
+ last_p = tmp_p;
+ tmp_p >>= 1;
+ }
+ }
+ }
+ }
+
+ pwm_conf->pwm_size = n;
+ pwm_conf->clk = best_clk;
+ pwm_conf->pre_div = best_div;
+ pwm_conf->pre_div_exp = best_m;
+}
+
+static int pm8xxx_pwm_configure(struct pwm_device *pwm,
+ struct pm8xxx_pwm_config *pwm_conf)
+{
+ int i, rc, len;
+ u8 reg, ramp_enabled = 0;
+
+ reg = (pwm_conf->pwm_size > 6) ? PM8XXX_PWM_SIZE_9_BIT : 0;
+ pwm->pwm_ctl[5] = reg;
+
+ reg = ((pwm_conf->clk + 1) << PM8XXX_PWM_CLK_SEL_SHIFT)
+ & PM8XXX_PWM_CLK_SEL_MASK;
+ reg |= (pwm_conf->pre_div << PM8XXX_PWM_PREDIVIDE_SHIFT)
+ & PM8XXX_PWM_PREDIVIDE_MASK;
+ reg |= pwm_conf->pre_div_exp & PM8XXX_PWM_M_MASK;
+ pwm->pwm_ctl[4] = reg;
+
+ if (pwm_conf->bypass_lut) {
+ pwm->pwm_ctl[0] &= PM8XXX_PWM_PWM_START; /* keep enabled */
+ pwm->pwm_ctl[1] = PM8XXX_PWM_BYPASS_LUT;
+ pwm->pwm_ctl[2] = 0;
+
+ if (pwm_conf->pwm_size > 6) {
+ pwm->pwm_ctl[3] = pwm_conf->pwm_value
+ & PM8XXX_PWM_VALUE_BIT7_0;
+ pwm->pwm_ctl[4] |= (pwm_conf->pwm_value >> 1)
+ & PM8XXX_PWM_VALUE_BIT8;
+ } else {
+ pwm->pwm_ctl[3] = pwm_conf->pwm_value
+ & PM8XXX_PWM_VALUE_BIT5_0;
+ }
+
+ len = 6;
+ } else {
+ int pause_cnt, j;
+
+ /* Linear search for duty time */
+ for (i = 0; i < PM8XXX_PWM_1KHZ_COUNT_MAX; i++) {
+ if (duty_msec[i] >= pwm_conf->lut_duty_ms)
+ break;
+ }
+
+ ramp_enabled = pwm->pwm_ctl[0] & PM8XXX_PWM_RAMP_GEN_START;
+ pwm->pwm_ctl[0] &= PM8XXX_PWM_PWM_START; /* keep enabled */
+ pwm->pwm_ctl[0] |= (i << PM8XXX_PWM_1KHZ_COUNT_SHIFT) &
+ PM8XXX_PWM_1KHZ_COUNT_MASK;
+ pwm->pwm_ctl[1] = pwm_conf->lut_hi_index &
+ PM8XXX_PWM_HIGH_INDEX_MASK;
+ pwm->pwm_ctl[2] = pwm_conf->lut_lo_index &
+ PM8XXX_PWM_LOW_INDEX_MASK;
+
+ if (pwm_conf->flags & PM_PWM_LUT_REVERSE)
+ pwm->pwm_ctl[1] |= PM8XXX_PWM_REVERSE_EN;
+ if (pwm_conf->flags & PM_PWM_LUT_RAMP_UP)
+ pwm->pwm_ctl[2] |= PM8XXX_PWM_RAMP_UP;
+ if (pwm_conf->flags & PM_PWM_LUT_LOOP)
+ pwm->pwm_ctl[2] |= PM8XXX_PWM_LOOP_EN;
+
+ /* Pause time */
+ if (pwm_conf->flags & PM_PWM_LUT_PAUSE_HI_EN) {
+ /* Linear search for pause time */
+ pause_cnt = (pwm_conf->lut_pause_hi + duty_msec[i] / 2)
+ / duty_msec[i];
+ for (j = 0; j < PM8XXX_PWM_PAUSE_COUNT_MAX; j++) {
+ if (pause_count[j] >= pause_cnt)
+ break;
+ }
+ pwm->pwm_ctl[5] |= (j <<
+ PM8XXX_PWM_PAUSE_COUNT_HI_SHIFT) &
+ PM8XXX_PWM_PAUSE_COUNT_HI_MASK;
+ pwm->pwm_ctl[5] |= PM8XXX_PWM_PAUSE_ENABLE_HIGH;
+ }
+
+ if (pwm_conf->flags & PM_PWM_LUT_PAUSE_LO_EN) {
+ /* Linear search for pause time */
+ pause_cnt = (pwm_conf->lut_pause_lo + duty_msec[i] / 2)
+ / duty_msec[i];
+ for (j = 0; j < PM8XXX_PWM_PAUSE_COUNT_MAX; j++) {
+ if (pause_count[j] >= pause_cnt)
+ break;
+ }
+ pwm->pwm_ctl[6] = (j <<
+ PM8XXX_PWM_PAUSE_COUNT_LO_SHIFT) &
+ PM8XXX_PWM_PAUSE_COUNT_LO_MASK;
+ pwm->pwm_ctl[6] |= PM8XXX_PWM_PAUSE_ENABLE_LOW;
+ } else {
+ pwm->pwm_ctl[6] = 0;
+ }
+
+ len = 7;
+ }
+
+ pm8xxx_pwm_bank_sel(pwm);
+
+ for (i = 0; i < len; i++) {
+ rc = pm8xxx_writeb(pwm->chip->dev->parent,
+ SSBI_REG_ADDR_LPG_CTL(i),
+ pwm->pwm_ctl[i]);
+ if (rc) {
+ pr_err("pm8xxx_write(): rc=%d (PWM Ctl[%d])\n", rc, i);
+ break;
+ }
+ }
+
+ if (ramp_enabled) {
+ pwm->pwm_ctl[0] |= ramp_enabled;
+ pm8xxx_writeb(pwm->chip->dev->parent,
+ SSBI_REG_ADDR_LPG_CTL(0),
+ pwm->pwm_ctl[0]);
+ }
+
+ return rc;
+}
+
+/* APIs */
+/**
+ * pwm_request - request a PWM device
+ * @pwm_id: PWM id or channel
+ * @label: the label to identify the user
+ */
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+ struct pwm_device *pwm;
+
+ if (pwm_id > PM8XXX_PWM_CHANNELS || pwm_id < 0) {
+ pr_err("Invalid pwm_id: %d with %s\n",
+ pwm_id, label ? label : ".");
+ return ERR_PTR(-EINVAL);
+ }
+ if (pwm_chip == NULL) {
+ pr_err("No pwm_chip\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ mutex_lock(&pwm_chip->pwm_mutex);
+ pwm = &pwm_chip->pwm_dev[pwm_id];
+ if (!pwm->in_use) {
+ pwm->in_use = 1;
+ pwm->label = label;
+ } else {
+ pwm = ERR_PTR(-EBUSY);
+ }
+ mutex_unlock(&pwm_chip->pwm_mutex);
+
+ return pwm;
+}
+EXPORT_SYMBOL_GPL(pwm_request);
+
+/**
+ * pwm_free - free a PWM device
+ * @pwm: the PWM device
+ */
+void pwm_free(struct pwm_device *pwm)
+{
+ if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
+ pr_err("Invalid pwm handle\n");
+ return;
+ }
+
+ mutex_lock(&pwm->chip->pwm_mutex);
+ if (pwm->in_use) {
+ pm8xxx_pwm_bank_sel(pwm);
+ pm8xxx_pwm_start(pwm, 0, 0);
+
+ pwm->in_use = 0;
+ pwm->label = NULL;
+ }
+ pm8xxx_pwm_bank_enable(pwm, 0);
+ mutex_unlock(&pwm->chip->pwm_mutex);
+}
+EXPORT_SYMBOL_GPL(pwm_free);
+
+/**
+ * pwm_config - change a PWM device configuration
+ * @pwm: the PWM device
+ * @period_us: period in microseconds
+ * @duty_us: duty cycle in microseconds
+ */
+int pwm_config(struct pwm_device *pwm, int duty_us, int period_us)
+{
+ struct pm8xxx_pwm_config pwm_conf;
+ unsigned int max_pwm_value, tmp;
+ int rc;
+
+ if (pwm == NULL || IS_ERR(pwm) ||
+ duty_us > period_us ||
+ (unsigned)period_us > PM8XXX_PWM_PERIOD_MAX ||
+ (unsigned)period_us < PM8XXX_PWM_PERIOD_MIN) {
+ pr_err("Invalid pwm handle or parameters\n");
+ return -EINVAL;
+ }
+ if (pwm->chip == NULL) {
+ pr_err("No pwm_chip\n");
+ return -ENODEV;
+ }
+
+ mutex_lock(&pwm->chip->pwm_mutex);
+
+ if (!pwm->in_use) {
+ pr_err("pwm_id: %d: stale handle?\n", pwm->pwm_id);
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+
+ pm8xxx_pwm_calc_period(period_us, &pwm_conf);
+
+ /* Figure out pwm_value with overflow handling */
+ if ((unsigned)period_us > (1 << pwm_conf.pwm_size)) {
+ tmp = period_us;
+ tmp >>= pwm_conf.pwm_size;
+ pwm_conf.pwm_value = (unsigned)duty_us / tmp;
+ } else {
+ tmp = duty_us;
+ tmp <<= pwm_conf.pwm_size;
+ pwm_conf.pwm_value = tmp / (unsigned)period_us;
+ }
+ max_pwm_value = (1 << pwm_conf.pwm_size) - 1;
+ if (pwm_conf.pwm_value > max_pwm_value)
+ pwm_conf.pwm_value = max_pwm_value;
+
+ pwm_conf.bypass_lut = 1;
+
+ rc = pm8xxx_pwm_configure(pwm, &pwm_conf);
+
+out_unlock:
+ mutex_unlock(&pwm->chip->pwm_mutex);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(pwm_config);
+
+/**
+ * pwm_enable - start a PWM output toggling
+ * @pwm: the PWM device
+ */
+int pwm_enable(struct pwm_device *pwm)
+{
+ int rc;
+
+ if (pwm == NULL || IS_ERR(pwm)) {
+ pr_err("Invalid pwm handle\n");
+ return -EINVAL;
+ }
+ if (pwm->chip == NULL) {
+ pr_err("No pwm_chip\n");
+ return -ENODEV;
+ }
+
+ mutex_lock(&pwm->chip->pwm_mutex);
+ if (!pwm->in_use) {
+ pr_err("pwm_id: %d: stale handle?\n", pwm->pwm_id);
+ rc = -EINVAL;
+ } else {
+ rc = pm8xxx_pwm_bank_enable(pwm, 1);
+
+ pm8xxx_pwm_bank_sel(pwm);
+ pm8xxx_pwm_start(pwm, 1, 0);
+ }
+ mutex_unlock(&pwm->chip->pwm_mutex);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(pwm_enable);
+
+/**
+ * pwm_disable - stop a PWM output toggling
+ * @pwm: the PWM device
+ */
+void pwm_disable(struct pwm_device *pwm)
+{
+ if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
+ pr_err("Invalid pwm handle or no pwm_chip\n");
+ return;
+ }
+
+ mutex_lock(&pwm->chip->pwm_mutex);
+ if (pwm->in_use) {
+ pm8xxx_pwm_bank_sel(pwm);
+ pm8xxx_pwm_start(pwm, 0, 0);
+
+ pm8xxx_pwm_bank_enable(pwm, 0);
+ }
+ mutex_unlock(&pwm->chip->pwm_mutex);
+}
+EXPORT_SYMBOL_GPL(pwm_disable);
+
+/**
+ * pm8xxx_pwm_lut_config - change a PWM device configuration to use LUT
+ * @pwm: the PWM device
+ * @period_us: period in microseconds
+ * @duty_pct: arrary of duty cycles in percent, like 20, 50.
+ * @duty_time_ms: time for each duty cycle in milliseconds
+ * @start_idx: start index in lookup table from 0 to MAX-1
+ * @idx_len: number of index
+ * @pause_lo: pause time in milliseconds at low index
+ * @pause_hi: pause time in milliseconds at high index
+ * @flags: control flags
+ */
+int pm8xxx_pwm_lut_config(struct pwm_device *pwm, int period_us,
+ int duty_pct[], int duty_time_ms, int start_idx,
+ int idx_len, int pause_lo, int pause_hi, int flags)
+{
+ struct pm8xxx_pwm_config pwm_conf;
+ unsigned int pwm_value, max_pwm_value;
+ u8 cfg0, cfg1;
+ int i, len;
+ int rc;
+
+ if (pwm == NULL || IS_ERR(pwm) || !idx_len) {
+ pr_err("Invalid pwm handle or idx_len=0\n");
+ return -EINVAL;
+ }
+ if (duty_pct == NULL && !(flags & PM_PWM_LUT_NO_TABLE)) {
+ pr_err("Invalid duty_pct with flag\n");
+ return -EINVAL;
+ }
+ if (pwm->chip == NULL) {
+ pr_err("No pwm_chip\n");
+ return -ENODEV;
+ }
+ if (idx_len >= PM_PWM_LUT_SIZE && start_idx) {
+ pr_err("Wrong LUT size or index\n");
+ return -EINVAL;
+ }
+ if ((start_idx + idx_len) > PM_PWM_LUT_SIZE) {
+ pr_err("Exceed LUT limit\n");
+ return -EINVAL;
+ }
+ if ((unsigned)period_us > PM8XXX_PWM_PERIOD_MAX ||
+ (unsigned)period_us < PM8XXX_PWM_PERIOD_MIN) {
+ pr_err("Period out of range\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&pwm->chip->pwm_mutex);
+
+ if (!pwm->in_use) {
+ pr_err("pwm_id: %d: stale handle?\n", pwm->pwm_id);
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+
+ pm8xxx_pwm_calc_period(period_us, &pwm_conf);
+
+ len = (idx_len > PM_PWM_LUT_SIZE) ? PM_PWM_LUT_SIZE : idx_len;
+
+ if (flags & PM_PWM_LUT_NO_TABLE)
+ goto after_table_write;
+
+ max_pwm_value = (1 << pwm_conf.pwm_size) - 1;
+ for (i = 0; i < len; i++) {
+ pwm_value = (duty_pct[i] << pwm_conf.pwm_size) / 100;
+ /* Avoid overflow */
+ if (pwm_value > max_pwm_value)
+ pwm_value = max_pwm_value;
+ cfg0 = pwm_value & 0xff;
+ cfg1 = (pwm_value >> 1) & 0x80;
+ cfg1 |= start_idx + i;
+
+ pm8xxx_writeb(pwm->chip->dev->parent,
+ SSBI_REG_ADDR_LPG_LUT_CFG0, cfg0);
+ pm8xxx_writeb(pwm->chip->dev->parent,
+ SSBI_REG_ADDR_LPG_LUT_CFG1, cfg1);
+ }
+
+after_table_write:
+ pwm_conf.lut_duty_ms = duty_time_ms;
+ pwm_conf.lut_lo_index = start_idx;
+ pwm_conf.lut_hi_index = start_idx + len - 1;
+ pwm_conf.lut_pause_lo = pause_lo;
+ pwm_conf.lut_pause_hi = pause_hi;
+ pwm_conf.flags = flags;
+ pwm_conf.bypass_lut = 0;
+
+ rc = pm8xxx_pwm_configure(pwm, &pwm_conf);
+
+out_unlock:
+ mutex_unlock(&pwm->chip->pwm_mutex);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_pwm_lut_config);
+
+/**
+ * pm8xxx_pwm_lut_enable - control a PWM device to start/stop LUT ramp
+ * @pwm: the PWM device
+ * @start: to start (1), or stop (0)
+ */
+int pm8xxx_pwm_lut_enable(struct pwm_device *pwm, int start)
+{
+ if (pwm == NULL || IS_ERR(pwm)) {
+ pr_err("Invalid pwm handle\n");
+ return -EINVAL;
+ }
+ if (pwm->chip == NULL) {
+ pr_err("No pwm_chip\n");
+ return -ENODEV;
+ }
+
+ mutex_lock(&pwm->chip->pwm_mutex);
+ if (start) {
+ pm8xxx_pwm_bank_enable(pwm, 1);
+
+ pm8xxx_pwm_bank_sel(pwm);
+ pm8xxx_pwm_start(pwm, 1, 1);
+ } else {
+ pm8xxx_pwm_bank_sel(pwm);
+ pm8xxx_pwm_start(pwm, 0, 0);
+
+ pm8xxx_pwm_bank_enable(pwm, 0);
+ }
+ mutex_unlock(&pwm->chip->pwm_mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_pwm_lut_enable);
+
+#if defined(CONFIG_DEBUG_FS)
+
+struct pm8xxx_pwm_dbg_device;
+
+struct pm8xxx_pwm_user {
+ int pwm_id;
+ struct pwm_device *pwm;
+ int period;
+ int duty_cycle;
+ int enable;
+ struct pm8xxx_pwm_dbg_device *dbgdev;
+};
+
+struct pm8xxx_pwm_dbg_device {
+ struct mutex dbg_mutex;
+ struct device *dev;
+ struct dentry *dent;
+
+ struct pm8xxx_pwm_user user[PM8XXX_PWM_CHANNELS];
+};
+
+static struct pm8xxx_pwm_dbg_device *pmic_dbg_device;
+
+static int dbg_pwm_check_period(int period)
+{
+ if (period < PM8XXX_PWM_PERIOD_MIN || period > PM8XXX_PWM_PERIOD_MAX) {
+ pr_err("period is invalid: %d\n", period);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int dbg_pwm_check_duty_cycle(int duty_cycle, const char *func_name)
+{
+ if (duty_cycle <= 0 || duty_cycle > 100) {
+ pr_err("%s: duty_cycle is invalid: %d\n",
+ func_name, duty_cycle);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void dbg_pwm_check_handle(struct pm8xxx_pwm_user *puser)
+{
+ struct pwm_device *tmp;
+
+ if (puser->pwm == NULL) {
+ tmp = pwm_request(puser->pwm_id, "pwm-dbg");
+ if (PTR_ERR(puser->pwm)) {
+ pr_err("pwm_request: err=%ld\n", PTR_ERR(puser->pwm));
+ puser->pwm = NULL;
+ }
+ }
+}
+
+static int dbg_pwm_enable_set(void *data, u64 val)
+{
+ struct pm8xxx_pwm_user *puser = data;
+ struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
+ int rc;
+
+ mutex_lock(&dbgdev->dbg_mutex);
+ rc = dbg_pwm_check_duty_cycle(puser->duty_cycle, __func__);
+ if (!rc) {
+ puser->enable = val;
+ dbg_pwm_check_handle(puser);
+ if (puser->pwm) {
+ if (puser->enable)
+ pwm_enable(puser->pwm);
+ else
+ pwm_disable(puser->pwm);
+ }
+ }
+ mutex_unlock(&dbgdev->dbg_mutex);
+ return 0;
+}
+
+static int dbg_pwm_enable_get(void *data, u64 *val)
+{
+ struct pm8xxx_pwm_user *puser = data;
+ struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
+
+ mutex_lock(&dbgdev->dbg_mutex);
+ *val = puser->enable;
+ mutex_unlock(&dbgdev->dbg_mutex);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_enable_fops,
+ dbg_pwm_enable_get, dbg_pwm_enable_set,
+ "%lld\n");
+
+static int dbg_pwm_duty_cycle_set(void *data, u64 val)
+{
+ struct pm8xxx_pwm_user *puser = data;
+ struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
+ int rc;
+
+ mutex_lock(&dbgdev->dbg_mutex);
+ rc = dbg_pwm_check_duty_cycle(val, __func__);
+ if (!rc) {
+ puser->duty_cycle = val;
+ dbg_pwm_check_handle(puser);
+ if (puser->pwm) {
+ int duty_us;
+
+ duty_us = puser->duty_cycle * puser->period;
+ pwm_config(puser->pwm,
+ puser->duty_cycle, puser->period);
+ }
+ }
+ mutex_unlock(&dbgdev->dbg_mutex);
+ return 0;
+}
+
+static int dbg_pwm_duty_cycle_get(void *data, u64 *val)
+{
+ struct pm8xxx_pwm_user *puser = data;
+ struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
+
+ mutex_lock(&dbgdev->dbg_mutex);
+ *val = puser->duty_cycle;
+ mutex_unlock(&dbgdev->dbg_mutex);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_duty_cycle_fops,
+ dbg_pwm_duty_cycle_get, dbg_pwm_duty_cycle_set,
+ "%lld\n");
+
+static int dbg_pwm_period_set(void *data, u64 val)
+{
+ struct pm8xxx_pwm_user *puser = data;
+ struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
+ int rc;
+
+ mutex_lock(&dbgdev->dbg_mutex);
+ rc = dbg_pwm_check_period(val);
+ if (!rc)
+ puser->period = val;
+ mutex_unlock(&dbgdev->dbg_mutex);
+ return 0;
+}
+
+static int dbg_pwm_period_get(void *data, u64 *val)
+{
+ struct pm8xxx_pwm_user *puser = data;
+ struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
+
+ mutex_lock(&dbgdev->dbg_mutex);
+ *val = puser->period;
+ mutex_unlock(&dbgdev->dbg_mutex);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_period_fops,
+ dbg_pwm_period_get, dbg_pwm_period_set, "%lld\n");
+
+static int __devinit pm8xxx_pwm_dbg_probe(struct device *dev)
+{
+ struct pm8xxx_pwm_dbg_device *dbgdev;
+ struct dentry *dent;
+ struct dentry *temp;
+ struct pm8xxx_pwm_user *puser;
+ int i;
+
+ if (dev == NULL) {
+ pr_err("no parent data passed in.\n");
+ return -EINVAL;
+ }
+
+ dbgdev = kzalloc(sizeof *dbgdev, GFP_KERNEL);
+ if (dbgdev == NULL) {
+ pr_err("kzalloc() failed.\n");
+ return -ENOMEM;
+ }
+
+ mutex_init(&dbgdev->dbg_mutex);
+
+ dbgdev->dev = dev;
+
+ dent = debugfs_create_dir("pm8xxx-pwm-dbg", NULL);
+ if (dent == NULL || IS_ERR(dent)) {
+ pr_err("ERR debugfs_create_dir: dent=%p\n", dent);
+ return -ENOMEM;
+ }
+
+ dbgdev->dent = dent;
+
+ for (i = 0; i < PM8XXX_PWM_CHANNELS; i++) {
+ char pwm_ch[] = "0";
+
+ pwm_ch[0] = '0' + i;
+ dent = debugfs_create_dir(pwm_ch, dbgdev->dent);
+ if (dent == NULL || IS_ERR(dent)) {
+ pr_err("ERR: pwm=%d: dir: dent=%p\n", i, dent);
+ goto debug_error;
+ }
+
+ puser = &dbgdev->user[i];
+ puser->dbgdev = dbgdev;
+ puser->pwm_id = i;
+ temp = debugfs_create_file("period", S_IRUGO | S_IWUSR,
+ dent, puser, &dbg_pwm_period_fops);
+ if (temp == NULL || IS_ERR(temp)) {
+ pr_err("ERR: pwm=%d: period: dent=%p\n", i, dent);
+ goto debug_error;
+ }
+
+ temp = debugfs_create_file("duty-cycle", S_IRUGO | S_IWUSR,
+ dent, puser, &dbg_pwm_duty_cycle_fops);
+ if (temp == NULL || IS_ERR(temp)) {
+ pr_err("ERR: pwm=%d: duty-cycle: dent=%p\n", i, dent);
+ goto debug_error;
+ }
+
+ temp = debugfs_create_file("enable", S_IRUGO | S_IWUSR,
+ dent, puser, &dbg_pwm_enable_fops);
+ if (temp == NULL || IS_ERR(temp)) {
+ pr_err("ERR: pwm=%d: enable: dent=%p\n", i, dent);
+ goto debug_error;
+ }
+ }
+
+ pmic_dbg_device = dbgdev;
+
+ return 0;
+
+debug_error:
+ debugfs_remove_recursive(dbgdev->dent);
+ return -ENOMEM;
+}
+
+static int __devexit pm8xxx_pwm_dbg_remove(void)
+{
+ if (pmic_dbg_device) {
+ debugfs_remove_recursive(pmic_dbg_device->dent);
+ kfree(pmic_dbg_device);
+ }
+ return 0;
+}
+
+#else
+
+static int __devinit pm8xxx_pwm_dbg_probe(struct device *dev)
+{
+ return 0;
+}
+
+static int __devexit pm8xxx_pwm_dbg_remove(void)
+{
+ return 0;
+}
+
+#endif
+
+static int __devinit pm8xxx_pwm_probe(struct platform_device *pdev)
+{
+ struct pm8xxx_pwm_chip *chip;
+ int i;
+
+ chip = kzalloc(sizeof *chip, GFP_KERNEL);
+ if (chip == NULL) {
+ pr_err("kzalloc() failed.\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < PM8XXX_PWM_CHANNELS; i++) {
+ chip->pwm_dev[i].pwm_id = i;
+ chip->pwm_dev[i].chip = chip;
+ }
+
+ mutex_init(&chip->pwm_mutex);
+
+ chip->dev = &pdev->dev;
+ pwm_chip = chip;
+ platform_set_drvdata(pdev, chip);
+
+ if (pm8xxx_pwm_dbg_probe(&pdev->dev) < 0)
+ pr_err("could not set up debugfs\n");
+
+ pr_notice("OK\n");
+ return 0;
+}
+
+static int __devexit pm8xxx_pwm_remove(struct platform_device *pdev)
+{
+ struct pm8xxx_pwm_chip *chip = dev_get_drvdata(pdev->dev.parent);
+
+ pm8xxx_pwm_dbg_remove();
+ mutex_destroy(&chip->pwm_mutex);
+ platform_set_drvdata(pdev, NULL);
+ kfree(chip);
+ return 0;
+}
+
+static struct platform_driver pm8xxx_pwm_driver = {
+ .probe = pm8xxx_pwm_probe,
+ .remove = __devexit_p(pm8xxx_pwm_remove),
+ .driver = {
+ .name = PM8XXX_PWM_DEV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pm8xxx_pwm_init(void)
+{
+ return platform_driver_register(&pm8xxx_pwm_driver);
+}
+
+static void __exit pm8xxx_pwm_exit(void)
+{
+ platform_driver_unregister(&pm8xxx_pwm_driver);
+}
+
+subsys_initcall(pm8xxx_pwm_init);
+module_exit(pm8xxx_pwm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PM8XXX PWM driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_PWM_DEV_NAME);
diff --git a/drivers/mfd/pmic8058.c b/drivers/mfd/pmic8058.c
new file mode 100644
index 0000000..87d4475
--- /dev/null
+++ b/drivers/mfd/pmic8058.c
@@ -0,0 +1,1308 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. 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 and
+ * only 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.
+ *
+ */
+/*
+ * Qualcomm PMIC8058 driver
+ *
+ */
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/ratelimit.h>
+#include <linux/kthread.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/platform_device.h>
+#include <linux/ratelimit.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/irq.h>
+
+/* PMIC8058 Revision */
+#define SSBI_REG_REV 0x002 /* PMIC4 revision */
+
+/* PMIC8058 IRQ */
+#define SSBI_REG_ADDR_IRQ_BASE 0x1BB
+
+#define SSBI_REG_ADDR_IRQ_ROOT (SSBI_REG_ADDR_IRQ_BASE + 0)
+#define SSBI_REG_ADDR_IRQ_M_STATUS1 (SSBI_REG_ADDR_IRQ_BASE + 1)
+#define SSBI_REG_ADDR_IRQ_M_STATUS2 (SSBI_REG_ADDR_IRQ_BASE + 2)
+#define SSBI_REG_ADDR_IRQ_M_STATUS3 (SSBI_REG_ADDR_IRQ_BASE + 3)
+#define SSBI_REG_ADDR_IRQ_M_STATUS4 (SSBI_REG_ADDR_IRQ_BASE + 4)
+#define SSBI_REG_ADDR_IRQ_BLK_SEL (SSBI_REG_ADDR_IRQ_BASE + 5)
+#define SSBI_REG_ADDR_IRQ_IT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 6)
+#define SSBI_REG_ADDR_IRQ_CONFIG (SSBI_REG_ADDR_IRQ_BASE + 7)
+#define SSBI_REG_ADDR_IRQ_RT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 8)
+
+#define PM8058_IRQF_LVL_SEL 0x01 /* level select */
+#define PM8058_IRQF_MASK_FE 0x02 /* mask falling edge */
+#define PM8058_IRQF_MASK_RE 0x04 /* mask rising edge */
+#define PM8058_IRQF_CLR 0x08 /* clear interrupt */
+#define PM8058_IRQF_BITS_MASK 0x70
+#define PM8058_IRQF_BITS_SHIFT 4
+#define PM8058_IRQF_WRITE 0x80
+
+#define PM8058_IRQF_MASK_ALL (PM8058_IRQF_MASK_FE | \
+ PM8058_IRQF_MASK_RE)
+#define PM8058_IRQF_W_C_M (PM8058_IRQF_WRITE | \
+ PM8058_IRQF_CLR | \
+ PM8058_IRQF_MASK_ALL)
+
+/* MISC register */
+#define SSBI_REG_ADDR_MISC 0x1CC
+
+/* PON CNTL 1 register */
+#define SSBI_REG_ADDR_PON_CNTL_1 0x01C
+
+#define PM8058_PON_PUP_MASK 0xF0
+
+#define PM8058_PON_WD_EN_MASK 0x08
+#define PM8058_PON_WD_EN_RESET 0x08
+#define PM8058_PON_WD_EN_PWR_OFF 0x00
+
+/* PON CNTL 4 register */
+#define SSBI_REG_ADDR_PON_CNTL_4 0x98
+#define PM8058_PON_RESET_EN_MASK 0x01
+
+/* PON CNTL 5 register */
+#define SSBI_REG_ADDR_PON_CNTL_5 0x7B
+#define PM8058_HARD_RESET_EN_MASK 0x08
+
+/* Regulator L22 control register */
+#define SSBI_REG_ADDR_L22_CTRL 0x121
+
+/* SLEEP CNTL register */
+#define SSBI_REG_ADDR_SLEEP_CNTL 0x02B
+
+#define PM8058_SLEEP_SMPL_EN_MASK 0x04
+#define PM8058_SLEEP_SMPL_EN_RESET 0x04
+#define PM8058_SLEEP_SMPL_EN_PWR_OFF 0x00
+
+#define PM8058_SLEEP_SMPL_SEL_MASK 0x03
+#define PM8058_SLEEP_SMPL_SEL_MIN 0
+#define PM8058_SLEEP_SMPL_SEL_MAX 3
+
+#define MAX_PM_IRQ 256
+#define MAX_PM_BLOCKS (MAX_PM_IRQ / 8 + 1)
+#define MAX_PM_MASTERS (MAX_PM_BLOCKS / 8 + 1)
+
+struct pm8058_chip {
+ struct pm8058_platform_data pdata;
+
+ struct i2c_client *dev;
+
+ u8 irqs_allowed[MAX_PM_BLOCKS];
+ u8 blocks_allowed[MAX_PM_MASTERS];
+ u8 masters_allowed;
+ int pm_max_irq;
+ int pm_max_blocks;
+ int pm_max_masters;
+
+ u8 config[MAX_PM_IRQ];
+ u8 bus_unlock_config[MAX_PM_IRQ];
+ u8 wake_enable[MAX_PM_IRQ];
+ u16 count_wakeable;
+
+ u8 revision;
+
+ struct mutex pm_lock;
+};
+
+#if defined(CONFIG_DEBUG_FS)
+struct pm8058_dbg_device {
+ struct mutex dbg_mutex;
+ struct pm8058_chip *pm_chip;
+ struct dentry *dent;
+ int addr;
+};
+
+static struct pm8058_dbg_device *pmic_dbg_device;
+#endif
+
+static struct pm8058_chip *pmic_chip;
+
+/* Helper Functions */
+DEFINE_RATELIMIT_STATE(pm8058_msg_ratelimit, 60 * HZ, 10);
+
+static inline int pm8058_can_print(void)
+{
+ return __ratelimit(&pm8058_msg_ratelimit);
+}
+
+static inline int
+ssbi_write(struct i2c_client *client, u16 addr, const u8 *buf, size_t len)
+{
+ int rc;
+ struct i2c_msg msg = {
+ .addr = addr,
+ .flags = 0x0,
+ .buf = (u8 *)buf,
+ .len = len,
+ };
+
+ rc = i2c_transfer(client->adapter, &msg, 1);
+ return (rc == 1) ? 0 : rc;
+}
+
+static inline int
+ssbi_read(struct i2c_client *client, u16 addr, u8 *buf, size_t len)
+{
+ int rc;
+ struct i2c_msg msg = {
+ .addr = addr,
+ .flags = I2C_M_RD,
+ .buf = buf,
+ .len = len,
+ };
+
+ rc = i2c_transfer(client->adapter, &msg, 1);
+ return (rc == 1) ? 0 : rc;
+}
+
+static int pm8058_masked_write(u16 addr, u8 val, u8 mask)
+{
+ int rc;
+ u8 reg;
+
+ if (pmic_chip == NULL)
+ return -ENODEV;
+
+ mutex_lock(&pmic_chip->pm_lock);
+
+ rc = ssbi_read(pmic_chip->dev, addr, ®, 1);
+ if (rc) {
+ pr_err("%s: ssbi_read(0x%03X) failed: rc=%d\n", __func__, addr,
+ rc);
+ goto done;
+ }
+
+ reg &= ~mask;
+ reg |= val & mask;
+
+ rc = ssbi_write(pmic_chip->dev, addr, ®, 1);
+ if (rc)
+ pr_err("%s: ssbi_write(0x%03X)=0x%02X failed: rc=%d\n",
+ __func__, addr, reg, rc);
+done:
+ mutex_unlock(&pmic_chip->pm_lock);
+
+ return rc;
+}
+
+/* External APIs */
+int pm8058_rev(struct pm8058_chip *chip)
+{
+ if (chip == NULL)
+ return -EINVAL;
+
+ return chip->revision;
+}
+EXPORT_SYMBOL(pm8058_rev);
+
+int pm8058_irq_get_rt_status(struct pm8058_chip *chip, int irq)
+{
+ int rc;
+ u8 block, bits, bit;
+
+ if (chip == NULL || irq < chip->pdata.irq_base ||
+ irq >= chip->pdata.irq_base + MAX_PM_IRQ)
+ return -EINVAL;
+
+ irq -= chip->pdata.irq_base;
+
+ block = irq / 8;
+ bit = irq % 8;
+
+ mutex_lock(&chip->pm_lock);
+
+ rc = ssbi_write(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, &block, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_write(): rc=%d (Select Block)\n",
+ __func__, rc);
+ goto bail_out;
+ }
+
+ rc = ssbi_read(chip->dev, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_read(): rc=%d (Read RT Status)\n",
+ __func__, rc);
+ goto bail_out;
+ }
+
+ rc = (bits & (1 << bit)) ? 1 : 0;
+
+bail_out:
+ mutex_unlock(&chip->pm_lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(pm8058_irq_get_rt_status);
+
+int pm8058_read(struct pm8058_chip *chip, u16 addr, u8 *values,
+ unsigned int len)
+{
+ if (chip == NULL)
+ return -EINVAL;
+
+ return ssbi_read(chip->dev, addr, values, len);
+}
+EXPORT_SYMBOL(pm8058_read);
+
+int pm8058_write(struct pm8058_chip *chip, u16 addr, u8 *values,
+ unsigned int len)
+{
+ if (chip == NULL)
+ return -EINVAL;
+
+ return ssbi_write(chip->dev, addr, values, len);
+}
+EXPORT_SYMBOL(pm8058_write);
+
+int pm8058_misc_control(struct pm8058_chip *chip, int mask, int flag)
+{
+ int rc;
+ u8 misc;
+
+ if (chip == NULL)
+ chip = pmic_chip; /* for calls from non child */
+ if (chip == NULL)
+ return -ENODEV;
+
+ mutex_lock(&chip->pm_lock);
+
+ rc = ssbi_read(chip->dev, SSBI_REG_ADDR_MISC, &misc, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_read(0x%x): rc=%d\n",
+ __func__, SSBI_REG_ADDR_MISC, rc);
+ goto get_out;
+ }
+
+ misc &= ~mask;
+ misc |= flag;
+
+ rc = ssbi_write(chip->dev, SSBI_REG_ADDR_MISC, &misc, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_write(0x%x)=0x%x: rc=%d\n",
+ __func__, SSBI_REG_ADDR_MISC, misc, rc);
+ goto get_out;
+ }
+
+get_out:
+ mutex_unlock(&chip->pm_lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(pm8058_misc_control);
+
+/**
+ * pm8058_smpl_control - enables/disables SMPL detection
+ * @enable: 0 = shutdown PMIC on power loss, 1 = reset PMIC on power loss
+ *
+ * This function enables or disables the Sudden Momentary Power Loss detection
+ * module. If SMPL detection is enabled, then when a sufficiently long power
+ * loss event occurs, the PMIC will automatically reset itself. If SMPL
+ * detection is disabled, then the PMIC will shutdown when power loss occurs.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8058_smpl_control(int enable)
+{
+ return pm8058_masked_write(SSBI_REG_ADDR_SLEEP_CNTL,
+ (enable ? PM8058_SLEEP_SMPL_EN_RESET
+ : PM8058_SLEEP_SMPL_EN_PWR_OFF),
+ PM8058_SLEEP_SMPL_EN_MASK);
+}
+EXPORT_SYMBOL(pm8058_smpl_control);
+
+/**
+ * pm8058_smpl_set_delay - sets the SMPL detection time delay
+ * @delay: enum value corresponding to delay time
+ *
+ * This function sets the time delay of the SMPL detection module. If power
+ * is reapplied within this interval, then the PMIC reset automatically. The
+ * SMPL detection module must be enabled for this delay time to take effect.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8058_smpl_set_delay(enum pm8058_smpl_delay delay)
+{
+ if (delay < PM8058_SLEEP_SMPL_SEL_MIN
+ || delay > PM8058_SLEEP_SMPL_SEL_MAX) {
+ pr_err("%s: invalid delay specified: %d\n", __func__, delay);
+ return -EINVAL;
+ }
+
+ return pm8058_masked_write(SSBI_REG_ADDR_SLEEP_CNTL, delay,
+ PM8058_SLEEP_SMPL_SEL_MASK);
+}
+EXPORT_SYMBOL(pm8058_smpl_set_delay);
+
+/**
+ * pm8058_watchdog_reset_control - enables/disables watchdog reset detection
+ * @enable: 0 = shutdown when PS_HOLD goes low, 1 = reset when PS_HOLD goes low
+ *
+ * This function enables or disables the PMIC watchdog reset detection feature.
+ * If watchdog reset detection is enabled, then the PMIC will reset itself
+ * when PS_HOLD goes low. If it is not enabled, then the PMIC will shutdown
+ * when PS_HOLD goes low.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8058_watchdog_reset_control(int enable)
+{
+ return pm8058_masked_write(SSBI_REG_ADDR_PON_CNTL_1,
+ (enable ? PM8058_PON_WD_EN_RESET
+ : PM8058_PON_WD_EN_PWR_OFF),
+ PM8058_PON_WD_EN_MASK);
+}
+EXPORT_SYMBOL(pm8058_watchdog_reset_control);
+
+int pm8058_reset_pwr_off(int reset)
+{
+ int rc;
+ u8 pon;
+ u8 ctrl;
+ u8 smpl;
+
+ if (pmic_chip == NULL)
+ return -ENODEV;
+
+ /* Set regulator L22 to 1.225V in high power mode. */
+ rc = ssbi_read(pmic_chip->dev, SSBI_REG_ADDR_L22_CTRL, &ctrl, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_read(0x%x): rc=%d\n", __func__,
+ SSBI_REG_ADDR_L22_CTRL, rc);
+ goto get_out3;
+ }
+ /* Leave pull-down state intact. */
+ ctrl &= 0x40;
+ ctrl |= 0x93;
+ rc = ssbi_write(pmic_chip->dev, SSBI_REG_ADDR_L22_CTRL, &ctrl, 1);
+ if (rc)
+ pr_err("%s: FAIL ssbi_write(0x%x)=0x%x: rc=%d\n", __func__,
+ SSBI_REG_ADDR_L22_CTRL, ctrl, rc);
+
+get_out3:
+ if (!reset) {
+ /* Only modify the SLEEP_CNTL reg if shutdown is desired. */
+ rc = ssbi_read(pmic_chip->dev, SSBI_REG_ADDR_SLEEP_CNTL,
+ &smpl, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_read(0x%x): rc=%d\n",
+ __func__, SSBI_REG_ADDR_SLEEP_CNTL, rc);
+ goto get_out2;
+ }
+
+ smpl &= ~PM8058_SLEEP_SMPL_EN_MASK;
+ smpl |= PM8058_SLEEP_SMPL_EN_PWR_OFF;
+
+ rc = ssbi_write(pmic_chip->dev, SSBI_REG_ADDR_SLEEP_CNTL,
+ &smpl, 1);
+ if (rc)
+ pr_err("%s: FAIL ssbi_write(0x%x)=0x%x: rc=%d\n",
+ __func__, SSBI_REG_ADDR_SLEEP_CNTL, smpl, rc);
+ }
+
+get_out2:
+ rc = ssbi_read(pmic_chip->dev, SSBI_REG_ADDR_PON_CNTL_1, &pon, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_read(0x%x): rc=%d\n",
+ __func__, SSBI_REG_ADDR_PON_CNTL_1, rc);
+ goto get_out;
+ }
+
+ pon &= ~PM8058_PON_WD_EN_MASK;
+ pon |= reset ? PM8058_PON_WD_EN_RESET : PM8058_PON_WD_EN_PWR_OFF;
+
+ /* Enable all pullups */
+ pon |= PM8058_PON_PUP_MASK;
+
+ rc = ssbi_write(pmic_chip->dev, SSBI_REG_ADDR_PON_CNTL_1, &pon, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_write(0x%x)=0x%x: rc=%d\n",
+ __func__, SSBI_REG_ADDR_PON_CNTL_1, pon, rc);
+ goto get_out;
+ }
+
+get_out:
+ return rc;
+}
+EXPORT_SYMBOL(pm8058_reset_pwr_off);
+
+/*
+ power on hard reset configuration
+ config = DISABLE_HARD_RESET to disable hard reset
+ = SHUTDOWN_ON_HARD_RESET to turn off the system on hard reset
+ = RESTART_ON_HARD_RESET to restart the system on hard reset
+ */
+int pm8058_hard_reset_config(enum pon_config config)
+{
+ int rc, ret;
+ u8 pon, pon_5;
+
+ if (config >= MAX_PON_CONFIG)
+ return -EINVAL;
+
+ if (pmic_chip == NULL)
+ return -ENODEV;
+
+ mutex_lock(&pmic_chip->pm_lock);
+
+ rc = ssbi_read(pmic_chip->dev, SSBI_REG_ADDR_PON_CNTL_5, &pon, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_read(0x%x): rc=%d\n",
+ __func__, SSBI_REG_ADDR_PON_CNTL_5, rc);
+ mutex_unlock(&pmic_chip->pm_lock);
+ return rc;
+ }
+
+ pon_5 = pon;
+ (config != DISABLE_HARD_RESET) ? (pon |= PM8058_HARD_RESET_EN_MASK) :
+ (pon &= ~PM8058_HARD_RESET_EN_MASK);
+
+ rc = ssbi_write(pmic_chip->dev, SSBI_REG_ADDR_PON_CNTL_5, &pon, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_write(0x%x)=0x%x: rc=%d\n",
+ __func__, SSBI_REG_ADDR_PON_CNTL_5, pon, rc);
+ mutex_unlock(&pmic_chip->pm_lock);
+ return rc;
+ }
+
+ if (config == DISABLE_HARD_RESET) {
+ mutex_unlock(&pmic_chip->pm_lock);
+ return 0;
+ }
+
+ rc = ssbi_read(pmic_chip->dev, SSBI_REG_ADDR_PON_CNTL_4, &pon, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_read(0x%x): rc=%d\n",
+ __func__, SSBI_REG_ADDR_PON_CNTL_4, rc);
+ goto err_restore_pon_5;
+ }
+
+ (config == RESTART_ON_HARD_RESET) ? (pon |= PM8058_PON_RESET_EN_MASK) :
+ (pon &= ~PM8058_PON_RESET_EN_MASK);
+
+ rc = ssbi_write(pmic_chip->dev, SSBI_REG_ADDR_PON_CNTL_4, &pon, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_write(0x%x)=0x%x: rc=%d\n",
+ __func__, SSBI_REG_ADDR_PON_CNTL_4, pon, rc);
+ goto err_restore_pon_5;
+ }
+ mutex_unlock(&pmic_chip->pm_lock);
+ return 0;
+
+err_restore_pon_5:
+ ret = ssbi_write(pmic_chip->dev, SSBI_REG_ADDR_PON_CNTL_5, &pon_5, 1);
+ if (ret)
+ pr_err("%s: FAIL ssbi_write(0x%x)=0x%x: rc=%d\n",
+ __func__, SSBI_REG_ADDR_PON_CNTL_5, pon, ret);
+ mutex_unlock(&pmic_chip->pm_lock);
+ return rc;
+}
+EXPORT_SYMBOL(pm8058_hard_reset_config);
+
+/* Internal functions */
+static inline int
+pm8058_config_irq(struct pm8058_chip *chip, u8 *bp, u8 *cp)
+{
+ int rc;
+
+ rc = ssbi_write(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp, 1);
+ if (rc) {
+ pr_err("%s: ssbi_write: rc=%d (Select block)\n",
+ __func__, rc);
+ goto bail_out;
+ }
+
+ rc = ssbi_write(chip->dev, SSBI_REG_ADDR_IRQ_CONFIG, cp, 1);
+ if (rc)
+ pr_err("%s: ssbi_write: rc=%d (Configure IRQ)\n",
+ __func__, rc);
+
+bail_out:
+ return rc;
+}
+
+static void pm8058_irq_mask(struct irq_data *data)
+{
+ int master, irq_bit;
+ struct pm8058_chip *chip = irq_data_get_irq_chip_data(data);
+ u8 block, config;
+ unsigned int irq = data->irq;
+
+ irq -= chip->pdata.irq_base;
+ block = irq / 8;
+ master = block / 8;
+ irq_bit = irq % 8;
+
+ chip->irqs_allowed[block] &= ~(1 << irq_bit);
+ if (!chip->irqs_allowed[block]) {
+ chip->blocks_allowed[master] &= ~(1 << (block % 8));
+
+ if (!chip->blocks_allowed[master])
+ chip->masters_allowed &= ~(1 << master);
+ }
+
+ config = PM8058_IRQF_WRITE | chip->config[irq] |
+ PM8058_IRQF_MASK_FE | PM8058_IRQF_MASK_RE;
+ chip->bus_unlock_config[irq] = config;
+}
+
+static void pm8058_irq_unmask(struct irq_data *data)
+{
+ int master, irq_bit;
+ struct pm8058_chip *chip = irq_data_get_irq_chip_data(data);
+ u8 block, config, old_irqs_allowed, old_blocks_allowed;
+ unsigned int irq = data->irq;
+
+ irq -= chip->pdata.irq_base;
+ block = irq / 8;
+ master = block / 8;
+ irq_bit = irq % 8;
+
+ old_irqs_allowed = chip->irqs_allowed[block];
+ chip->irqs_allowed[block] |= 1 << irq_bit;
+ if (!old_irqs_allowed) {
+ master = block / 8;
+
+ old_blocks_allowed = chip->blocks_allowed[master];
+ chip->blocks_allowed[master] |= 1 << (block % 8);
+
+ if (!old_blocks_allowed)
+ chip->masters_allowed |= 1 << master;
+ }
+
+ config = PM8058_IRQF_WRITE | chip->config[irq];
+ chip->bus_unlock_config[irq] = config;
+}
+
+static void pm8058_irq_ack(struct irq_data *data)
+{
+ struct pm8058_chip *chip = irq_data_get_irq_chip_data(data);
+ u8 block, config;
+ unsigned int irq = data->irq;
+
+ irq -= chip->pdata.irq_base;
+ block = irq / 8;
+
+ config = PM8058_IRQF_WRITE | chip->config[irq] | PM8058_IRQF_CLR;
+ /* Keep the mask */
+ if (!(chip->irqs_allowed[block] & (1 << (irq % 8))))
+ config |= PM8058_IRQF_MASK_FE | PM8058_IRQF_MASK_RE;
+ chip->bus_unlock_config[irq] = config;
+}
+
+static int pm8058_irq_set_type(struct irq_data *data, unsigned int flow_type)
+{
+ int master, irq_bit;
+ struct pm8058_chip *chip = irq_data_get_irq_chip_data(data);
+ u8 block, config;
+ unsigned int irq = data->irq;
+
+ irq -= chip->pdata.irq_base;
+ if (irq > chip->pm_max_irq) {
+ chip->pm_max_irq = irq;
+ chip->pm_max_blocks =
+ chip->pm_max_irq / 8 + 1;
+ chip->pm_max_masters =
+ chip->pm_max_blocks / 8 + 1;
+ }
+ block = irq / 8;
+ master = block / 8;
+ irq_bit = irq % 8;
+
+ chip->config[irq] = (irq_bit << PM8058_IRQF_BITS_SHIFT) |
+ PM8058_IRQF_MASK_RE | PM8058_IRQF_MASK_FE;
+ if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
+ if (flow_type & IRQF_TRIGGER_RISING)
+ chip->config[irq] &= ~PM8058_IRQF_MASK_RE;
+ if (flow_type & IRQF_TRIGGER_FALLING)
+ chip->config[irq] &= ~PM8058_IRQF_MASK_FE;
+ } else {
+ chip->config[irq] |= PM8058_IRQF_LVL_SEL;
+
+ if (flow_type & IRQF_TRIGGER_HIGH)
+ chip->config[irq] &= ~PM8058_IRQF_MASK_RE;
+ else
+ chip->config[irq] &= ~PM8058_IRQF_MASK_FE;
+ }
+
+ config = PM8058_IRQF_WRITE | chip->config[irq] | PM8058_IRQF_CLR;
+ chip->bus_unlock_config[irq] = config;
+ return 0;
+}
+
+static int pm8058_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+ struct pm8058_chip *chip = irq_data_get_irq_chip_data(data);
+ unsigned int irq = data->irq;
+
+ irq -= chip->pdata.irq_base;
+ if (on) {
+ if (!chip->wake_enable[irq]) {
+ chip->wake_enable[irq] = 1;
+ chip->count_wakeable++;
+ }
+ } else {
+ if (chip->wake_enable[irq]) {
+ chip->wake_enable[irq] = 0;
+ chip->count_wakeable--;
+ }
+ }
+
+ return 0;
+}
+
+static void pm8058_irq_bus_lock(struct irq_data *data)
+{
+ u8 block;
+ struct pm8058_chip *chip = irq_data_get_irq_chip_data(data);
+ unsigned int irq = data->irq;
+
+ irq -= chip->pdata.irq_base;
+ block = irq / 8;
+ chip->bus_unlock_config[irq] = 0;
+
+ mutex_lock(&chip->pm_lock);
+}
+
+static void pm8058_irq_bus_sync_unlock(struct irq_data *data)
+{
+ u8 block, config;
+ struct pm8058_chip *chip = irq_data_get_irq_chip_data(data);
+ unsigned int irq = data->irq;
+
+ irq -= chip->pdata.irq_base;
+ block = irq / 8;
+ config = chip->bus_unlock_config[irq];
+ /* dont waste cpu cycles if we dont have data to write */
+ if (config)
+ pm8058_config_irq(chip, &block, &config);
+ mutex_unlock(&chip->pm_lock);
+}
+
+static inline int
+pm8058_read_root(struct pm8058_chip *chip, u8 *rp)
+{
+ int rc;
+
+ rc = ssbi_read(chip->dev, SSBI_REG_ADDR_IRQ_ROOT, rp, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_read(): rc=%d (Read Root)\n",
+ __func__, rc);
+ *rp = 0;
+ }
+
+ return rc;
+}
+
+static inline int
+pm8058_read_master(struct pm8058_chip *chip, u8 m, u8 *bp)
+{
+ int rc;
+
+ rc = ssbi_read(chip->dev, SSBI_REG_ADDR_IRQ_M_STATUS1 + m, bp, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_read(): rc=%d (Read Master)\n",
+ __func__, rc);
+ *bp = 0;
+ }
+
+ return rc;
+}
+
+static inline int
+pm8058_read_block(struct pm8058_chip *chip, u8 *bp, u8 *ip)
+{
+ int rc;
+
+ rc = ssbi_write(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_write(): rc=%d (Select Block)\n",
+ __func__, rc);
+ *bp = 0;
+ goto bail_out;
+ }
+
+ rc = ssbi_read(chip->dev, SSBI_REG_ADDR_IRQ_IT_STATUS, ip, 1);
+ if (rc)
+ pr_err("%s: FAIL ssbi_read(): rc=%d (Read Status)\n",
+ __func__, rc);
+
+bail_out:
+ return rc;
+}
+
+static irqreturn_t pm8058_isr_thread(int irq_requested, void *data)
+{
+ struct pm8058_chip *chip = data;
+ int i, j, k;
+ u8 root, block, config, bits;
+ u8 blocks[MAX_PM_MASTERS];
+ int masters = 0, irq, handled = 0, spurious = 0;
+ u16 irqs_to_handle[MAX_PM_IRQ];
+
+ mutex_lock(&chip->pm_lock);
+
+ /* Read root for masters */
+ if (pm8058_read_root(chip, &root))
+ goto bail_out;
+
+ masters = root >> 1;
+
+ if (!(masters & chip->masters_allowed) ||
+ (masters & ~chip->masters_allowed)) {
+ spurious = 1000000;
+ }
+
+ /* Read allowed masters for blocks. */
+ for (i = 0; i < chip->pm_max_masters; i++) {
+ if (masters & (1 << i)) {
+ if (pm8058_read_master(chip, i, &blocks[i]))
+ goto bail_out;
+
+ if (!blocks[i]) {
+ if (pm8058_can_print())
+ pr_err("%s: Spurious master: %d "
+ "(blocks=0)", __func__, i);
+ spurious += 10000;
+ }
+ } else
+ blocks[i] = 0;
+ }
+
+ /* Select block, read status and call isr */
+ for (i = 0; i < chip->pm_max_masters; i++) {
+ if (!blocks[i])
+ continue;
+
+ for (j = 0; j < 8; j++) {
+ if (!(blocks[i] & (1 << j)))
+ continue;
+
+ block = i * 8 + j; /* block # */
+ if (pm8058_read_block(chip, &block, &bits))
+ goto bail_out;
+
+ if (!bits) {
+ if (pm8058_can_print())
+ pr_err("%s: Spurious block: "
+ "[master, block]=[%d, %d] "
+ "(bits=0)\n", __func__, i, j);
+ spurious += 100;
+ continue;
+ }
+
+ /* Check IRQ bits */
+ for (k = 0; k < 8; k++) {
+ if (!(bits & (1 << k)))
+ continue;
+
+ /* Check spurious interrupts */
+ if (((1 << i) & chip->masters_allowed) &&
+ (blocks[i] & chip->blocks_allowed[i]) &&
+ (bits & chip->irqs_allowed[block])) {
+
+ /* Found one */
+ irq = block * 8 + k;
+ irqs_to_handle[handled] = irq +
+ chip->pdata.irq_base;
+ handled++;
+ } else {
+ /* Clear and mask wrong one */
+ config = PM8058_IRQF_W_C_M |
+ (k << PM8058_IRQF_BITS_SHIFT);
+
+ pm8058_config_irq(chip,
+ &block, &config);
+
+ if (pm8058_can_print())
+ pr_err("%s: Spurious IRQ: "
+ "[master, block, bit]="
+ "[%d, %d (%d), %d]\n",
+ __func__,
+ i, j, block, k);
+ spurious++;
+ }
+ }
+ }
+
+ }
+
+bail_out:
+
+ mutex_unlock(&chip->pm_lock);
+
+ for (i = 0; i < handled; i++)
+ handle_nested_irq(irqs_to_handle[i]);
+
+ for (i = 0; i < handled; i++) {
+ irqs_to_handle[i] -= chip->pdata.irq_base;
+ block = irqs_to_handle[i] / 8 ;
+ config = PM8058_IRQF_WRITE | chip->config[irqs_to_handle[i]]
+ | PM8058_IRQF_CLR;
+ pm8058_config_irq(chip, &block, &config);
+ }
+
+ if (spurious) {
+ if (!pm8058_can_print())
+ return IRQ_HANDLED;
+
+ pr_err("%s: spurious = %d (handled = %d)\n",
+ __func__, spurious, handled);
+ pr_err(" root = 0x%x (masters_allowed<<1 = 0x%x)\n",
+ root, chip->masters_allowed << 1);
+ for (i = 0; i < chip->pm_max_masters; i++) {
+ if (masters & (1 << i))
+ pr_err(" blocks[%d]=0x%x, "
+ "allowed[%d]=0x%x\n",
+ i, blocks[i],
+ i, chip->blocks_allowed[i]);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+
+static int check_addr(int addr, const char *func_name)
+{
+ if (addr < 0 || addr > 0x3FF) {
+ pr_err("%s: PMIC 8058 register address is invalid: %d\n",
+ func_name, addr);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int data_set(void *data, u64 val)
+{
+ struct pm8058_dbg_device *dbgdev = data;
+ u8 reg = val;
+ int rc;
+
+ mutex_lock(&dbgdev->dbg_mutex);
+
+ rc = check_addr(dbgdev->addr, __func__);
+ if (rc)
+ goto done;
+
+ rc = pm8058_write(dbgdev->pm_chip, dbgdev->addr, ®, 1);
+
+ if (rc)
+ pr_err("%s: FAIL pm8058_write(0x%03X)=0x%02X: rc=%d\n",
+ __func__, dbgdev->addr, reg, rc);
+done:
+ mutex_unlock(&dbgdev->dbg_mutex);
+ return rc;
+}
+
+static int data_get(void *data, u64 *val)
+{
+ struct pm8058_dbg_device *dbgdev = data;
+ int rc;
+ u8 reg;
+
+ mutex_lock(&dbgdev->dbg_mutex);
+
+ rc = check_addr(dbgdev->addr, __func__);
+ if (rc)
+ goto done;
+
+ rc = pm8058_read(dbgdev->pm_chip, dbgdev->addr, ®, 1);
+
+ if (rc) {
+ pr_err("%s: FAIL pm8058_read(0x%03X)=0x%02X: rc=%d\n",
+ __func__, dbgdev->addr, reg, rc);
+ goto done;
+ }
+
+ *val = reg;
+done:
+ mutex_unlock(&dbgdev->dbg_mutex);
+ return rc;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(dbg_data_fops, data_get, data_set, "0x%02llX\n");
+
+static int addr_set(void *data, u64 val)
+{
+ struct pm8058_dbg_device *dbgdev = data;
+ int rc;
+
+ rc = check_addr(val, __func__);
+ if (rc)
+ return rc;
+
+ mutex_lock(&dbgdev->dbg_mutex);
+ dbgdev->addr = val;
+ mutex_unlock(&dbgdev->dbg_mutex);
+
+ return 0;
+}
+
+static int addr_get(void *data, u64 *val)
+{
+ struct pm8058_dbg_device *dbgdev = data;
+ int rc;
+
+ mutex_lock(&dbgdev->dbg_mutex);
+
+ rc = check_addr(dbgdev->addr, __func__);
+ if (rc) {
+ mutex_unlock(&dbgdev->dbg_mutex);
+ return rc;
+ }
+ *val = dbgdev->addr;
+
+ mutex_unlock(&dbgdev->dbg_mutex);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(dbg_addr_fops, addr_get, addr_set, "0x%03llX\n");
+
+static int __devinit pmic8058_dbg_probe(struct pm8058_chip *chip)
+{
+ struct pm8058_dbg_device *dbgdev;
+ struct dentry *dent;
+ struct dentry *temp;
+
+ if (chip == NULL) {
+ pr_err("%s: no parent data passed in.\n", __func__);
+ return -EINVAL;
+ }
+
+ dbgdev = kzalloc(sizeof *dbgdev, GFP_KERNEL);
+ if (dbgdev == NULL) {
+ pr_err("%s: kzalloc() failed.\n", __func__);
+ return -ENOMEM;
+ }
+
+ mutex_init(&dbgdev->dbg_mutex);
+
+ dbgdev->pm_chip = chip;
+ dbgdev->addr = -1;
+
+ dent = debugfs_create_dir("pm8058-dbg", NULL);
+ if (dent == NULL || IS_ERR(dent)) {
+ pr_err("%s: ERR debugfs_create_dir: dent=0x%X\n",
+ __func__, (unsigned)dent);
+ return -ENOMEM;
+ }
+
+ temp = debugfs_create_file("addr", S_IRUSR | S_IWUSR, dent,
+ dbgdev, &dbg_addr_fops);
+ if (temp == NULL || IS_ERR(temp)) {
+ pr_err("%s: ERR debugfs_create_file: dent=0x%X\n",
+ __func__, (unsigned)temp);
+ goto debug_error;
+ }
+
+ temp = debugfs_create_file("data", S_IRUSR | S_IWUSR, dent,
+ dbgdev, &dbg_data_fops);
+ if (temp == NULL || IS_ERR(temp)) {
+ pr_err("%s: ERR debugfs_create_file: dent=0x%X\n",
+ __func__, (unsigned)temp);
+ goto debug_error;
+ }
+
+ dbgdev->dent = dent;
+
+ pmic_dbg_device = dbgdev;
+
+ return 0;
+
+debug_error:
+ debugfs_remove_recursive(dent);
+ return -ENOMEM;
+}
+
+static int __devexit pmic8058_dbg_remove(void)
+{
+ if (pmic_dbg_device) {
+ debugfs_remove_recursive(pmic_dbg_device->dent);
+ kfree(pmic_dbg_device);
+ }
+ return 0;
+}
+
+#else
+
+static int __devinit pmic8058_dbg_probe(struct pm8058_chip *chip)
+{
+ return 0;
+}
+
+static int __devexit pmic8058_dbg_remove(void)
+{
+ return 0;
+}
+
+#endif
+
+static struct irq_chip pm8058_irq_chip = {
+ .name = "pm8058",
+ .irq_ack = pm8058_irq_ack,
+ .irq_mask = pm8058_irq_mask,
+ .irq_unmask = pm8058_irq_unmask,
+ .irq_set_type = pm8058_irq_set_type,
+ .irq_set_wake = pm8058_irq_set_wake,
+ .irq_bus_lock = pm8058_irq_bus_lock,
+ .irq_bus_sync_unlock = pm8058_irq_bus_sync_unlock,
+};
+
+static int pm8058_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int i, rc;
+ struct pm8058_platform_data *pdata = client->dev.platform_data;
+ struct pm8058_chip *chip;
+
+ if (pdata == NULL || !client->irq) {
+ pr_err("%s: No platform_data or IRQ.\n", __func__);
+ return -ENODEV;
+ }
+
+ if (pdata->num_subdevs == 0) {
+ pr_err("%s: No sub devices to support.\n", __func__);
+ return -ENODEV;
+ }
+
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
+ pr_err("%s: i2c_check_functionality failed.\n", __func__);
+ return -ENODEV;
+ }
+
+ chip = kzalloc(sizeof *chip, GFP_KERNEL);
+ if (chip == NULL) {
+ pr_err("%s: kzalloc() failed.\n", __func__);
+ return -ENOMEM;
+ }
+
+ chip->dev = client;
+
+ /* Read PMIC chip revision */
+ rc = ssbi_read(chip->dev, SSBI_REG_REV, &chip->revision, 1);
+ if (rc)
+ pr_err("%s: Failed on ssbi_read for revision: rc=%d.\n",
+ __func__, rc);
+ pr_info("%s: PMIC revision: %X\n", __func__, chip->revision);
+
+ (void) memcpy((void *)&chip->pdata, (const void *)pdata,
+ sizeof(chip->pdata));
+
+ mutex_init(&chip->pm_lock);
+ irq_set_handler_data(chip->dev->irq, (void *)chip);
+ irq_set_irq_wake(chip->dev->irq, 1);
+
+ chip->pm_max_irq = 0;
+ chip->pm_max_blocks = 0;
+ chip->pm_max_masters = 0;
+
+ i2c_set_clientdata(client, chip);
+
+ pmic_chip = chip;
+
+ /* Register for all reserved IRQs */
+ for (i = pdata->irq_base; i < (pdata->irq_base + MAX_PM_IRQ); i++) {
+ irq_set_chip(i, &pm8058_irq_chip);
+ irq_set_chip_data(i, (void *)chip);
+ irq_set_handler(i, handle_edge_irq);
+ set_irq_flags(i, IRQF_VALID);
+ irq_set_nested_thread(i, 1);
+ }
+
+ rc = mfd_add_devices(&chip->dev->dev, 0, pdata->sub_devices,
+ pdata->num_subdevs, NULL, 0);
+
+ /* Add charger sub device with the chip parameter as driver data */
+ if (pdata->charger_sub_device) {
+ rc = mfd_add_devices(&chip->dev->dev, 0,
+ pdata->charger_sub_device,
+ 1, NULL, 0);
+ }
+
+ if (pdata->init) {
+ rc = pdata->init(chip);
+ if (rc != 0) {
+ pr_err("%s: board init failed\n", __func__);
+ chip->dev = NULL;
+ kfree(chip);
+ return -ENODEV;
+ }
+ }
+
+ rc = request_threaded_irq(chip->dev->irq, NULL, pm8058_isr_thread,
+ IRQF_ONESHOT | IRQF_DISABLED | pdata->irq_trigger_flags,
+ "pm8058-irq", chip);
+ if (rc < 0)
+ pr_err("%s: could not request irq %d: %d\n", __func__,
+ chip->dev->irq, rc);
+
+ rc = pmic8058_dbg_probe(chip);
+ if (rc < 0)
+ pr_err("%s: could not set up debugfs: %d\n", __func__, rc);
+
+ return 0;
+}
+
+static int __devexit pm8058_remove(struct i2c_client *client)
+{
+ struct pm8058_chip *chip;
+
+ chip = i2c_get_clientdata(client);
+ if (chip) {
+ if (chip->pm_max_irq) {
+ irq_set_irq_wake(chip->dev->irq, 0);
+ free_irq(chip->dev->irq, chip);
+ }
+ mutex_destroy(&chip->pm_lock);
+ chip->dev = NULL;
+
+ kfree(chip);
+ }
+
+ pmic8058_dbg_remove();
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int pm8058_suspend(struct device *dev)
+{
+ struct i2c_client *client;
+ struct pm8058_chip *chip;
+ struct irq_data *data;
+ int i;
+
+ client = to_i2c_client(dev);
+ chip = i2c_get_clientdata(client);
+
+ for (i = 0; i < MAX_PM_IRQ; i++) {
+ if (chip->config[i] && !chip->wake_enable[i]) {
+ if (!((chip->config[i] & PM8058_IRQF_MASK_ALL)
+ == PM8058_IRQF_MASK_ALL)) {
+ data = irq_get_irq_data(i +
+ chip->pdata.irq_base);
+ pm8058_irq_bus_lock(data);
+ pm8058_irq_mask(data);
+ pm8058_irq_bus_sync_unlock(data);
+ }
+ }
+ }
+
+ if (!chip->count_wakeable)
+ disable_irq(chip->dev->irq);
+
+ return 0;
+}
+
+extern int msm_show_resume_irq_mask;
+
+static void pm8058_show_resume_irq(void)
+{
+ u8 block, bits;
+ int i;
+ struct pm8058_chip *chip = pmic_chip;
+
+ if (!msm_show_resume_irq_mask)
+ return;
+
+ for (i = 0; i < MAX_PM_IRQ; i++) {
+ if (chip->wake_enable[i]) {
+ block = i / 8;
+ if (!pm8058_read_block(chip, &block, &bits)) {
+ if (bits & (1 << (i & 0x7)))
+ pr_warning("%s:%d triggered\n",
+ __func__, i + chip->pdata.irq_base);
+ }
+ }
+ }
+}
+
+static int pm8058_resume(struct device *dev)
+{
+ struct i2c_client *client;
+ struct pm8058_chip *chip;
+ struct irq_data *data;
+ int i;
+
+ pm8058_show_resume_irq();
+
+ client = to_i2c_client(dev);
+ chip = i2c_get_clientdata(client);
+
+ for (i = 0; i < MAX_PM_IRQ; i++) {
+ if (chip->config[i] && !chip->wake_enable[i]) {
+ if (!((chip->config[i] & PM8058_IRQF_MASK_ALL)
+ == PM8058_IRQF_MASK_ALL)) {
+ data = irq_get_irq_data(i +
+ chip->pdata.irq_base);
+ pm8058_irq_bus_lock(data);
+ pm8058_irq_unmask(data);
+ pm8058_irq_bus_sync_unlock(data);
+ }
+ }
+ }
+
+ if (!chip->count_wakeable)
+ enable_irq(chip->dev->irq);
+
+ return 0;
+}
+#else
+#define pm8058_suspend NULL
+#define pm8058_resume NULL
+#endif
+
+static const struct i2c_device_id pm8058_ids[] = {
+ { "pm8058-core", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, pm8058_ids);
+
+static struct dev_pm_ops pm8058_pm = {
+ .suspend = pm8058_suspend,
+ .resume = pm8058_resume,
+};
+
+static struct i2c_driver pm8058_driver = {
+ .driver.name = "pm8058-core",
+ .driver.pm = &pm8058_pm,
+ .id_table = pm8058_ids,
+ .probe = pm8058_probe,
+ .remove = __devexit_p(pm8058_remove),
+};
+
+static int __init pm8058_init(void)
+{
+ int rc = i2c_add_driver(&pm8058_driver);
+ pr_notice("%s: i2c_add_driver: rc = %d\n", __func__, rc);
+
+ return rc;
+}
+
+static void __exit pm8058_exit(void)
+{
+ i2c_del_driver(&pm8058_driver);
+}
+
+arch_initcall(pm8058_init);
+module_exit(pm8058_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8058 core driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pmic8058-core");
diff --git a/drivers/mfd/pmic8901.c b/drivers/mfd/pmic8901.c
new file mode 100644
index 0000000..da46656
--- /dev/null
+++ b/drivers/mfd/pmic8901.c
@@ -0,0 +1,953 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. 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 and
+ * only 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.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/ratelimit.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/pmic8901.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+
+/* PMIC8901 Revision */
+#define SSBI_REG_REV 0x002 /* PMIC4 revision */
+
+/* PMIC8901 IRQ */
+#define SSBI_REG_ADDR_IRQ_BASE 0xD5
+
+#define SSBI_REG_ADDR_IRQ_ROOT (SSBI_REG_ADDR_IRQ_BASE + 0)
+#define SSBI_REG_ADDR_IRQ_M_STATUS1 (SSBI_REG_ADDR_IRQ_BASE + 1)
+#define SSBI_REG_ADDR_IRQ_M_STATUS2 (SSBI_REG_ADDR_IRQ_BASE + 2)
+#define SSBI_REG_ADDR_IRQ_M_STATUS3 (SSBI_REG_ADDR_IRQ_BASE + 3)
+#define SSBI_REG_ADDR_IRQ_M_STATUS4 (SSBI_REG_ADDR_IRQ_BASE + 4)
+#define SSBI_REG_ADDR_IRQ_BLK_SEL (SSBI_REG_ADDR_IRQ_BASE + 5)
+#define SSBI_REG_ADDR_IRQ_IT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 6)
+#define SSBI_REG_ADDR_IRQ_CONFIG (SSBI_REG_ADDR_IRQ_BASE + 7)
+#define SSBI_REG_ADDR_IRQ_RT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 8)
+
+#define PM8901_IRQF_LVL_SEL 0x01 /* level select */
+#define PM8901_IRQF_MASK_FE 0x02 /* mask falling edge */
+#define PM8901_IRQF_MASK_RE 0x04 /* mask rising edge */
+#define PM8901_IRQF_CLR 0x08 /* clear interrupt */
+#define PM8901_IRQF_BITS_MASK 0x70
+#define PM8901_IRQF_BITS_SHIFT 4
+#define PM8901_IRQF_WRITE 0x80
+
+#define PM8901_IRQF_MASK_ALL (PM8901_IRQF_MASK_FE | \
+ PM8901_IRQF_MASK_RE)
+#define PM8901_IRQF_W_C_M (PM8901_IRQF_WRITE | \
+ PM8901_IRQF_CLR | \
+ PM8901_IRQF_MASK_ALL)
+
+#define MAX_PM_IRQ 72
+#define MAX_PM_BLOCKS (MAX_PM_IRQ / 8 + 1)
+#define MAX_PM_MASTERS (MAX_PM_BLOCKS / 8 + 1)
+
+#define MPP_IRQ_BLOCK 1
+
+/* FTS regulator PMR registers */
+#define SSBI_REG_ADDR_S1_PMR (0xA7)
+#define SSBI_REG_ADDR_S2_PMR (0xA8)
+#define SSBI_REG_ADDR_S3_PMR (0xA9)
+#define SSBI_REG_ADDR_S4_PMR (0xAA)
+
+#define REGULATOR_PMR_STATE_MASK 0x60
+#define REGULATOR_PMR_STATE_OFF 0x20
+
+struct pm8901_chip {
+ struct pm8901_platform_data pdata;
+
+ struct i2c_client *dev;
+
+ u8 irqs_allowed[MAX_PM_BLOCKS];
+ u8 blocks_allowed[MAX_PM_MASTERS];
+ u8 masters_allowed;
+ int pm_max_irq;
+ int pm_max_blocks;
+ int pm_max_masters;
+
+ u8 config[MAX_PM_IRQ];
+ u8 wake_enable[MAX_PM_IRQ];
+ u16 count_wakeable;
+
+ u8 revision;
+
+ spinlock_t pm_lock;
+};
+
+#if defined(CONFIG_DEBUG_FS)
+struct pm8901_dbg_device {
+ struct mutex dbg_mutex;
+ struct pm8901_chip *pm_chip;
+ struct dentry *dent;
+ int addr;
+};
+
+static struct pm8901_dbg_device *pmic_dbg_device;
+#endif
+
+static struct pm8901_chip *pmic_chip;
+
+/* Helper Functions */
+DEFINE_RATELIMIT_STATE(pm8901_msg_ratelimit, 60 * HZ, 10);
+
+static inline int pm8901_can_print(void)
+{
+ return __ratelimit(&pm8901_msg_ratelimit);
+}
+
+static inline int
+ssbi_write(struct i2c_client *client, u16 addr, const u8 *buf, size_t len)
+{
+ int rc;
+ struct i2c_msg msg = {
+ .addr = addr,
+ .flags = 0x0,
+ .buf = (u8 *)buf,
+ .len = len,
+ };
+
+ rc = i2c_transfer(client->adapter, &msg, 1);
+ return (rc == 1) ? 0 : rc;
+}
+
+static inline int
+ssbi_read(struct i2c_client *client, u16 addr, u8 *buf, size_t len)
+{
+ int rc;
+ struct i2c_msg msg = {
+ .addr = addr,
+ .flags = I2C_M_RD,
+ .buf = buf,
+ .len = len,
+ };
+
+ rc = i2c_transfer(client->adapter, &msg, 1);
+ return (rc == 1) ? 0 : rc;
+}
+
+/* External APIs */
+int pm8901_rev(struct pm8901_chip *chip)
+{
+ if (chip == NULL) {
+ if (pmic_chip != NULL)
+ return pmic_chip->revision;
+ else
+ return -EINVAL;
+ }
+
+ return chip->revision;
+}
+EXPORT_SYMBOL(pm8901_rev);
+
+int pm8901_read(struct pm8901_chip *chip, u16 addr, u8 *values,
+ unsigned int len)
+{
+ if (chip == NULL)
+ return -EINVAL;
+
+ return ssbi_read(chip->dev, addr, values, len);
+}
+EXPORT_SYMBOL(pm8901_read);
+
+int pm8901_write(struct pm8901_chip *chip, u16 addr, u8 *values,
+ unsigned int len)
+{
+ if (chip == NULL)
+ return -EINVAL;
+
+ return ssbi_write(chip->dev, addr, values, len);
+}
+EXPORT_SYMBOL(pm8901_write);
+
+int pm8901_irq_get_rt_status(struct pm8901_chip *chip, int irq)
+{
+ int rc;
+ u8 block, bits, bit;
+ unsigned long irqsave;
+
+ if (chip == NULL || irq < chip->pdata.irq_base ||
+ irq >= chip->pdata.irq_base + MAX_PM_IRQ)
+ return -EINVAL;
+
+ irq -= chip->pdata.irq_base;
+
+ block = irq / 8;
+ bit = irq % 8;
+
+ spin_lock_irqsave(&chip->pm_lock, irqsave);
+
+ rc = ssbi_write(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, &block, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_write(): rc=%d (Select Block)\n",
+ __func__, rc);
+ goto bail_out;
+ }
+
+ rc = ssbi_read(chip->dev, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_read(): rc=%d (Read RT Status)\n",
+ __func__, rc);
+ goto bail_out;
+ }
+
+ rc = (bits & (1 << bit)) ? 1 : 0;
+
+bail_out:
+ spin_unlock_irqrestore(&chip->pm_lock, irqsave);
+
+ return rc;
+}
+EXPORT_SYMBOL(pm8901_irq_get_rt_status);
+
+int pm8901_reset_pwr_off(int reset)
+{
+ int rc = 0, i;
+ u8 pmr;
+ u8 pmr_addr[4] = {
+ SSBI_REG_ADDR_S2_PMR,
+ SSBI_REG_ADDR_S3_PMR,
+ SSBI_REG_ADDR_S4_PMR,
+ SSBI_REG_ADDR_S1_PMR,
+ };
+
+ if (pmic_chip == NULL)
+ return -ENODEV;
+
+ /* Turn off regulators S1, S2, S3, S4 when shutting down. */
+ if (!reset) {
+ for (i = 0; i < 4; i++) {
+ rc = ssbi_read(pmic_chip->dev, pmr_addr[i], &pmr, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_read(0x%x): rc=%d\n",
+ __func__, pmr_addr[i], rc);
+ goto get_out;
+ }
+
+ pmr &= ~REGULATOR_PMR_STATE_MASK;
+ pmr |= REGULATOR_PMR_STATE_OFF;
+
+ rc = ssbi_write(pmic_chip->dev, pmr_addr[i], &pmr, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_write(0x%x)=0x%x: rc=%d"
+ "\n", __func__, pmr_addr[i], pmr, rc);
+ goto get_out;
+ }
+ }
+ }
+
+get_out:
+ return rc;
+}
+EXPORT_SYMBOL(pm8901_reset_pwr_off);
+
+/* Internal functions */
+static inline int
+pm8901_config_irq(struct pm8901_chip *chip, u8 *bp, u8 *cp)
+{
+ int rc;
+
+ rc = ssbi_write(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp, 1);
+ if (rc) {
+ pr_err("%s: ssbi_write: rc=%d (Select block)\n",
+ __func__, rc);
+ goto bail_out;
+ }
+
+ rc = ssbi_write(chip->dev, SSBI_REG_ADDR_IRQ_CONFIG, cp, 1);
+ if (rc)
+ pr_err("%s: ssbi_write: rc=%d (Configure IRQ)\n",
+ __func__, rc);
+
+bail_out:
+ return rc;
+}
+
+static void pm8901_irq_mask(struct irq_data *d)
+{
+ int master, irq_bit;
+ struct pm8901_chip *chip = irq_data_get_irq_handler_data(d);
+ u8 block, config;
+ unsigned int irq = d->irq;
+
+ irq -= chip->pdata.irq_base;
+ block = irq / 8;
+ master = block / 8;
+ irq_bit = irq % 8;
+
+ chip->irqs_allowed[block] &= ~(1 << irq_bit);
+ if (!chip->irqs_allowed[block]) {
+ chip->blocks_allowed[master] &= ~(1 << (block % 8));
+
+ if (!chip->blocks_allowed[master])
+ chip->masters_allowed &= ~(1 << master);
+ }
+
+ config = PM8901_IRQF_WRITE | chip->config[irq] |
+ PM8901_IRQF_MASK_FE | PM8901_IRQF_MASK_RE;
+ pm8901_config_irq(chip, &block, &config);
+}
+
+static void pm8901_irq_unmask(struct irq_data *d)
+{
+ int master, irq_bit;
+ struct pm8901_chip *chip = irq_data_get_irq_handler_data(d);
+ u8 block, config, old_irqs_allowed, old_blocks_allowed;
+ unsigned int irq = d->irq;
+
+ irq -= chip->pdata.irq_base;
+ block = irq / 8;
+ master = block / 8;
+ irq_bit = irq % 8;
+
+ old_irqs_allowed = chip->irqs_allowed[block];
+ chip->irqs_allowed[block] |= 1 << irq_bit;
+ if (!old_irqs_allowed) {
+ master = block / 8;
+
+ old_blocks_allowed = chip->blocks_allowed[master];
+ chip->blocks_allowed[master] |= 1 << (block % 8);
+
+ if (!old_blocks_allowed)
+ chip->masters_allowed |= 1 << master;
+ }
+
+ config = PM8901_IRQF_WRITE | chip->config[irq];
+ pm8901_config_irq(chip, &block, &config);
+}
+
+static void pm8901_irq_ack(struct irq_data *d)
+{
+ struct pm8901_chip *chip = irq_data_get_irq_handler_data(d);
+ u8 block, config;
+ unsigned int irq = d->irq;
+
+ irq -= chip->pdata.irq_base;
+ block = irq / 8;
+
+ config = PM8901_IRQF_WRITE | chip->config[irq] | PM8901_IRQF_CLR;
+ pm8901_config_irq(chip, &block, &config);
+}
+
+static int pm8901_irq_set_type(struct irq_data *d, unsigned int flow_type)
+{
+ int master, irq_bit;
+ struct pm8901_chip *chip = irq_data_get_irq_handler_data(d);
+ u8 block, config;
+ unsigned int irq = d->irq;
+
+ irq -= chip->pdata.irq_base;
+ if (irq > chip->pm_max_irq) {
+ chip->pm_max_irq = irq;
+ chip->pm_max_blocks =
+ chip->pm_max_irq / 8 + 1;
+ chip->pm_max_masters =
+ chip->pm_max_blocks / 8 + 1;
+ }
+ block = irq / 8;
+ master = block / 8;
+ irq_bit = irq % 8;
+
+ chip->config[irq] = (irq_bit << PM8901_IRQF_BITS_SHIFT) |
+ PM8901_IRQF_MASK_RE | PM8901_IRQF_MASK_FE;
+ if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
+ if (flow_type & IRQF_TRIGGER_RISING)
+ chip->config[irq] &= ~PM8901_IRQF_MASK_RE;
+ if (flow_type & IRQF_TRIGGER_FALLING)
+ chip->config[irq] &= ~PM8901_IRQF_MASK_FE;
+ } else {
+ chip->config[irq] |= PM8901_IRQF_LVL_SEL;
+
+ if (flow_type & IRQF_TRIGGER_HIGH)
+ chip->config[irq] &= ~PM8901_IRQF_MASK_RE;
+ else
+ chip->config[irq] &= ~PM8901_IRQF_MASK_FE;
+ }
+
+ config = PM8901_IRQF_WRITE | chip->config[irq] | PM8901_IRQF_CLR;
+ return pm8901_config_irq(chip, &block, &config);
+}
+
+static int pm8901_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+ struct pm8901_chip *chip = irq_data_get_irq_handler_data(d);
+ unsigned int irq = d->irq;
+
+ irq -= chip->pdata.irq_base;
+ if (on) {
+ if (!chip->wake_enable[irq]) {
+ chip->wake_enable[irq] = 1;
+ chip->count_wakeable++;
+ }
+ } else {
+ if (chip->wake_enable[irq]) {
+ chip->wake_enable[irq] = 0;
+ chip->count_wakeable--;
+ }
+ }
+
+ return 0;
+}
+
+static inline int
+pm8901_read_root(struct pm8901_chip *chip, u8 *rp)
+{
+ int rc;
+
+ rc = ssbi_read(chip->dev, SSBI_REG_ADDR_IRQ_ROOT, rp, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_read(): rc=%d (Read Root)\n",
+ __func__, rc);
+ *rp = 0;
+ }
+
+ return rc;
+}
+
+static inline int
+pm8901_read_master(struct pm8901_chip *chip, u8 m, u8 *bp)
+{
+ int rc;
+
+ rc = ssbi_read(chip->dev, SSBI_REG_ADDR_IRQ_M_STATUS1 + m, bp, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_read(): rc=%d (Read Master)\n",
+ __func__, rc);
+ *bp = 0;
+ }
+
+ return rc;
+}
+
+static inline int
+pm8901_read_block(struct pm8901_chip *chip, u8 *bp, u8 *ip)
+{
+ int rc;
+
+ rc = ssbi_write(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp, 1);
+ if (rc) {
+ pr_err("%s: FAIL ssbi_write(): rc=%d (Select Block)\n",
+ __func__, rc);
+ *bp = 0;
+ goto bail_out;
+ }
+
+ rc = ssbi_read(chip->dev, SSBI_REG_ADDR_IRQ_IT_STATUS, ip, 1);
+ if (rc)
+ pr_err("%s: FAIL ssbi_read(): rc=%d (Read Status)\n",
+ __func__, rc);
+
+bail_out:
+ return rc;
+}
+
+static irqreturn_t pm8901_isr_thread(int irq_requested, void *data)
+{
+ struct pm8901_chip *chip = data;
+ int i, j, k;
+ u8 root, block, config, bits;
+ u8 blocks[MAX_PM_MASTERS];
+ int masters = 0, irq, handled = 0, spurious = 0;
+ u16 irqs_to_handle[MAX_PM_IRQ];
+ unsigned long irqsave;
+
+ spin_lock_irqsave(&chip->pm_lock, irqsave);
+
+ /* Read root for masters */
+ if (pm8901_read_root(chip, &root))
+ goto bail_out;
+
+ masters = root >> 1;
+
+ if (!(masters & chip->masters_allowed) ||
+ (masters & ~chip->masters_allowed)) {
+ spurious = 1000000;
+ }
+
+ /* Read allowed masters for blocks. */
+ for (i = 0; i < chip->pm_max_masters; i++) {
+ if (masters & (1 << i)) {
+ if (pm8901_read_master(chip, i, &blocks[i]))
+ goto bail_out;
+
+ if (!blocks[i]) {
+ if (pm8901_can_print())
+ pr_err("%s: Spurious master: %d "
+ "(blocks=0)", __func__, i);
+ spurious += 10000;
+ }
+ } else
+ blocks[i] = 0;
+ }
+
+ /* Select block, read status and call isr */
+ for (i = 0; i < chip->pm_max_masters; i++) {
+ if (!blocks[i])
+ continue;
+
+ for (j = 0; j < 8; j++) {
+ if (!(blocks[i] & (1 << j)))
+ continue;
+
+ block = i * 8 + j; /* block # */
+ if (pm8901_read_block(chip, &block, &bits))
+ goto bail_out;
+
+ if (!bits) {
+ if (pm8901_can_print())
+ pr_err("%s: Spurious block: "
+ "[master, block]=[%d, %d] "
+ "(bits=0)\n", __func__, i, j);
+ spurious += 100;
+ continue;
+ }
+
+ /* Check IRQ bits */
+ for (k = 0; k < 8; k++) {
+ if (!(bits & (1 << k)))
+ continue;
+
+ /* Check spurious interrupts */
+ if (((1 << i) & chip->masters_allowed) &&
+ (blocks[i] & chip->blocks_allowed[i]) &&
+ (bits & chip->irqs_allowed[block])) {
+
+ /* Found one */
+ irq = block * 8 + k;
+ irqs_to_handle[handled] = irq +
+ chip->pdata.irq_base;
+ handled++;
+ } else {
+ /* Clear and mask wrong one */
+ config = PM8901_IRQF_W_C_M |
+ (k < PM8901_IRQF_BITS_SHIFT);
+
+ pm8901_config_irq(chip,
+ &block, &config);
+
+ if (pm8901_can_print())
+ pr_err("%s: Spurious IRQ: "
+ "[master, block, bit]="
+ "[%d, %d (%d), %d]\n",
+ __func__,
+ i, j, block, k);
+ spurious++;
+ }
+ }
+ }
+
+ }
+
+bail_out:
+
+ spin_unlock_irqrestore(&chip->pm_lock, irqsave);
+
+ for (i = 0; i < handled; i++)
+ generic_handle_irq(irqs_to_handle[i]);
+
+ if (spurious) {
+ if (!pm8901_can_print())
+ return IRQ_HANDLED;
+
+ pr_err("%s: spurious = %d (handled = %d)\n",
+ __func__, spurious, handled);
+ pr_err(" root = 0x%x (masters_allowed<<1 = 0x%x)\n",
+ root, chip->masters_allowed << 1);
+ for (i = 0; i < chip->pm_max_masters; i++) {
+ if (masters & (1 << i))
+ pr_err(" blocks[%d]=0x%x, "
+ "allowed[%d]=0x%x\n",
+ i, blocks[i],
+ i, chip->blocks_allowed[i]);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+
+static int check_addr(int addr, const char *func_name)
+{
+ if (addr < 0 || addr > 0x3FF) {
+ pr_err("%s: PMIC 8901 register address is invalid: %d\n",
+ func_name, addr);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int data_set(void *data, u64 val)
+{
+ struct pm8901_dbg_device *dbgdev = data;
+ u8 reg = val;
+ int rc;
+
+ mutex_lock(&dbgdev->dbg_mutex);
+
+ rc = check_addr(dbgdev->addr, __func__);
+ if (rc)
+ goto done;
+
+ rc = pm8901_write(dbgdev->pm_chip, dbgdev->addr, ®, 1);
+
+ if (rc)
+ pr_err("%s: FAIL pm8901_write(0x%03X)=0x%02X: rc=%d\n",
+ __func__, dbgdev->addr, reg, rc);
+done:
+ mutex_unlock(&dbgdev->dbg_mutex);
+ return rc;
+}
+
+static int data_get(void *data, u64 *val)
+{
+ struct pm8901_dbg_device *dbgdev = data;
+ int rc;
+ u8 reg;
+
+ mutex_lock(&dbgdev->dbg_mutex);
+
+ rc = check_addr(dbgdev->addr, __func__);
+ if (rc)
+ goto done;
+
+ rc = pm8901_read(dbgdev->pm_chip, dbgdev->addr, ®, 1);
+
+ if (rc) {
+ pr_err("%s: FAIL pm8901_read(0x%03X)=0x%02X: rc=%d\n",
+ __func__, dbgdev->addr, reg, rc);
+ goto done;
+ }
+
+ *val = reg;
+done:
+ mutex_unlock(&dbgdev->dbg_mutex);
+ return rc;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(dbg_data_fops, data_get, data_set, "0x%02llX\n");
+
+static int addr_set(void *data, u64 val)
+{
+ struct pm8901_dbg_device *dbgdev = data;
+ int rc;
+
+ rc = check_addr(val, __func__);
+ if (rc)
+ return rc;
+
+ mutex_lock(&dbgdev->dbg_mutex);
+ dbgdev->addr = val;
+ mutex_unlock(&dbgdev->dbg_mutex);
+
+ return 0;
+}
+
+static int addr_get(void *data, u64 *val)
+{
+ struct pm8901_dbg_device *dbgdev = data;
+ int rc;
+
+ mutex_lock(&dbgdev->dbg_mutex);
+
+ rc = check_addr(dbgdev->addr, __func__);
+ if (rc) {
+ mutex_unlock(&dbgdev->dbg_mutex);
+ return rc;
+ }
+ *val = dbgdev->addr;
+
+ mutex_unlock(&dbgdev->dbg_mutex);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(dbg_addr_fops, addr_get, addr_set, "0x%03llX\n");
+
+static int __devinit pmic8901_dbg_probe(struct pm8901_chip *chip)
+{
+ struct pm8901_dbg_device *dbgdev;
+ struct dentry *dent;
+ struct dentry *temp;
+
+ if (chip == NULL) {
+ pr_err("%s: no parent data passed in.\n", __func__);
+ return -EINVAL;
+ }
+
+ dbgdev = kzalloc(sizeof *dbgdev, GFP_KERNEL);
+ if (dbgdev == NULL) {
+ pr_err("%s: kzalloc() failed.\n", __func__);
+ return -ENOMEM;
+ }
+
+ mutex_init(&dbgdev->dbg_mutex);
+
+ dbgdev->pm_chip = chip;
+ dbgdev->addr = -1;
+
+ dent = debugfs_create_dir("pm8901-dbg", NULL);
+ if (dent == NULL || IS_ERR(dent)) {
+ pr_err("%s: ERR debugfs_create_dir: dent=0x%X\n",
+ __func__, (unsigned)dent);
+ return -ENOMEM;
+ }
+
+ temp = debugfs_create_file("addr", S_IRUSR | S_IWUSR, dent,
+ dbgdev, &dbg_addr_fops);
+ if (temp == NULL || IS_ERR(temp)) {
+ pr_err("%s: ERR debugfs_create_file: dent=0x%X\n",
+ __func__, (unsigned)temp);
+ goto debug_error;
+ }
+
+ temp = debugfs_create_file("data", S_IRUSR | S_IWUSR, dent,
+ dbgdev, &dbg_data_fops);
+ if (temp == NULL || IS_ERR(temp)) {
+ pr_err("%s: ERR debugfs_create_file: dent=0x%X\n",
+ __func__, (unsigned)temp);
+ goto debug_error;
+ }
+
+ dbgdev->dent = dent;
+
+ pmic_dbg_device = dbgdev;
+
+ return 0;
+
+debug_error:
+ debugfs_remove_recursive(dent);
+ return -ENOMEM;
+}
+
+static int __devexit pmic8901_dbg_remove(void)
+{
+ if (pmic_dbg_device) {
+ debugfs_remove_recursive(pmic_dbg_device->dent);
+ kfree(pmic_dbg_device);
+ }
+ return 0;
+}
+
+#else
+
+static int __devinit pmic8901_dbg_probe(struct pm8901_chip *chip)
+{
+ return 0;
+}
+
+static int __devexit pmic8901_dbg_remove(void)
+{
+ return 0;
+}
+
+#endif
+
+static struct irq_chip pm8901_irq_chip = {
+ .name = "pm8901",
+ .irq_ack = pm8901_irq_ack,
+ .irq_mask = pm8901_irq_mask,
+ .irq_unmask = pm8901_irq_unmask,
+ .irq_set_type = pm8901_irq_set_type,
+ .irq_set_wake = pm8901_irq_set_wake,
+};
+
+static int pm8901_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int i, rc;
+ struct pm8901_platform_data *pdata = client->dev.platform_data;
+ struct pm8901_chip *chip;
+
+ if (pdata == NULL || !client->irq) {
+ pr_err("%s: No platform_data or IRQ.\n", __func__);
+ return -ENODEV;
+ }
+
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
+ pr_err("%s: i2c_check_functionality failed.\n", __func__);
+ return -ENODEV;
+ }
+
+ chip = kzalloc(sizeof *chip, GFP_KERNEL);
+ if (chip == NULL) {
+ pr_err("%s: kzalloc() failed.\n", __func__);
+ return -ENOMEM;
+ }
+
+ chip->dev = client;
+
+ /* Read PMIC chip revision */
+ rc = ssbi_read(chip->dev, SSBI_REG_REV, &chip->revision, 1);
+ if (rc)
+ pr_err("%s: Failed on ssbi_read for revision: rc=%d.\n",
+ __func__, rc);
+ pr_info("%s: PMIC revision: %X\n", __func__, chip->revision);
+
+ (void) memcpy((void *)&chip->pdata, (const void *)pdata,
+ sizeof(chip->pdata));
+
+ irq_set_handler_data(chip->dev->irq, (void *)chip);
+ irq_set_irq_wake(chip->dev->irq, 1);
+
+ chip->pm_max_irq = 0;
+ chip->pm_max_blocks = 0;
+ chip->pm_max_masters = 0;
+
+ i2c_set_clientdata(client, chip);
+
+ pmic_chip = chip;
+ spin_lock_init(&chip->pm_lock);
+
+ /* Register for all reserved IRQs */
+ for (i = pdata->irq_base; i < (pdata->irq_base + MAX_PM_IRQ); i++) {
+ irq_set_chip(i, &pm8901_irq_chip);
+ irq_set_handler(i, handle_edge_irq);
+ set_irq_flags(i, IRQF_VALID);
+ irq_set_handler_data(i, (void *)chip);
+ }
+
+ rc = mfd_add_devices(&chip->dev->dev, 0, pdata->sub_devices,
+ pdata->num_subdevs, NULL, 0);
+ if (rc) {
+ pr_err("%s: could not add devices %d\n", __func__, rc);
+ return rc;
+ }
+
+ rc = request_threaded_irq(chip->dev->irq, NULL, pm8901_isr_thread,
+ IRQF_ONESHOT | IRQF_DISABLED | pdata->irq_trigger_flags,
+ "pm8901-irq", chip);
+ if (rc)
+ pr_err("%s: could not request irq %d: %d\n", __func__,
+ chip->dev->irq, rc);
+
+ rc = pmic8901_dbg_probe(chip);
+ if (rc < 0)
+ pr_err("%s: could not set up debugfs: %d\n", __func__, rc);
+
+ return rc;
+}
+
+static int __devexit pm8901_remove(struct i2c_client *client)
+{
+ struct pm8901_chip *chip;
+
+ chip = i2c_get_clientdata(client);
+ if (chip) {
+ if (chip->pm_max_irq) {
+ irq_set_irq_wake(chip->dev->irq, 0);
+ free_irq(chip->dev->irq, chip);
+ }
+
+ mfd_remove_devices(&chip->dev->dev);
+
+ chip->dev = NULL;
+
+ kfree(chip);
+ }
+
+ pmic8901_dbg_remove();
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int pm8901_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ struct pm8901_chip *chip;
+ int i;
+ unsigned long irqsave;
+
+ chip = i2c_get_clientdata(client);
+
+ for (i = 0; i < MAX_PM_IRQ; i++) {
+ spin_lock_irqsave(&chip->pm_lock, irqsave);
+ if (chip->config[i] && !chip->wake_enable[i]) {
+ if (!((chip->config[i] & PM8901_IRQF_MASK_ALL)
+ == PM8901_IRQF_MASK_ALL))
+ pm8901_irq_mask(irq_get_irq_data(i +
+ chip->pdata.irq_base));
+ }
+ spin_unlock_irqrestore(&chip->pm_lock, irqsave);
+ }
+
+ if (!chip->count_wakeable)
+ disable_irq(chip->dev->irq);
+
+ return 0;
+}
+
+static int pm8901_resume(struct i2c_client *client)
+{
+ struct pm8901_chip *chip;
+ int i;
+ unsigned long irqsave;
+
+ chip = i2c_get_clientdata(client);
+
+ for (i = 0; i < MAX_PM_IRQ; i++) {
+ spin_lock_irqsave(&chip->pm_lock, irqsave);
+ if (chip->config[i] && !chip->wake_enable[i]) {
+ if (!((chip->config[i] & PM8901_IRQF_MASK_ALL)
+ == PM8901_IRQF_MASK_ALL))
+ pm8901_irq_unmask(irq_get_irq_data(i +
+ chip->pdata.irq_base));
+ }
+ spin_unlock_irqrestore(&chip->pm_lock, irqsave);
+ }
+
+ if (!chip->count_wakeable)
+ enable_irq(chip->dev->irq);
+
+ return 0;
+}
+#else
+#define pm8901_suspend NULL
+#define pm8901_resume NULL
+#endif
+
+static const struct i2c_device_id pm8901_ids[] = {
+ { "pm8901-core", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, pm8901_ids);
+
+static struct i2c_driver pm8901_driver = {
+ .driver.name = "pm8901-core",
+ .id_table = pm8901_ids,
+ .probe = pm8901_probe,
+ .remove = __devexit_p(pm8901_remove),
+ .suspend = pm8901_suspend,
+ .resume = pm8901_resume,
+};
+
+static int __init pm8901_init(void)
+{
+ int rc = i2c_add_driver(&pm8901_driver);
+ pr_notice("%s: i2c_add_driver: rc = %d\n", __func__, rc);
+
+ return rc;
+}
+
+static void __exit pm8901_exit(void)
+{
+ i2c_del_driver(&pm8901_driver);
+}
+
+arch_initcall(pm8901_init);
+module_exit(pm8901_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8901 core driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pmic8901-core");
diff --git a/drivers/mfd/timpani-codec.c b/drivers/mfd/timpani-codec.c
new file mode 100644
index 0000000..364670e
--- /dev/null
+++ b/drivers/mfd/timpani-codec.c
@@ -0,0 +1,3645 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. 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 and
+ * only 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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/msm-adie-codec.h>
+#include <linux/mfd/marimba.h>
+#include <linux/mfd/timpani-audio.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/string.h>
+
+/* Timpani codec driver is activated through Marimba core driver */
+
+#define MAX_MDELAY_US 20000
+
+#define TIMPANI_PATH_MASK(x) (1 << (x))
+
+#define TIMPANI_CODEC_AUXPGA_GAIN_RANGE (0x0F)
+
+#define TIMPANI_RX1_ST_MASK (TIMPANI_CDC_RX1_CTL_SIDETONE_EN1_L_M |\
+ TIMPANI_CDC_RX1_CTL_SIDETONE_EN1_R_M)
+#define TIMPANI_RX1_ST_ENABLE ((1 << TIMPANI_CDC_RX1_CTL_SIDETONE_EN1_L_S) |\
+ (1 << TIMPANI_CDC_RX1_CTL_SIDETONE_EN1_R_S))
+#define TIMPANI_CDC_ST_MIXING_TX1_MASK (TIMPANI_CDC_ST_MIXING_TX1_L_M |\
+ TIMPANI_CDC_ST_MIXING_TX1_R_M)
+#define TIMPANI_CDC_ST_MIXING_TX1_ENABLE ((1 << TIMPANI_CDC_ST_MIXING_TX1_L_S)\
+ | (1 << TIMPANI_CDC_ST_MIXING_TX1_R_S))
+#define TIMPANI_CDC_ST_MIXING_TX2_MASK (TIMPANI_CDC_ST_MIXING_TX2_L_M |\
+ TIMPANI_CDC_ST_MIXING_TX2_R_M)
+#define TIMPANI_CDC_ST_MIXING_TX2_ENABLE ((1 << TIMPANI_CDC_ST_MIXING_TX2_L_S)\
+ | (1 << TIMPANI_CDC_ST_MIXING_TX2_R_S))
+
+enum refcnt {
+ DEC = 0,
+ INC = 1,
+ IGNORE = 2,
+};
+#define TIMPANI_ARRAY_SIZE (TIMPANI_A_CDC_COMP_HALT + 1)
+
+static u8 timpani_shadow[TIMPANI_ARRAY_SIZE];
+
+struct adie_codec_path {
+ struct adie_codec_dev_profile *profile;
+ struct adie_codec_register_image img;
+ u32 hwsetting_idx;
+ u32 stage_idx;
+ u32 curr_stage;
+ u32 reg_owner;
+};
+
+enum /* regaccess blk id */
+{
+ RA_BLOCK_RX1 = 0,
+ RA_BLOCK_RX2,
+ RA_BLOCK_TX1,
+ RA_BLOCK_TX2,
+ RA_BLOCK_LB,
+ RA_BLOCK_SHARED_RX_LB,
+ RA_BLOCK_SHARED_TX,
+ RA_BLOCK_TXFE1,
+ RA_BLOCK_TXFE2,
+ RA_BLOCK_PA_COMMON,
+ RA_BLOCK_PA_EAR,
+ RA_BLOCK_PA_HPH,
+ RA_BLOCK_PA_LINE,
+ RA_BLOCK_PA_AUX,
+ RA_BLOCK_ADC,
+ RA_BLOCK_DMIC,
+ RA_BLOCK_TX_I2S,
+ RA_BLOCK_DRV,
+ RA_BLOCK_TEST,
+ RA_BLOCK_RESERVED,
+ RA_BLOCK_NUM,
+};
+
+enum /* regaccess onwer ID */
+{
+ RA_OWNER_NONE = 0,
+ RA_OWNER_PATH_RX1,
+ RA_OWNER_PATH_RX2,
+ RA_OWNER_PATH_TX1,
+ RA_OWNER_PATH_TX2,
+ RA_OWNER_PATH_LB,
+ RA_OWNER_DRV,
+ RA_OWNER_NUM,
+};
+
+struct reg_acc_blk_cfg {
+ u8 valid_owners[RA_OWNER_NUM];
+};
+
+struct reg_ref_cnt {
+ u8 mask;
+ u8 path_mask;
+};
+
+#define TIMPANI_MAX_FIELDS 5
+
+struct timpani_regaccess {
+ u8 reg_addr;
+ u8 blk_mask[RA_BLOCK_NUM];
+ u8 reg_mask;
+ u8 reg_default;
+ struct reg_ref_cnt fld_ref_cnt[TIMPANI_MAX_FIELDS];
+};
+
+struct timpani_regaccess timpani_regset[] = {
+ {
+ TIMPANI_A_MREF,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xFC, 0x0, 0x3},
+ TIMPANI_MREF_M,
+ TIMPANI_MREF_POR,
+ {
+ { .mask = 0xFC, .path_mask = 0},
+ { .mask = 0x03, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDAC_IDAC_REF_CUR,
+ {0xFC, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDAC_IDAC_REF_CUR_M,
+ TIMPANI_CDAC_IDAC_REF_CUR_POR,
+ {
+ { .mask = 0xFC, .path_mask = 0},
+ { .mask = 0x03, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_TXADC12_REF_CURR,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+ TIMPANI_TXADC12_REF_CURR_M,
+ TIMPANI_TXADC12_REF_CURR_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_TXADC3_EN,
+ { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFE, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+ TIMPANI_TXADC3_EN_M,
+ TIMPANI_TXADC3_EN_POR,
+ {
+ { .mask = 0xFE, .path_mask = 0},
+ { .mask = 0x01, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_TXADC4_EN,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFE, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+ TIMPANI_TXADC4_EN_M,
+ TIMPANI_TXADC4_EN_POR,
+ {
+ { .mask = 0xFE, .path_mask = 0},
+ { .mask = 0x01, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CODEC_TXADC_STATUS_REGISTER_1,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0, 0x30, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF},
+ TIMPANI_CODEC_TXADC_STATUS_REGISTER_1_M,
+ TIMPANI_CODEC_TXADC_STATUS_REGISTER_1_POR,
+ {
+ { .mask = 0xC0, .path_mask = 0},
+ { .mask = 0x30, .path_mask = 0},
+ { .mask = 0x0F, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_TXFE1,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_TXFE1_M,
+ TIMPANI_TXFE1_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_TXFE2,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_TXFE2_M,
+ TIMPANI_TXFE2_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_TXFE12_ATEST,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_TXFE12_ATEST_M,
+ TIMPANI_TXFE12_ATEST_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_TXFE_CLT,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF8, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7},
+ TIMPANI_TXFE_CLT_M,
+ TIMPANI_TXFE_CLT_POR,
+ {
+ { .mask = 0xF8, .path_mask = 0},
+ { .mask = 0x07, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_TXADC1_EN,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFE, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+ TIMPANI_TXADC1_EN_M,
+ TIMPANI_TXADC1_EN_POR,
+ {
+ { .mask = 0xFE, .path_mask = 0},
+ { .mask = 0x01, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_TXADC2_EN,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFE, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+ TIMPANI_TXADC2_EN_M,
+ TIMPANI_TXADC2_EN_POR,
+ {
+ { .mask = 0xFE, .path_mask = 0},
+ { .mask = 0x01, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_TXADC_CTL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_TXADC_CTL_M,
+ TIMPANI_TXADC_CTL_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_TXADC_CTL2,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_TXADC_CTL2_M,
+ TIMPANI_TXADC_CTL2_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_TXADC_CTL3,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xFE, 0x0, 0x0, 0x0, 0x0, 0x1},
+ TIMPANI_TXADC_CTL3_M,
+ TIMPANI_TXADC_CTL3_POR,
+ {
+ { .mask = 0xFE, .path_mask = 0},
+ { .mask = 0x01, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_TXADC_CHOP_CTL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xFC, 0x0, 0x0, 0x0, 0x0, 0x3},
+ TIMPANI_TXADC_CHOP_CTL_M,
+ TIMPANI_TXADC_CHOP_CTL_POR,
+ {
+ { .mask = 0xFC, .path_mask = 0},
+ { .mask = 0x03, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_TXFE3,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xE2, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1D},
+ TIMPANI_TXFE3_M,
+ TIMPANI_TXFE3_POR,
+ {
+ { .mask = 0xE2, .path_mask = 0},
+ { .mask = 0x1D, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_TXFE4,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xE2, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1D},
+ TIMPANI_TXFE4_M,
+ TIMPANI_TXFE4_POR,
+ {
+ { .mask = 0xE2, .path_mask = 0},
+ { .mask = 0x1D, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_TXFE3_ATEST,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_TXFE3_ATEST_M,
+ TIMPANI_TXFE3_ATEST_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_TXFE_DIFF_SE,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xC, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF0},
+ TIMPANI_TXFE_DIFF_SE_M,
+ TIMPANI_TXFE_DIFF_SE_POR,
+ {
+ { .mask = 0x03, .path_mask = 0},
+ { .mask = 0x0C, .path_mask = 0},
+ { .mask = 0xF0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDAC_RX_CLK_CTL,
+ {0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDAC_RX_CLK_CTL_M,
+ TIMPANI_CDAC_RX_CLK_CTL_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDAC_BUFF_CTL,
+ {0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDAC_BUFF_CTL_M,
+ TIMPANI_CDAC_BUFF_CTL_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDAC_REF_CTL1,
+ {0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDAC_REF_CTL1_M,
+ TIMPANI_CDAC_REF_CTL1_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_IDAC_DWA_FIR_CTL,
+ {0xF8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7},
+ TIMPANI_IDAC_DWA_FIR_CTL_M,
+ TIMPANI_IDAC_DWA_FIR_CTL_POR,
+ {
+ { .mask = 0xF8, .path_mask = 0},
+ { .mask = 0x07, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDAC_REF_CTL2,
+ {0x6F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x90},
+ TIMPANI_CDAC_REF_CTL2_M,
+ TIMPANI_CDAC_REF_CTL2_POR,
+ {
+ { .mask = 0x6F, .path_mask = 0},
+ { .mask = 0x90, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDAC_CTL1,
+ {0x7F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80},
+ TIMPANI_CDAC_CTL1_M,
+ TIMPANI_CDAC_CTL1_POR,
+ {
+ { .mask = 0x7F, .path_mask = 0},
+ { .mask = 0x80, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDAC_CTL2,
+ {0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDAC_CTL2_M,
+ TIMPANI_CDAC_CTL2_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_IDAC_L_CTL,
+ {0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_IDAC_L_CTL_M,
+ TIMPANI_IDAC_L_CTL_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_IDAC_R_CTL,
+ {0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_IDAC_R_CTL_M,
+ TIMPANI_IDAC_R_CTL_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_MASTER_BIAS,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1F,
+ 0xE0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_PA_MASTER_BIAS_M,
+ TIMPANI_PA_MASTER_BIAS_POR,
+ {
+ { .mask = 0x1F, .path_mask = 0},
+ { .mask = 0xE0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_CLASSD_BIAS,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_PA_CLASSD_BIAS_M,
+ TIMPANI_PA_CLASSD_BIAS_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_AUXPGA_CUR,
+ {0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_AUXPGA_CUR_M,
+ TIMPANI_AUXPGA_CUR_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_AUXPGA_CM,
+ {0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_AUXPGA_CM_M,
+ TIMPANI_AUXPGA_CM_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_HPH_EARPA_MSTB_EN,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0xFC,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_PA_HPH_EARPA_MSTB_EN_M,
+ TIMPANI_PA_HPH_EARPA_MSTB_EN_POR,
+ {
+ { .mask = 0x01, .path_mask = 0},
+ { .mask = 0x02, .path_mask = 0},
+ { .mask = 0xFC, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_LINE_AUXO_EN,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xF8, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_PA_LINE_AUXO_EN_M,
+ TIMPANI_PA_LINE_AUXO_EN_POR,
+ {
+ { .mask = 0xF8, .path_mask = 0},
+ { .mask = 0x07, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_CLASSD_AUXPGA_EN,
+ {0x0, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+ TIMPANI_PA_CLASSD_AUXPGA_EN_M,
+ TIMPANI_PA_CLASSD_AUXPGA_EN_POR,
+ {
+ { .mask = 0x30, .path_mask = 0},
+ { .mask = 0x0F, .path_mask = 0},
+ { .mask = 0xC0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_LINE_L_GAIN,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xFC, 0x0, 0x3},
+ TIMPANI_PA_LINE_L_GAIN_M,
+ TIMPANI_PA_LINE_L_GAIN_POR,
+ {
+ { .mask = 0xFC, .path_mask = 0},
+ { .mask = 0x03, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_LINE_R_GAIN,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xFC, 0x0, 0x3},
+ TIMPANI_PA_LINE_R_GAIN_M,
+ TIMPANI_PA_LINE_R_GAIN_POR,
+ {
+ { .mask = 0xFC, .path_mask = 0},
+ { .mask = 0x03, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_HPH_L_GAIN,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xFE, 0x0, 0x1},
+ TIMPANI_PA_HPH_L_GAIN_M,
+ TIMPANI_PA_HPH_L_GAIN_POR,
+ {
+ { .mask = 0xFE, .path_mask = 0},
+ { .mask = 0x01, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_HPH_R_GAIN,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xFE, 0x0, 0x1},
+ TIMPANI_PA_HPH_R_GAIN_M,
+ TIMPANI_PA_HPH_R_GAIN_POR,
+ {
+ { .mask = 0xFE, .path_mask = 0},
+ { .mask = 0x01, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_AUXPGA_LR_GAIN,
+ {0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_AUXPGA_LR_GAIN_M,
+ TIMPANI_AUXPGA_LR_GAIN_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_AUXO_EARPA_CONN,
+ {0x21, 0x42, 0x0, 0x0, 0x84, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18},
+ TIMPANI_PA_AUXO_EARPA_CONN_M,
+ TIMPANI_PA_AUXO_EARPA_CONN_POR,
+ {
+ { .mask = 0x21, .path_mask = 0},
+ { .mask = 0x42, .path_mask = 0},
+ { .mask = 0x84, .path_mask = 0},
+ { .mask = 0x18, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_LINE_ST_CONN,
+ {0x24, 0x48, 0x0, 0x0, 0x93, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_PA_LINE_ST_CONN_M,
+ TIMPANI_PA_LINE_ST_CONN_POR,
+ {
+ { .mask = 0x24, .path_mask = 0},
+ { .mask = 0x48, .path_mask = 0},
+ { .mask = 0x93, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_LINE_MONO_CONN,
+ {0x24, 0x48, 0x0, 0x0, 0x93, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_PA_LINE_MONO_CONN_M,
+ TIMPANI_PA_LINE_MONO_CONN_POR,
+ {
+ { .mask = 0x24, .path_mask = 0},
+ { .mask = 0x48, .path_mask = 0},
+ { .mask = 0x93, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_HPH_ST_CONN,
+ {0x24, 0x48, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_PA_HPH_ST_CONN_M,
+ TIMPANI_PA_HPH_ST_CONN_POR,
+ {
+ { .mask = 0x24, .path_mask = 0},
+ { .mask = 0x48, .path_mask = 0},
+ { .mask = 0x90, .path_mask = 0},
+ { .mask = 0x03, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_HPH_MONO_CONN,
+ {0x24, 0x48, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3},
+ TIMPANI_PA_HPH_MONO_CONN_M,
+ TIMPANI_PA_HPH_MONO_CONN_POR,
+ {
+ { .mask = 0x24, .path_mask = 0},
+ { .mask = 0x48, .path_mask = 0},
+ { .mask = 0x90, .path_mask = 0},
+ { .mask = 0x03, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_CLASSD_CONN,
+ {0x80, 0x40, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF},
+ TIMPANI_PA_CLASSD_CONN_M,
+ TIMPANI_PA_CLASSD_CONN_POR,
+ {
+ { .mask = 0x80, .path_mask = 0},
+ { .mask = 0x40, .path_mask = 0},
+ { .mask = 0x20, .path_mask = 0},
+ { .mask = 0x10, .path_mask = 0},
+ { .mask = 0x0F, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_CNP_CTL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xCF,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30},
+ TIMPANI_PA_CNP_CTL_M,
+ TIMPANI_PA_CNP_CTL_POR,
+ {
+ { .mask = 0xCF, .path_mask = 0},
+ { .mask = 0x30, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_CLASSD_L_CTL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3F,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+ TIMPANI_PA_CLASSD_L_CTL_M,
+ TIMPANI_PA_CLASSD_L_CTL_POR,
+ {
+ { .mask = 0x3F, .path_mask = 0},
+ { .mask = 0xC0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_CLASSD_R_CTL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3F,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+ TIMPANI_PA_CLASSD_R_CTL_M,
+ TIMPANI_PA_CLASSD_R_CTL_POR,
+ {
+ { .mask = 0x3F, .path_mask = 0},
+ { .mask = 0xC0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_CLASSD_INT2_CTL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_PA_CLASSD_INT2_CTL_M,
+ TIMPANI_PA_CLASSD_INT2_CTL_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_HPH_L_OCP_CLK_CTL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_PA_HPH_L_OCP_CLK_CTL_M,
+ TIMPANI_PA_HPH_L_OCP_CLK_CTL_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_CLASSD_L_SW_CTL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF7,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8},
+ TIMPANI_PA_CLASSD_L_SW_CTL_M,
+ TIMPANI_PA_CLASSD_L_SW_CTL_POR,
+ {
+ { .mask = 0xF7, .path_mask = 0},
+ { .mask = 0x08, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_CLASSD_L_OCP1,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_PA_CLASSD_L_OCP1_M,
+ TIMPANI_PA_CLASSD_L_OCP1_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_CLASSD_L_OCP2,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_PA_CLASSD_L_OCP2_M,
+ TIMPANI_PA_CLASSD_L_OCP2_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_HPH_R_OCP_CLK_CTL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_PA_HPH_R_OCP_CLK_CTL_M,
+ TIMPANI_PA_HPH_R_OCP_CLK_CTL_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_CLASSD_R_SW_CTL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF7,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8},
+ TIMPANI_PA_CLASSD_R_SW_CTL_M,
+ TIMPANI_PA_CLASSD_R_SW_CTL_POR,
+ {
+ { .mask = 0xF7, .path_mask = 0},
+ { .mask = 0x08, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_CLASSD_R_OCP1,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_PA_CLASSD_R_OCP1_M,
+ TIMPANI_PA_CLASSD_R_OCP1_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_CLASSD_R_OCP2,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_PA_CLASSD_R_OCP2_M,
+ TIMPANI_PA_CLASSD_R_OCP2_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_HPH_CTL1,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+ TIMPANI_PA_HPH_CTL1_M,
+ TIMPANI_PA_HPH_CTL1_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_HPH_CTL2,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFE,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+ TIMPANI_PA_HPH_CTL2_M,
+ TIMPANI_PA_HPH_CTL2_POR,
+ {
+ { .mask = 0xFE, .path_mask = 0},
+ { .mask = 0x01, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_LINE_AUXO_CTL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0xC3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x3C, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_PA_LINE_AUXO_CTL_M,
+ TIMPANI_PA_LINE_AUXO_CTL_POR,
+ {
+ { .mask = 0xC3, .path_mask = 0},
+ { .mask = 0x3C, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_AUXO_EARPA_CTL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0,
+ 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+ TIMPANI_PA_AUXO_EARPA_CTL_M,
+ TIMPANI_PA_AUXO_EARPA_CTL_POR,
+ {
+ { .mask = 0x07, .path_mask = 0},
+ { .mask = 0x38, .path_mask = 0},
+ { .mask = 0xC0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_EARO_CTL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_PA_EARO_CTL_M,
+ TIMPANI_PA_EARO_CTL_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_MASTER_BIAS_CUR,
+ {0x0, 0x0, 0x0, 0x0, 0x60, 0x0, 0x0, 0x0, 0x0, 0x80, 0x0, 0x18,
+ 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+ TIMPANI_PA_MASTER_BIAS_CUR_M,
+ TIMPANI_PA_MASTER_BIAS_CUR_POR,
+ {
+ { .mask = 0x60, .path_mask = 0},
+ { .mask = 0x80, .path_mask = 0},
+ { .mask = 0x18, .path_mask = 0},
+ { .mask = 0x06, .path_mask = 0},
+ { .mask = 0x01, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_CLASSD_SC_STATUS,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xCC,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x33},
+ TIMPANI_PA_CLASSD_SC_STATUS_M,
+ TIMPANI_PA_CLASSD_SC_STATUS_POR,
+ {
+ { .mask = 0xCC, .path_mask = 0},
+ { .mask = 0x33, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_PA_HPH_SC_STATUS,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x88,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x77},
+ TIMPANI_PA_HPH_SC_STATUS_M,
+ TIMPANI_PA_HPH_SC_STATUS_POR,
+ {
+ { .mask = 0x88, .path_mask = 0},
+ { .mask = 0x77, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_ATEST_EN,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x7F},
+ TIMPANI_ATEST_EN_M,
+ TIMPANI_ATEST_EN_POR,
+ {
+ { .mask = 0x80, .path_mask = 0},
+ { .mask = 0x7F, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_ATEST_TSHKADC,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF0},
+ TIMPANI_ATEST_TSHKADC_M,
+ TIMPANI_ATEST_TSHKADC_POR,
+ {
+ { .mask = 0x0F, .path_mask = 0},
+ { .mask = 0xF0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_ATEST_TXADC13,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7F, 0x80},
+ TIMPANI_ATEST_TXADC13_M,
+ TIMPANI_ATEST_TXADC13_POR,
+ {
+ { .mask = 0x7F, .path_mask = 0},
+ { .mask = 0x80, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_ATEST_TXADC24,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7F, 0x80},
+ TIMPANI_ATEST_TXADC24_M,
+ TIMPANI_ATEST_TXADC24_POR,
+ {
+ { .mask = 0x7F, .path_mask = 0},
+ { .mask = 0x80, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_ATEST_AUXPGA,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF8, 0x7},
+ TIMPANI_ATEST_AUXPGA_M,
+ TIMPANI_ATEST_AUXPGA_POR,
+ {
+ { .mask = 0xF8, .path_mask = 0},
+ { .mask = 0x07, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_ATEST_CDAC,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0},
+ TIMPANI_ATEST_CDAC_M,
+ TIMPANI_ATEST_CDAC_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_ATEST_IDAC,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0},
+ TIMPANI_ATEST_IDAC_M,
+ TIMPANI_ATEST_IDAC_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_ATEST_PA1,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0},
+ TIMPANI_ATEST_PA1_M,
+ TIMPANI_ATEST_PA1_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_ATEST_CLASSD,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0},
+ TIMPANI_ATEST_CLASSD_M,
+ TIMPANI_ATEST_CLASSD_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_ATEST_LINEO_AUXO,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0},
+ TIMPANI_ATEST_LINEO_AUXO_M,
+ TIMPANI_ATEST_LINEO_AUXO_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_RESET_CTL,
+ {0x2, 0x8, 0x5, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+ TIMPANI_CDC_RESET_CTL_M,
+ TIMPANI_CDC_RESET_CTL_POR,
+ {
+ { .mask = 0x02, .path_mask = 0},
+ { .mask = 0x08, .path_mask = 0},
+ { .mask = 0x05, .path_mask = 0},
+ { .mask = 0x30, .path_mask = 0},
+ { .mask = 0xC0, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_RX1_CTL,
+ {0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_RX1_CTL_M,
+ TIMPANI_CDC_RX1_CTL_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_TX_I2S_CTL,
+ {0x0, 0x0, 0x10, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0x0, 0xC0},
+ TIMPANI_CDC_TX_I2S_CTL_M,
+ TIMPANI_CDC_TX_I2S_CTL_POR,
+ {
+ { .mask = 0x10, .path_mask = 0},
+ { .mask = 0x20, .path_mask = 0},
+ { .mask = 0x0F, .path_mask = 0},
+ { .mask = 0xC0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_CH_CTL,
+ {0x3, 0x30, 0xC, 0xC0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_CH_CTL_M,
+ TIMPANI_CDC_CH_CTL_POR,
+ {
+ { .mask = 0x03, .path_mask = 0},
+ { .mask = 0x30, .path_mask = 0},
+ { .mask = 0x0C, .path_mask = 0},
+ { .mask = 0xC0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_RX1LG,
+ {0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_RX1LG_M,
+ TIMPANI_CDC_RX1LG_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_RX1RG,
+ {0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_RX1RG_M,
+ TIMPANI_CDC_RX1RG_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_TX1LG,
+ {0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_TX1LG_M,
+ TIMPANI_CDC_TX1LG_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_TX1RG,
+ {0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_TX1RG_M,
+ TIMPANI_CDC_TX1RG_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_RX_PGA_TIMER,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_RX_PGA_TIMER_M,
+ TIMPANI_CDC_RX_PGA_TIMER_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_TX_PGA_TIMER,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_TX_PGA_TIMER_M,
+ TIMPANI_CDC_TX_PGA_TIMER_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_GCTL1,
+ {0xF, 0x0, 0xF0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_GCTL1_M,
+ TIMPANI_CDC_GCTL1_POR,
+ {
+ { .mask = 0x0F, .path_mask = 0},
+ { .mask = 0xF0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_TX1L_STG,
+ {0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_TX1L_STG_M,
+ TIMPANI_CDC_TX1L_STG_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ST_CTL,
+ {0x0, 0xF, 0x0, 0xF0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_ST_CTL_M,
+ TIMPANI_CDC_ST_CTL_POR,
+ {
+ { .mask = 0x0F, .path_mask = 0},
+ { .mask = 0xF0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_RX1L_DCOFFSET,
+ {0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_RX1L_DCOFFSET_M,
+ TIMPANI_CDC_RX1L_DCOFFSET_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_RX1R_DCOFFSET,
+ {0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_RX1R_DCOFFSET_M,
+ TIMPANI_CDC_RX1R_DCOFFSET_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_BYPASS_CTL1,
+ {0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF0},
+ TIMPANI_CDC_BYPASS_CTL1_M,
+ TIMPANI_CDC_BYPASS_CTL1_POR,
+ {
+ { .mask = 0x0F, .path_mask = 0},
+ { .mask = 0xF0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_PDM_CONFIG,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF0},
+ TIMPANI_CDC_PDM_CONFIG_M,
+ TIMPANI_CDC_PDM_CONFIG_POR,
+ {
+ { .mask = 0x0F, .path_mask = 0},
+ { .mask = 0xF0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_TESTMODE1,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3F, 0xC0},
+ TIMPANI_CDC_TESTMODE1_M,
+ TIMPANI_CDC_TESTMODE1_POR,
+ {
+ { .mask = 0x3F, .path_mask = 0},
+ { .mask = 0xC0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_DMIC_CLK_CTL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x3F, 0x0, 0x0, 0x0, 0xC0},
+ TIMPANI_CDC_DMIC_CLK_CTL_M,
+ TIMPANI_CDC_DMIC_CLK_CTL_POR,
+ {
+ { .mask = 0x3F, .path_mask = 0},
+ { .mask = 0xC0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ADC12_CLK_CTL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_ADC12_CLK_CTL_M,
+ TIMPANI_CDC_ADC12_CLK_CTL_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_TX1_CTL,
+ {0x0, 0x0, 0x3F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+ TIMPANI_CDC_TX1_CTL_M,
+ TIMPANI_CDC_TX1_CTL_POR,
+ {
+ { .mask = 0x3F, .path_mask = 0},
+ { .mask = 0xC0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ADC34_CLK_CTL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_ADC34_CLK_CTL_M,
+ TIMPANI_CDC_ADC34_CLK_CTL_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_TX2_CTL,
+ {0x0, 0x0, 0x0, 0x3F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+ TIMPANI_CDC_TX2_CTL_M,
+ TIMPANI_CDC_TX2_CTL_POR,
+ {
+ { .mask = 0x3F, .path_mask = 0},
+ { .mask = 0xC0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_RX1_CLK_CTL,
+ {0x1F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xE0},
+ TIMPANI_CDC_RX1_CLK_CTL_M,
+ TIMPANI_CDC_RX1_CLK_CTL_POR,
+ {
+ { .mask = 0x1F, .path_mask = 0},
+ { .mask = 0xE0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_RX2_CLK_CTL,
+ {0x1F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xE0},
+ TIMPANI_CDC_RX2_CLK_CTL_M,
+ TIMPANI_CDC_RX2_CLK_CTL_POR,
+ {
+ { .mask = 0x1F, .path_mask = 0},
+ { .mask = 0xE0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_DEC_ADC_SEL,
+ {0x0, 0x0, 0xF, 0xF0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_DEC_ADC_SEL_M,
+ TIMPANI_CDC_DEC_ADC_SEL_POR,
+ {
+ { .mask = 0x0F, .path_mask = 0},
+ { .mask = 0xF0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC_INPUT_MUX,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x3F, 0x0, 0xC0},
+ TIMPANI_CDC_ANC_INPUT_MUX_M,
+ TIMPANI_CDC_ANC_INPUT_MUX_POR,
+ {
+ { .mask = 0x3F, .path_mask = 0},
+ { .mask = 0xC0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC_RX_CLK_NS_SEL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0xFE},
+ TIMPANI_CDC_ANC_RX_CLK_NS_SEL_M,
+ TIMPANI_CDC_ANC_RX_CLK_NS_SEL_POR,
+ {
+ { .mask = 0x01, .path_mask = 0},
+ { .mask = 0xFE, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC_FB_TUNE_SEL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+ TIMPANI_CDC_ANC_FB_TUNE_SEL_M,
+ TIMPANI_CDC_ANC_FB_TUNE_SEL_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CLK_DIV_SYNC_CTL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0xFC},
+ TIMPANI_CLK_DIV_SYNC_CTL_M,
+ TIMPANI_CLK_DIV_SYNC_CTL_POR,
+ {
+ { .mask = 0x03, .path_mask = 0},
+ { .mask = 0xFC, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ADC_CLK_EN,
+ {0x0, 0x0, 0x3, 0xC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF0},
+ TIMPANI_CDC_ADC_CLK_EN_M,
+ TIMPANI_CDC_ADC_CLK_EN_POR,
+ {
+ { .mask = 0x03, .path_mask = 0},
+ { .mask = 0x0C, .path_mask = 0},
+ { .mask = 0xF0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ST_MIXING,
+ {0x0, 0x0, 0x3, 0xC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF0},
+ TIMPANI_CDC_ST_MIXING_M,
+ TIMPANI_CDC_ST_MIXING_POR,
+ {
+ { .mask = 0x03, .path_mask = 0},
+ { .mask = 0x0C, .path_mask = 0},
+ { .mask = 0xF0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_RX2_CTL,
+ {0x0, 0x7F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80},
+ TIMPANI_CDC_RX2_CTL_M,
+ TIMPANI_CDC_RX2_CTL_POR,
+ {
+ { .mask = 0x7F, .path_mask = 0},
+ { .mask = 0x80, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ARB_CLK_EN,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+ TIMPANI_CDC_ARB_CLK_EN_M,
+ TIMPANI_CDC_ARB_CLK_EN_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_I2S_CTL2,
+ {0x2, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0xC0},
+ TIMPANI_CDC_I2S_CTL2_M,
+ TIMPANI_CDC_I2S_CTL2_POR,
+ {
+ { .mask = 0x02, .path_mask = 0},
+ { .mask = 0x04, .path_mask = 0},
+ { .mask = 0x39, .path_mask = 0},
+ { .mask = 0xC0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_RX2LG,
+ {0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_RX2LG_M,
+ TIMPANI_CDC_RX2LG_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_RX2RG,
+ {0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_RX2RG_M,
+ TIMPANI_CDC_RX2RG_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_TX2LG,
+ {0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_TX2LG_M,
+ TIMPANI_CDC_TX2LG_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_TX2RG,
+ {0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_TX2RG_M,
+ TIMPANI_CDC_TX2RG_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_DMIC_MUX,
+ {0x0, 0x0, 0xF, 0xF0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_DMIC_MUX_M,
+ TIMPANI_CDC_DMIC_MUX_POR,
+ {
+ { .mask = 0x0F, .path_mask = 0},
+ { .mask = 0xF0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ARB_CLK_CTL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0xFC},
+ TIMPANI_CDC_ARB_CLK_CTL_M,
+ TIMPANI_CDC_ARB_CLK_CTL_POR,
+ {
+ { .mask = 0x03, .path_mask = 0},
+ { .mask = 0xFC, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_GCTL2,
+ {0x0, 0xF, 0x0, 0xF0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_GCTL2_M,
+ TIMPANI_CDC_GCTL2_POR,
+ {
+ { .mask = 0x0F, .path_mask = 0},
+ { .mask = 0xF0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_BYPASS_CTL2,
+ {0x0, 0x0, 0x3F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+ TIMPANI_CDC_BYPASS_CTL2_M,
+ TIMPANI_CDC_BYPASS_CTL2_POR,
+ {
+ { .mask = 0x3F, .path_mask = 0},
+ { .mask = 0xC0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_BYPASS_CTL3,
+ {0x0, 0x0, 0x0, 0x3F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+ TIMPANI_CDC_BYPASS_CTL3_M,
+ TIMPANI_CDC_BYPASS_CTL3_POR,
+ {
+ { .mask = 0x3F, .path_mask = 0},
+ { .mask = 0xC0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_BYPASS_CTL4,
+ {0x0, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF0},
+ TIMPANI_CDC_BYPASS_CTL4_M,
+ TIMPANI_CDC_BYPASS_CTL4_POR,
+ {
+ { .mask = 0x0F, .path_mask = 0},
+ { .mask = 0xF0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_RX2L_DCOFFSET,
+ {0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_RX2L_DCOFFSET_M,
+ TIMPANI_CDC_RX2L_DCOFFSET_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_RX2R_DCOFFSET,
+ {0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_RX2R_DCOFFSET_M,
+ TIMPANI_CDC_RX2R_DCOFFSET_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_RX_MIX_CTL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0xFC},
+ TIMPANI_CDC_RX_MIX_CTL_M,
+ TIMPANI_CDC_RX_MIX_CTL_POR,
+ {
+ { .mask = 0x03, .path_mask = 0},
+ { .mask = 0xFC, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_SPARE_CTL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xFE},
+ TIMPANI_CDC_SPARE_CTL_M,
+ TIMPANI_CDC_SPARE_CTL_POR,
+ {
+ { .mask = 0x01, .path_mask = 0},
+ { .mask = 0xFE, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_TESTMODE2,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1F, 0xE0},
+ TIMPANI_CDC_TESTMODE2_M,
+ TIMPANI_CDC_TESTMODE2_POR,
+ {
+ { .mask = 0x1F, .path_mask = 0},
+ { .mask = 0xE0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_PDM_OE,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0},
+ TIMPANI_CDC_PDM_OE_M,
+ TIMPANI_CDC_PDM_OE_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_TX1R_STG,
+ {0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_TX1R_STG_M,
+ TIMPANI_CDC_TX1R_STG_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_TX2L_STG,
+ {0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_TX2L_STG_M,
+ TIMPANI_CDC_TX2L_STG_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_TX2R_STG,
+ {0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ TIMPANI_CDC_TX2R_STG_M,
+ TIMPANI_CDC_TX2R_STG_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ARB_BYPASS_CTL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+ TIMPANI_CDC_ARB_BYPASS_CTL_M,
+ TIMPANI_CDC_ARB_BYPASS_CTL_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC1_CTL1,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x1F, 0x0, 0xE0},
+ TIMPANI_CDC_ANC1_CTL1_M,
+ TIMPANI_CDC_ANC1_CTL1_POR,
+ {
+ { .mask = 0x1F, .path_mask = 0},
+ { .mask = 0xE0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC1_CTL2,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x3F, 0x0, 0xC0},
+ TIMPANI_CDC_ANC1_CTL2_M,
+ TIMPANI_CDC_ANC1_CTL2_POR,
+ {
+ { .mask = 0x3F, .path_mask = 0},
+ { .mask = 0xC0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC1_FF_FB_SHIFT,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+ TIMPANI_CDC_ANC1_FF_FB_SHIFT_M,
+ TIMPANI_CDC_ANC1_FF_FB_SHIFT_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC1_RX_NS,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0xF8},
+ TIMPANI_CDC_ANC1_RX_NS_M,
+ TIMPANI_CDC_ANC1_RX_NS_POR,
+ {
+ { .mask = 0x07, .path_mask = 0},
+ { .mask = 0xF8, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC1_SPARE,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+ TIMPANI_CDC_ANC1_SPARE_M,
+ TIMPANI_CDC_ANC1_SPARE_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC1_IIR_COEFF_PTR,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x1F, 0x0, 0xE0},
+ TIMPANI_CDC_ANC1_IIR_COEFF_PTR_M,
+ TIMPANI_CDC_ANC1_IIR_COEFF_PTR_POR,
+ {
+ { .mask = 0x1F, .path_mask = 0},
+ { .mask = 0xE0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC1_IIR_COEFF_MSB,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0xFE},
+ TIMPANI_CDC_ANC1_IIR_COEFF_MSB_M,
+ TIMPANI_CDC_ANC1_IIR_COEFF_MSB_POR,
+ {
+ { .mask = 0x01, .path_mask = 0},
+ { .mask = 0xFE, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC1_IIR_COEFF_LSB,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+ TIMPANI_CDC_ANC1_IIR_COEFF_LSB_M,
+ TIMPANI_CDC_ANC1_IIR_COEFF_LSB_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC1_IIR_COEFF_CTL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0xFC},
+ TIMPANI_CDC_ANC1_IIR_COEFF_CTL_M,
+ TIMPANI_CDC_ANC1_IIR_COEFF_CTL_POR,
+ {
+ { .mask = 0x03, .path_mask = 0},
+ { .mask = 0xFC, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC1_LPF_COEFF_PTR,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+ TIMPANI_CDC_ANC1_LPF_COEFF_PTR_M,
+ TIMPANI_CDC_ANC1_LPF_COEFF_PTR_POR,
+ {
+ { .mask = 0x0F, .path_mask = 0},
+ { .mask = 0xF0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC1_LPF_COEFF_MSB,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+ TIMPANI_CDC_ANC1_LPF_COEFF_MSB_M,
+ TIMPANI_CDC_ANC1_LPF_COEFF_MSB_POR,
+ {
+ { .mask = 0x0F, .path_mask = 0},
+ { .mask = 0xF0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC1_LPF_COEFF_LSB,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+ TIMPANI_CDC_ANC1_LPF_COEFF_LSB_M,
+ TIMPANI_CDC_ANC1_LPF_COEFF_LSB_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC1_SCALE_PTR,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+ TIMPANI_CDC_ANC1_SCALE_PTR_M,
+ TIMPANI_CDC_ANC1_SCALE_PTR_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC1_SCALE,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+ TIMPANI_CDC_ANC1_SCALE_M,
+ TIMPANI_CDC_ANC1_SCALE_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC1_DEBUG,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+ TIMPANI_CDC_ANC1_DEBUG_M,
+ TIMPANI_CDC_ANC1_DEBUG_POR,
+ {
+ { .mask = 0x0F, .path_mask = 0},
+ { .mask = 0xF0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC2_CTL1,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x1F, 0x0, 0xE0},
+ TIMPANI_CDC_ANC2_CTL1_M,
+ TIMPANI_CDC_ANC2_CTL1_POR,
+ {
+ { .mask = 0x1F, .path_mask = 0},
+ { .mask = 0xE0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC2_CTL2,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x3F, 0x0, 0xC0},
+ TIMPANI_CDC_ANC2_CTL2_M,
+ TIMPANI_CDC_ANC2_CTL2_POR,
+ {
+ { .mask = 0x3F, .path_mask = 0},
+ { .mask = 0xC0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC2_FF_FB_SHIFT,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+ TIMPANI_CDC_ANC2_FF_FB_SHIFT_M,
+ TIMPANI_CDC_ANC2_FF_FB_SHIFT_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC2_RX_NS,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0xF8},
+ TIMPANI_CDC_ANC2_RX_NS_M,
+ TIMPANI_CDC_ANC2_RX_NS_POR,
+ {
+ { .mask = 0x07, .path_mask = 0},
+ { .mask = 0xF8, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC2_SPARE,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+ TIMPANI_CDC_ANC2_SPARE_M,
+ TIMPANI_CDC_ANC2_SPARE_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC2_IIR_COEFF_PTR,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+ TIMPANI_CDC_ANC2_IIR_COEFF_PTR_M,
+ TIMPANI_CDC_ANC2_IIR_COEFF_PTR_POR,
+ {
+ { .mask = 0x0F, .path_mask = 0},
+ { .mask = 0xF0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC2_IIR_COEFF_MSB,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0xFE},
+ TIMPANI_CDC_ANC2_IIR_COEFF_MSB_M,
+ TIMPANI_CDC_ANC2_IIR_COEFF_MSB_POR,
+ {
+ { .mask = 0x01, .path_mask = 0},
+ { .mask = 0xFE, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC2_IIR_COEFF_LSB,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+ TIMPANI_CDC_ANC2_IIR_COEFF_LSB_M,
+ TIMPANI_CDC_ANC2_IIR_COEFF_LSB_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC2_IIR_COEFF_CTL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0xFC},
+ TIMPANI_CDC_ANC2_IIR_COEFF_CTL_M,
+ TIMPANI_CDC_ANC2_IIR_COEFF_CTL_POR,
+ {
+ { .mask = 0x03, .path_mask = 0},
+ { .mask = 0xFC, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC2_LPF_COEFF_PTR,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+ TIMPANI_CDC_ANC2_LPF_COEFF_PTR_M,
+ TIMPANI_CDC_ANC2_LPF_COEFF_PTR_POR,
+ {
+ { .mask = 0x0F, .path_mask = 0},
+ { .mask = 0xF0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC2_LPF_COEFF_MSB,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+ TIMPANI_CDC_ANC2_LPF_COEFF_MSB_M,
+ TIMPANI_CDC_ANC2_LPF_COEFF_MSB_POR,
+ {
+ { .mask = 0x0F, .path_mask = 0},
+ { .mask = 0xF0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC2_LPF_COEFF_LSB,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+ TIMPANI_CDC_ANC2_LPF_COEFF_LSB_M,
+ TIMPANI_CDC_ANC2_LPF_COEFF_LSB_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC2_SCALE_PTR,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+ TIMPANI_CDC_ANC2_SCALE_PTR_M,
+ TIMPANI_CDC_ANC2_SCALE_PTR_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC2_SCALE,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+ TIMPANI_CDC_ANC2_SCALE_M,
+ TIMPANI_CDC_ANC2_SCALE_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_ANC2_DEBUG,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+ TIMPANI_CDC_ANC2_DEBUG_M,
+ TIMPANI_CDC_ANC2_DEBUG_POR,
+ {
+ { .mask = 0x0F, .path_mask = 0},
+ { .mask = 0xF0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_LINE_L_AVOL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xFC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3},
+ TIMPANI_CDC_LINE_L_AVOL_M,
+ TIMPANI_CDC_LINE_L_AVOL_POR,
+ {
+ { .mask = 0xFC, .path_mask = 0},
+ { .mask = 0x03, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_LINE_R_AVOL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xFC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3},
+ TIMPANI_CDC_LINE_R_AVOL_M,
+ TIMPANI_CDC_LINE_R_AVOL_POR,
+ {
+ { .mask = 0xFC, .path_mask = 0},
+ { .mask = 0x03, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_HPH_L_AVOL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFE,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+ TIMPANI_CDC_HPH_L_AVOL_M,
+ TIMPANI_CDC_HPH_L_AVOL_POR,
+ {
+ { .mask = 0xFE, .path_mask = 0},
+ { .mask = 0x01, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_HPH_R_AVOL,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFE,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+ TIMPANI_CDC_HPH_R_AVOL_M,
+ TIMPANI_CDC_HPH_R_AVOL_POR,
+ {
+ { .mask = 0xFE, .path_mask = 0},
+ { .mask = 0x01, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_COMP_CTL1,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x3F, 0x0, 0xC0},
+ TIMPANI_CDC_COMP_CTL1_M,
+ TIMPANI_CDC_COMP_CTL1_POR,
+ {
+ { .mask = 0x3F, .path_mask = 0},
+ { .mask = 0xC0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_COMP_CTL2,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+ TIMPANI_CDC_COMP_CTL2_M,
+ TIMPANI_CDC_COMP_CTL2_POR,
+ {
+ { .mask = 0x0F, .path_mask = 0},
+ { .mask = 0xF0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_COMP_PEAK_METER,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+ TIMPANI_CDC_COMP_PEAK_METER_M,
+ TIMPANI_CDC_COMP_PEAK_METER_POR,
+ {
+ { .mask = 0x0F, .path_mask = 0},
+ { .mask = 0xF0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_COMP_LEVEL_METER_CTL1,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+ TIMPANI_CDC_COMP_LEVEL_METER_CTL1_M,
+ TIMPANI_CDC_COMP_LEVEL_METER_CTL1_POR,
+ {
+ { .mask = 0x0F, .path_mask = 0},
+ { .mask = 0xF0, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_COMP_LEVEL_METER_CTL2,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+ TIMPANI_CDC_COMP_LEVEL_METER_CTL2_M,
+ TIMPANI_CDC_COMP_LEVEL_METER_CTL2_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_COMP_ZONE_SELECT,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x7F, 0x0, 0x80},
+ TIMPANI_CDC_COMP_ZONE_SELECT_M,
+ TIMPANI_CDC_COMP_ZONE_SELECT_POR,
+ {
+ { .mask = 0x7F, .path_mask = 0},
+ { .mask = 0x80, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_COMP_ZC_MSB,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+ TIMPANI_CDC_COMP_ZC_MSB_M,
+ TIMPANI_CDC_COMP_ZC_MSB_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_COMP_ZC_LSB,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+ TIMPANI_CDC_COMP_ZC_LSB_M,
+ TIMPANI_CDC_COMP_ZC_LSB_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_COMP_SHUT_DOWN,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+ TIMPANI_CDC_COMP_SHUT_DOWN_M,
+ TIMPANI_CDC_COMP_SHUT_DOWN_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_COMP_SHUT_DOWN_STATUS,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+ TIMPANI_CDC_COMP_SHUT_DOWN_STATUS_M,
+ TIMPANI_CDC_COMP_SHUT_DOWN_STATUS_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ },
+ {
+ TIMPANI_A_CDC_COMP_HALT,
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+ TIMPANI_CDC_COMP_HALT_M,
+ TIMPANI_CDC_COMP_HALT_POR,
+ {
+ { .mask = 0xFF, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ { .mask = 0x00, .path_mask = 0},
+ }
+ }
+};
+
+struct reg_acc_blk_cfg timpani_blkcfg[RA_BLOCK_NUM] = {
+ {
+ .valid_owners = {RA_OWNER_NONE, RA_OWNER_PATH_RX1,
+ 0, 0, 0, 0, RA_OWNER_DRV}
+ },
+ /* RA_BLOCK_RX1 */
+ {
+ .valid_owners = {RA_OWNER_NONE, 0, RA_OWNER_PATH_RX2,
+ 0, 0, 0, RA_OWNER_DRV}
+ },
+ /* RA_BLOCK_RX2 */
+ {
+ .valid_owners = {RA_OWNER_NONE, 0, 0, RA_OWNER_PATH_TX1,
+ 0, 0, RA_OWNER_DRV}
+ },
+ /* RA_BLOCK_TX1 */
+ {
+ .valid_owners = {RA_OWNER_NONE, 0, 0, 0, RA_OWNER_PATH_TX2,
+ 0, RA_OWNER_DRV}
+ },
+ /* RA_BLOCK_TX2 */
+ {
+ .valid_owners = {RA_OWNER_NONE, 0, 0, 0, 0,
+ RA_OWNER_PATH_LB, RA_OWNER_DRV}
+ },
+ /* RA_BLOCK_LB */
+ {
+ .valid_owners = {RA_OWNER_NONE, RA_OWNER_PATH_RX1,
+ RA_OWNER_PATH_RX2, 0, 0, RA_OWNER_PATH_LB, RA_OWNER_DRV}
+ },
+ /* RA_BLOCK_SHARED_RX_LB */
+ {
+ .valid_owners = {RA_OWNER_NONE, 0, 0, RA_OWNER_PATH_TX1,
+ RA_OWNER_PATH_TX2, 0, RA_OWNER_DRV}
+ },
+ /* RA_BLOCK_SHARED_TX */
+ {
+ .valid_owners = {RA_OWNER_NONE, 0, 0, RA_OWNER_PATH_TX1,
+ RA_OWNER_PATH_TX2, 0, RA_OWNER_DRV}
+ },
+ /* RA_BLOCK_TXFE1 */
+ {
+ .valid_owners = {RA_OWNER_NONE, 0, 0, RA_OWNER_PATH_TX1,
+ RA_OWNER_PATH_TX2, 0, RA_OWNER_DRV}
+ },
+ /* RA_BLOCK_TXFE2 */
+ {
+ .valid_owners = {RA_OWNER_NONE, RA_OWNER_PATH_RX1,
+ RA_OWNER_PATH_RX2, 0, 0, RA_OWNER_PATH_LB, RA_OWNER_DRV}
+ },
+ /* RA_BLOCK_PA_COMMON */
+ {
+ .valid_owners = {RA_OWNER_NONE, RA_OWNER_PATH_RX1,
+ RA_OWNER_PATH_RX2, 0, 0, RA_OWNER_PATH_LB, RA_OWNER_DRV}
+ },
+ /* RA_BLOCK_PA_EAR */
+ {
+ .valid_owners = {RA_OWNER_NONE, RA_OWNER_PATH_RX1,
+ RA_OWNER_PATH_RX2, 0, 0, RA_OWNER_PATH_LB, RA_OWNER_DRV}
+ },
+ /* RA_BLOCK_PA_HPH */
+ {
+ .valid_owners = {RA_OWNER_NONE, RA_OWNER_PATH_RX1,
+ RA_OWNER_PATH_RX2, 0, 0, RA_OWNER_PATH_LB, RA_OWNER_DRV}
+ },
+ /* RA_BLOCK_PA_LINE */
+ {
+ .valid_owners = {RA_OWNER_NONE, RA_OWNER_PATH_RX1,
+ RA_OWNER_PATH_RX2, 0, 0, RA_OWNER_PATH_LB, RA_OWNER_DRV}
+ },
+ /* RA_BLOCK_PA_AUX */
+ {
+ .valid_owners = {RA_OWNER_NONE, 0, 0, RA_OWNER_PATH_TX1,
+ RA_OWNER_PATH_TX2, 0, RA_OWNER_DRV}
+ },
+ /* RA_BLOCK_ADC */
+ {
+ .valid_owners = {RA_OWNER_NONE, 0, 0, RA_OWNER_PATH_TX1,
+ RA_OWNER_PATH_TX2, 0, RA_OWNER_DRV}
+ },
+ /* RA_BLOCK_DMIC */
+ {
+ .valid_owners = {RA_OWNER_NONE, 0, 0, RA_OWNER_PATH_TX1,
+ RA_OWNER_PATH_TX2, 0, RA_OWNER_DRV}
+ },
+ /* RA_BLOCK_TX_I2S */
+ {
+ .valid_owners = {RA_OWNER_NONE, 0, 0, 0, 0, 0, RA_OWNER_DRV}
+ },
+ /*RA_BLOCK_DRV */
+ {
+ .valid_owners = {RA_OWNER_NONE, 0, 0, 0, 0, 0, RA_OWNER_DRV}
+ },
+ /* RA_BLOCK_TEST */
+ {
+ .valid_owners = {RA_OWNER_NONE, 0, 0, 0, 0, 0, RA_OWNER_DRV}
+ },
+ /* RA_BLOCK_RESERVED */
+};
+
+struct adie_codec_state {
+ struct adie_codec_path path[ADIE_CODEC_MAX];
+ u32 ref_cnt;
+ struct marimba *pdrv_ptr;
+ struct marimba_codec_platform_data *codec_pdata;
+ struct mutex lock;
+};
+
+static struct adie_codec_state adie_codec;
+
+/* A cacheable register is one that if the register's current value is being
+ * written to it again, then it is permissable to skip that register write
+ * because it does not actually change the value of the hardware register.
+ *
+ * Some registers are uncacheable, meaning that even they are being written
+ * again with their current value, the write has another purpose and must go
+ * through.
+ *
+ * Knowing the codec's uncacheable registers allows the driver to avoid
+ * unnecessary codec register writes while making sure important register writes
+ * are not skipped.
+ */
+
+static bool timpani_register_is_cacheable(u8 reg)
+{
+ switch (reg) {
+ case TIMPANI_A_PA_LINE_L_GAIN:
+ case TIMPANI_A_PA_LINE_R_GAIN:
+ case TIMPANI_A_PA_HPH_L_GAIN:
+ case TIMPANI_A_PA_HPH_R_GAIN:
+ case TIMPANI_A_CDC_GCTL1:
+ case TIMPANI_A_CDC_ST_CTL:
+ case TIMPANI_A_CDC_GCTL2:
+ case TIMPANI_A_CDC_ARB_BYPASS_CTL:
+ case TIMPANI_A_CDC_CH_CTL:
+ case TIMPANI_A_CDC_ANC1_IIR_COEFF_PTR:
+ case TIMPANI_A_CDC_ANC1_IIR_COEFF_MSB:
+ case TIMPANI_A_CDC_ANC1_IIR_COEFF_LSB:
+ case TIMPANI_A_CDC_ANC1_LPF_COEFF_PTR:
+ case TIMPANI_A_CDC_ANC1_LPF_COEFF_MSB:
+ case TIMPANI_A_CDC_ANC1_LPF_COEFF_LSB:
+ case TIMPANI_A_CDC_ANC1_SCALE_PTR:
+ case TIMPANI_A_CDC_ANC1_SCALE:
+ case TIMPANI_A_CDC_ANC2_IIR_COEFF_PTR:
+ case TIMPANI_A_CDC_ANC2_IIR_COEFF_MSB:
+ case TIMPANI_A_CDC_ANC2_IIR_COEFF_LSB:
+ case TIMPANI_A_CDC_ANC2_LPF_COEFF_PTR:
+ case TIMPANI_A_CDC_ANC2_LPF_COEFF_MSB:
+ case TIMPANI_A_CDC_ANC2_LPF_COEFF_LSB:
+ case TIMPANI_A_CDC_ANC2_SCALE_PTR:
+ case TIMPANI_A_CDC_ANC2_SCALE:
+ case TIMPANI_A_CDC_ANC1_CTL1:
+ case TIMPANI_A_CDC_ANC1_CTL2:
+ case TIMPANI_A_CDC_ANC1_FF_FB_SHIFT:
+ case TIMPANI_A_CDC_ANC2_CTL1:
+ case TIMPANI_A_CDC_ANC2_CTL2:
+ case TIMPANI_A_CDC_ANC2_FF_FB_SHIFT:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static int adie_codec_write(u8 reg, u8 mask, u8 val)
+{
+ int rc = 0;
+ u8 new_val;
+
+ new_val = (val & mask) | (timpani_shadow[reg] & ~mask);
+ if (!(timpani_register_is_cacheable(reg) &&
+ (new_val == timpani_shadow[reg]))) {
+
+ rc = marimba_write_bit_mask(adie_codec.pdrv_ptr, reg, &new_val,
+ 1, 0xFF);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("%s: fail to write reg %x\n", __func__, reg);
+ rc = -EIO;
+ goto error;
+ }
+ timpani_shadow[reg] = new_val;
+ pr_debug("%s: write reg %x val %x new value %x\n", __func__,
+ reg, val, new_val);
+ }
+
+error:
+ return rc;
+}
+
+
+static int reg_in_use(u8 reg_ref, u8 path_type)
+{
+ if ((reg_ref & ~path_type) == 0)
+ return 0;
+ else
+ return 1;
+}
+
+static int adie_codec_refcnt_write(u8 reg, u8 mask, u8 val, enum refcnt cnt,
+ u8 path_type)
+{
+ u8 i;
+ int j;
+ u8 fld_mask;
+ u8 path_mask;
+ u8 reg_mask = 0;
+ int rc = 0;
+
+ for (i = 0; i < 0xEF; i++) {
+ if (timpani_regset[i].reg_addr == reg) {
+ for (j = 0; j < TIMPANI_MAX_FIELDS; j++) {
+ fld_mask = timpani_regset[i].fld_ref_cnt[j].mask
+ & mask;
+ path_mask = timpani_regset[i].fld_ref_cnt[j]
+ .path_mask;
+ if (fld_mask) {
+ if (!reg_in_use(path_mask, path_type))
+ reg_mask |= fld_mask;
+ if (cnt == INC)
+ timpani_regset[i].fld_ref_cnt[j]
+ .path_mask |= path_type;
+ else if (cnt == DEC)
+ timpani_regset[i].fld_ref_cnt[j]
+ .path_mask &=
+ ~path_type;
+ }
+ }
+
+ if (reg_mask)
+ rc = adie_codec_write(reg, reg_mask, val);
+ reg_mask = 0;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static int adie_codec_read(u8 reg, u8 *val)
+{
+ return marimba_read(adie_codec.pdrv_ptr, reg, val, 1);
+}
+
+static int timpani_adie_codec_setpath(struct adie_codec_path *path_ptr,
+ u32 freq_plan, u32 osr)
+{
+ int rc = 0;
+ u32 i, freq_idx = 0, freq = 0;
+
+ if (path_ptr == NULL)
+ return -EINVAL;
+
+ if (path_ptr->curr_stage != ADIE_CODEC_DIGITAL_OFF) {
+ rc = -EBUSY;
+ goto error;
+ }
+
+ for (i = 0; i < path_ptr->profile->setting_sz; i++) {
+ if (path_ptr->profile->settings[i].osr == osr) {
+ if (path_ptr->profile->settings[i].freq_plan >=
+ freq_plan) {
+ if (freq == 0) {
+ freq = path_ptr->profile->settings[i].
+ freq_plan;
+ freq_idx = i;
+ } else if (path_ptr->profile->settings[i].
+ freq_plan < freq) {
+ freq = path_ptr->profile->settings[i].
+ freq_plan;
+ freq_idx = i;
+ }
+ }
+ }
+ }
+
+ if (freq_idx >= path_ptr->profile->setting_sz)
+ rc = -ENODEV;
+ else {
+ path_ptr->hwsetting_idx = freq_idx;
+ path_ptr->stage_idx = 0;
+ }
+
+error:
+ return rc;
+}
+
+static u32 timpani_adie_codec_freq_supported(
+ struct adie_codec_dev_profile *profile,
+ u32 requested_freq)
+{
+ u32 i, rc = -EINVAL;
+
+ for (i = 0; i < profile->setting_sz; i++) {
+ if (profile->settings[i].freq_plan >= requested_freq) {
+ rc = 0;
+ break;
+ }
+ }
+ return rc;
+}
+int timpani_adie_codec_enable_sidetone(struct adie_codec_path *rx_path_ptr,
+ u32 enable)
+{
+ int rc = 0;
+
+ pr_debug("%s()\n", __func__);
+
+ mutex_lock(&adie_codec.lock);
+
+ if (!rx_path_ptr || &adie_codec.path[ADIE_CODEC_RX] != rx_path_ptr) {
+ pr_err("%s: invalid path pointer\n", __func__);
+ rc = -EINVAL;
+ goto error;
+ } else if (rx_path_ptr->curr_stage !=
+ ADIE_CODEC_DIGITAL_ANALOG_READY) {
+ pr_err("%s: bad state\n", __func__);
+ rc = -EPERM;
+ goto error;
+ }
+
+ if (enable) {
+ rc = adie_codec_write(TIMPANI_A_CDC_RX1_CTL,
+ TIMPANI_RX1_ST_MASK, TIMPANI_RX1_ST_ENABLE);
+
+ if (rx_path_ptr->reg_owner == RA_OWNER_PATH_RX1)
+ adie_codec_write(TIMPANI_A_CDC_ST_MIXING,
+ TIMPANI_CDC_ST_MIXING_TX1_MASK,
+ TIMPANI_CDC_ST_MIXING_TX1_ENABLE);
+ else if (rx_path_ptr->reg_owner == RA_OWNER_PATH_RX2)
+ adie_codec_write(TIMPANI_A_CDC_ST_MIXING,
+ TIMPANI_CDC_ST_MIXING_TX2_MASK,
+ TIMPANI_CDC_ST_MIXING_TX2_ENABLE);
+ } else {
+ rc = adie_codec_write(TIMPANI_A_CDC_RX1_CTL,
+ TIMPANI_RX1_ST_MASK, 0);
+
+ if (rx_path_ptr->reg_owner == RA_OWNER_PATH_RX1)
+ adie_codec_write(TIMPANI_A_CDC_ST_MIXING,
+ TIMPANI_CDC_ST_MIXING_TX1_MASK, 0);
+ else if (rx_path_ptr->reg_owner == RA_OWNER_PATH_RX2)
+ adie_codec_write(TIMPANI_A_CDC_ST_MIXING,
+ TIMPANI_CDC_ST_MIXING_TX2_MASK, 0);
+ }
+
+error:
+ mutex_unlock(&adie_codec.lock);
+ return rc;
+}
+static int timpani_adie_codec_enable_anc(struct adie_codec_path *rx_path_ptr,
+ u32 enable, struct adie_codec_anc_data *calibration_writes)
+{
+ int index = 0;
+ int rc = 0;
+ u8 reg, mask, val;
+ pr_debug("%s: enable = %d\n", __func__, enable);
+
+ mutex_lock(&adie_codec.lock);
+
+ if (!rx_path_ptr || &adie_codec.path[ADIE_CODEC_RX] != rx_path_ptr) {
+ pr_err("%s: invalid path pointer\n", __func__);
+ rc = -EINVAL;
+ goto error;
+ } else if (rx_path_ptr->curr_stage !=
+ ADIE_CODEC_DIGITAL_ANALOG_READY) {
+ pr_err("%s: bad state\n", __func__);
+ rc = -EPERM;
+ goto error;
+ }
+ if (enable) {
+ if (!calibration_writes || !calibration_writes->writes) {
+ pr_err("%s: No ANC calibration data\n", __func__);
+ rc = -EPERM;
+ goto error;
+ }
+ while (index < calibration_writes->size) {
+ ADIE_CODEC_UNPACK_ENTRY(calibration_writes->
+ writes[index], reg, mask, val);
+ adie_codec_write(reg, mask, val);
+ index++;
+ }
+ } else {
+ adie_codec_write(TIMPANI_A_CDC_ANC1_CTL1,
+ TIMPANI_CDC_ANC1_CTL1_ANC1_EN_M,
+ TIMPANI_CDC_ANC1_CTL1_ANC1_EN_ANC_DIS <<
+ TIMPANI_CDC_ANC1_CTL1_ANC1_EN_S);
+
+ adie_codec_write(TIMPANI_A_CDC_ANC2_CTL1,
+ TIMPANI_CDC_ANC2_CTL1_ANC2_EN_M,
+ TIMPANI_CDC_ANC2_CTL1_ANC2_EN_ANC_DIS <<
+ TIMPANI_CDC_ANC2_CTL1_ANC2_EN_S);
+ }
+
+error:
+ mutex_unlock(&adie_codec.lock);
+ return rc;
+}
+
+static void adie_codec_restore_regdefault(u8 path_mask, u32 blk)
+{
+ u32 ireg;
+ u32 regset_sz =
+ (sizeof(timpani_regset)/sizeof(struct timpani_regaccess));
+
+ for (ireg = 0; ireg < regset_sz; ireg++) {
+ if (timpani_regset[ireg].blk_mask[blk]) {
+ /* only process register belong to the block */
+ u8 reg = timpani_regset[ireg].reg_addr;
+ u8 mask = timpani_regset[ireg].blk_mask[blk];
+ u8 val = timpani_regset[ireg].reg_default;
+ adie_codec_refcnt_write(reg, mask, val, IGNORE,
+ path_mask);
+ }
+ }
+}
+
+static void adie_codec_reach_stage_action(struct adie_codec_path *path_ptr,
+ u32 stage)
+{
+ u32 iblk, iowner; /* iterators */
+ u8 path_mask;
+
+ if (path_ptr == NULL)
+ return;
+
+ path_mask = TIMPANI_PATH_MASK(path_ptr->reg_owner);
+
+ if (stage != ADIE_CODEC_DIGITAL_OFF)
+ return;
+
+ for (iblk = 0 ; iblk <= RA_BLOCK_RESERVED ; iblk++) {
+ for (iowner = 0; iowner < RA_OWNER_NUM; iowner++) {
+ if (timpani_blkcfg[iblk].valid_owners[iowner] ==
+ path_ptr->reg_owner) {
+ adie_codec_restore_regdefault(path_mask, iblk);
+ break; /* This path owns this block */
+ }
+ }
+ }
+}
+
+static int timpani_adie_codec_proceed_stage(struct adie_codec_path *path_ptr,
+ u32 state)
+{
+ int rc = 0, loop_exit = 0;
+ struct adie_codec_action_unit *curr_action;
+ struct adie_codec_hwsetting_entry *setting;
+ u8 reg, mask, val;
+ u8 path_mask;
+
+ if (path_ptr == NULL)
+ return -EINVAL;
+
+ path_mask = TIMPANI_PATH_MASK(path_ptr->reg_owner);
+
+ mutex_lock(&adie_codec.lock);
+ setting = &path_ptr->profile->settings[path_ptr->hwsetting_idx];
+ while (!loop_exit) {
+
+ curr_action = &setting->actions[path_ptr->stage_idx];
+
+ switch (curr_action->type) {
+ case ADIE_CODEC_ACTION_ENTRY:
+ ADIE_CODEC_UNPACK_ENTRY(curr_action->action,
+ reg, mask, val);
+ if (state == ADIE_CODEC_DIGITAL_OFF)
+ adie_codec_refcnt_write(reg, mask, val, DEC,
+ path_mask);
+ else
+ adie_codec_refcnt_write(reg, mask, val, INC,
+ path_mask);
+ break;
+ case ADIE_CODEC_ACTION_DELAY_WAIT:
+ if (curr_action->action > MAX_MDELAY_US)
+ msleep(curr_action->action/1000);
+ else
+ usleep_range(curr_action->action,
+ curr_action->action);
+ break;
+ case ADIE_CODEC_ACTION_STAGE_REACHED:
+ adie_codec_reach_stage_action(path_ptr,
+ curr_action->action);
+ if (curr_action->action == state) {
+ path_ptr->curr_stage = state;
+ loop_exit = 1;
+ }
+ break;
+ default:
+ BUG();
+ }
+
+ path_ptr->stage_idx++;
+ if (path_ptr->stage_idx == setting->action_sz)
+ path_ptr->stage_idx = 0;
+ }
+ mutex_unlock(&adie_codec.lock);
+
+ return rc;
+}
+
+static void timpani_codec_bring_up(void)
+{
+ /* Codec power up sequence */
+ adie_codec_write(0xFF, 0xFF, 0x08);
+ adie_codec_write(0xFF, 0xFF, 0x0A);
+ adie_codec_write(0xFF, 0xFF, 0x0E);
+ adie_codec_write(0xFF, 0xFF, 0x07);
+ adie_codec_write(0xFF, 0xFF, 0x17);
+ adie_codec_write(TIMPANI_A_MREF, 0xFF, 0xF2);
+ msleep(15);
+ adie_codec_write(TIMPANI_A_MREF, 0xFF, 0x22);
+
+ /* Bypass TX HPFs to prevent pops */
+ adie_codec_write(TIMPANI_A_CDC_BYPASS_CTL2, TIMPANI_CDC_BYPASS_CTL2_M,
+ TIMPANI_CDC_BYPASS_CTL2_POR);
+ adie_codec_write(TIMPANI_A_CDC_BYPASS_CTL3, TIMPANI_CDC_BYPASS_CTL3_M,
+ TIMPANI_CDC_BYPASS_CTL3_POR);
+}
+
+static void timpani_codec_bring_down(void)
+{
+ adie_codec_write(TIMPANI_A_MREF, 0xFF, TIMPANI_MREF_POR);
+ adie_codec_write(0xFF, 0xFF, 0x07);
+ adie_codec_write(0xFF, 0xFF, 0x06);
+ adie_codec_write(0xFF, 0xFF, 0x0E);
+ adie_codec_write(0xFF, 0xFF, 0x08);
+}
+
+static int timpani_adie_codec_open(struct adie_codec_dev_profile *profile,
+ struct adie_codec_path **path_pptr)
+{
+ int rc = 0;
+
+ mutex_lock(&adie_codec.lock);
+
+ if (!profile || !path_pptr) {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ if (adie_codec.path[profile->path_type].profile) {
+ rc = -EBUSY;
+ goto error;
+ }
+
+ if (!adie_codec.ref_cnt) {
+
+ if (adie_codec.codec_pdata &&
+ adie_codec.codec_pdata->marimba_codec_power) {
+
+ rc = adie_codec.codec_pdata->marimba_codec_power(1);
+ if (rc) {
+ pr_err("%s: could not power up timpani "
+ "codec\n", __func__);
+ goto error;
+ }
+ timpani_codec_bring_up();
+ } else {
+ pr_err("%s: couldn't detect timpani codec\n", __func__);
+ rc = -ENODEV;
+ goto error;
+ }
+
+ }
+
+ adie_codec.path[profile->path_type].profile = profile;
+ *path_pptr = (void *) &adie_codec.path[profile->path_type];
+ adie_codec.ref_cnt++;
+ adie_codec.path[profile->path_type].hwsetting_idx = 0;
+ adie_codec.path[profile->path_type].curr_stage = ADIE_CODEC_DIGITAL_OFF;
+ adie_codec.path[profile->path_type].stage_idx = 0;
+
+
+error:
+ mutex_unlock(&adie_codec.lock);
+ return rc;
+}
+
+static int timpani_adie_codec_close(struct adie_codec_path *path_ptr)
+{
+ int rc = 0;
+
+ mutex_lock(&adie_codec.lock);
+
+ if (!path_ptr) {
+ rc = -EINVAL;
+ goto error;
+ }
+ if (path_ptr->curr_stage != ADIE_CODEC_DIGITAL_OFF)
+ adie_codec_proceed_stage(path_ptr, ADIE_CODEC_DIGITAL_OFF);
+
+ BUG_ON(!adie_codec.ref_cnt);
+
+ path_ptr->profile = NULL;
+ adie_codec.ref_cnt--;
+
+ if (!adie_codec.ref_cnt) {
+ /* Timpani CDC power down sequence */
+ timpani_codec_bring_down();
+
+ if (adie_codec.codec_pdata &&
+ adie_codec.codec_pdata->marimba_codec_power) {
+
+ rc = adie_codec.codec_pdata->marimba_codec_power(0);
+ if (rc) {
+ pr_err("%s: could not power down timpani "
+ "codec\n", __func__);
+ goto error;
+ }
+ }
+ }
+
+error:
+ mutex_unlock(&adie_codec.lock);
+ return rc;
+}
+
+static int timpani_adie_codec_set_master_mode(struct adie_codec_path *path_ptr,
+ u8 master)
+{
+ u8 val = master ? 1 : 0;
+
+ if (!path_ptr)
+ return -EINVAL;
+
+ if (path_ptr->reg_owner == RA_OWNER_PATH_RX1)
+ adie_codec_write(TIMPANI_A_CDC_RX1_CTL, 0x01, val);
+ else if (path_ptr->reg_owner == RA_OWNER_PATH_TX1)
+ adie_codec_write(TIMPANI_A_CDC_TX_I2S_CTL, 0x01, val);
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+int timpani_adie_codec_set_device_analog_volume(
+ struct adie_codec_path *path_ptr,
+ u32 num_channels, u32 volume)
+{
+ u8 val;
+ u8 curr_val;
+ u8 i;
+
+ adie_codec_read(TIMPANI_A_AUXPGA_LR_GAIN, &curr_val);
+
+ /* Volume is expressed as a percentage. */
+ /* The upper nibble is the left channel, lower right channel. */
+ val = (u8)((volume * TIMPANI_CODEC_AUXPGA_GAIN_RANGE) / 100);
+ val |= val << 4;
+
+ if ((curr_val & 0x0F) < (val & 0x0F)) {
+ for (i = curr_val; i < val; i += 0x11)
+ adie_codec_write(TIMPANI_A_AUXPGA_LR_GAIN, 0xFF, i);
+ } else if ((curr_val & 0x0F) > (val & 0x0F)) {
+ for (i = curr_val; i > val; i -= 0x11)
+ adie_codec_write(TIMPANI_A_AUXPGA_LR_GAIN, 0xFF, i);
+ }
+
+ return 0;
+}
+
+enum adie_vol_type {
+ ADIE_CODEC_RX_DIG_VOL,
+ ADIE_CODEC_TX_DIG_VOL,
+ ADIE_CODEC_VOL_TYPE_MAX
+};
+
+#define CDC_RX1LG 0x84
+#define CDC_RX1RG 0x85
+#define CDC_TX1LG 0x86
+#define CDC_TX1RG 0x87
+#define DIG_VOL_MASK 0xFF
+
+#define CDC_GCTL1 0x8A
+#define RX1_PGA_UPDATE_L 0x04
+#define RX1_PGA_UPDATE_R 0x08
+#define TX1_PGA_UPDATE_L 0x40
+#define TX1_PGA_UPDATE_R 0x80
+#define CDC_GCTL1_RX_MASK 0x0F
+#define CDC_GCTL1_TX_MASK 0xF0
+
+enum {
+ TIMPANI_MIN_DIG_VOL = -84, /* in DB*/
+ TIMPANI_MAX_DIG_VOL = 16, /* in DB*/
+ TIMPANI_DIG_VOL_STEP = 3 /* in DB*/
+};
+
+static int timpani_adie_codec_set_dig_vol(enum adie_vol_type vol_type,
+ u32 num_chan, u32 vol_per)
+{
+ u8 reg_left, reg_right;
+ u8 gain_reg_val, gain_reg_mask;
+ s8 new_reg_val, cur_reg_val;
+ s8 step_size;
+
+ adie_codec_read(CDC_GCTL1, &gain_reg_val);
+
+ if (vol_type == ADIE_CODEC_RX_DIG_VOL) {
+
+ pr_debug("%s : RX DIG VOL. num_chan = %u\n", __func__,
+ num_chan);
+ reg_left = CDC_RX1LG;
+ reg_right = CDC_RX1RG;
+
+ if (num_chan == 1)
+ gain_reg_val |= RX1_PGA_UPDATE_L;
+ else
+ gain_reg_val |= (RX1_PGA_UPDATE_L | RX1_PGA_UPDATE_R);
+
+ gain_reg_mask = CDC_GCTL1_RX_MASK;
+ } else {
+
+ pr_debug("%s : TX DIG VOL. num_chan = %u\n", __func__,
+ num_chan);
+ reg_left = CDC_TX1LG;
+ reg_right = CDC_TX1RG;
+
+ if (num_chan == 1)
+ gain_reg_val |= TX1_PGA_UPDATE_L;
+ else
+ gain_reg_val |= (TX1_PGA_UPDATE_L | TX1_PGA_UPDATE_R);
+
+ gain_reg_mask = CDC_GCTL1_TX_MASK;
+ }
+
+ adie_codec_read(reg_left, &cur_reg_val);
+
+ pr_debug("%s: vol_per = %d cur_reg_val = %d 0x%x\n", __func__, vol_per,
+ cur_reg_val, cur_reg_val);
+
+ new_reg_val = TIMPANI_MIN_DIG_VOL +
+ (((TIMPANI_MAX_DIG_VOL - TIMPANI_MIN_DIG_VOL) * vol_per) / 100);
+
+ pr_debug("new_reg_val = %d 0x%x\n", new_reg_val, new_reg_val);
+
+ if (new_reg_val > cur_reg_val) {
+ step_size = TIMPANI_DIG_VOL_STEP;
+ } else if (new_reg_val < cur_reg_val) {
+ step_size = -TIMPANI_DIG_VOL_STEP;
+ } else {
+ pr_debug("new_reg_val and cur_reg_val are same 0x%x\n",
+ new_reg_val);
+ return 0;
+ }
+
+ while (cur_reg_val != new_reg_val) {
+
+ if (((new_reg_val > cur_reg_val) &&
+ ((new_reg_val - cur_reg_val) < TIMPANI_DIG_VOL_STEP)) ||
+ ((cur_reg_val > new_reg_val) &&
+ ((cur_reg_val - new_reg_val)
+ < TIMPANI_DIG_VOL_STEP))) {
+
+ cur_reg_val = new_reg_val;
+
+ pr_debug("diff less than step. write new_reg_val = %d"
+ " 0x%x\n", new_reg_val, new_reg_val);
+
+ } else {
+ cur_reg_val = cur_reg_val + step_size;
+
+ pr_debug("cur_reg_val = %d 0x%x\n",
+ cur_reg_val, cur_reg_val);
+ }
+
+ adie_codec_write(reg_left, DIG_VOL_MASK, cur_reg_val);
+
+ if (num_chan == 2)
+ adie_codec_write(reg_right, DIG_VOL_MASK, cur_reg_val);
+
+ adie_codec_write(CDC_GCTL1, gain_reg_mask, gain_reg_val);
+ }
+ return 0;
+}
+
+static int timpani_adie_codec_set_device_digital_volume(
+ struct adie_codec_path *path_ptr,
+ u32 num_channels, u32 vol_percentage /* in percentage */)
+{
+ enum adie_vol_type vol_type;
+
+ if (!path_ptr || (path_ptr->curr_stage !=
+ ADIE_CODEC_DIGITAL_ANALOG_READY)) {
+ pr_info("%s: timpani codec not ready for volume control\n",
+ __func__);
+ return -EPERM;
+ }
+
+ if (num_channels > 2) {
+ pr_err("%s: timpani odec only supports max two channels\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (path_ptr->profile->path_type == ADIE_CODEC_RX) {
+ vol_type = ADIE_CODEC_RX_DIG_VOL;
+ } else if (path_ptr->profile->path_type == ADIE_CODEC_TX) {
+ vol_type = ADIE_CODEC_TX_DIG_VOL;
+ } else {
+ pr_err("%s: invalid device data neither RX nor TX\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ timpani_adie_codec_set_dig_vol(vol_type, num_channels, vol_percentage);
+
+ return 0;
+}
+
+static const struct adie_codec_operations timpani_adie_ops = {
+ .codec_id = TIMPANI_ID,
+ .codec_open = timpani_adie_codec_open,
+ .codec_close = timpani_adie_codec_close,
+ .codec_setpath = timpani_adie_codec_setpath,
+ .codec_proceed_stage = timpani_adie_codec_proceed_stage,
+ .codec_freq_supported = timpani_adie_codec_freq_supported,
+ .codec_enable_sidetone = timpani_adie_codec_enable_sidetone,
+ .codec_set_master_mode = timpani_adie_codec_set_master_mode,
+ .codec_enable_anc = timpani_adie_codec_enable_anc,
+ .codec_set_device_analog_volume =
+ timpani_adie_codec_set_device_analog_volume,
+ .codec_set_device_digital_volume =
+ timpani_adie_codec_set_device_digital_volume,
+};
+
+static void timpani_codec_populate_shadow_registers(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(timpani_regset); i++) {
+ if (timpani_regset[i].reg_addr < TIMPANI_ARRAY_SIZE) {
+ timpani_shadow[timpani_regset[i].reg_addr] =
+ timpani_regset[i].reg_default;
+ }
+ }
+}
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_timpani_dent;
+static struct dentry *debugfs_peek;
+static struct dentry *debugfs_poke;
+static struct dentry *debugfs_power;
+static struct dentry *debugfs_dump;
+
+static unsigned char read_data;
+
+static int codec_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static int get_parameters(char *buf, long int *param1, int num_of_par)
+{
+ char *token;
+ int base, cnt;
+
+ token = strsep(&buf, " ");
+
+ for (cnt = 0; cnt < num_of_par; cnt++) {
+ if (token != NULL) {
+ if ((token[1] == 'x') || (token[1] == 'X'))
+ base = 16;
+ else
+ base = 10;
+
+ if (strict_strtoul(token, base, ¶m1[cnt]) != 0)
+ return -EINVAL;
+
+ token = strsep(&buf, " ");
+ }
+ else
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static ssize_t codec_debug_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ char lbuf[8];
+
+ snprintf(lbuf, sizeof(lbuf), "0x%x\n", read_data);
+ return simple_read_from_buffer(ubuf, count, ppos, lbuf, strlen(lbuf));
+}
+
+static ssize_t codec_debug_write(struct file *filp,
+ const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+ char *access_str = filp->private_data;
+ char lbuf[32];
+ int rc;
+ int i;
+ int read_result;
+ u8 reg_val;
+ long int param[5];
+
+ if (cnt > sizeof(lbuf) - 1)
+ return -EINVAL;
+
+ rc = copy_from_user(lbuf, ubuf, cnt);
+ if (rc)
+ return -EFAULT;
+
+ lbuf[cnt] = '\0';
+
+ if (!strcmp(access_str, "power")) {
+ if (get_parameters(lbuf, param, 1) == 0) {
+ switch (param[0]) {
+ case 1:
+ adie_codec.codec_pdata->marimba_codec_power(1);
+ timpani_codec_bring_up();
+ break;
+ case 0:
+ timpani_codec_bring_down();
+ adie_codec.codec_pdata->marimba_codec_power(0);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ } else
+ rc = -EINVAL;
+ } else if (!strcmp(access_str, "poke")) {
+ /* write */
+ rc = get_parameters(lbuf, param, 2);
+ if ((param[0] <= 0xFF) && (param[1] <= 0xFF) &&
+ (rc == 0))
+ adie_codec_write(param[0], 0xFF, param[1]);
+ else
+ rc = -EINVAL;
+ } else if (!strcmp(access_str, "peek")) {
+ /* read */
+ rc = get_parameters(lbuf, param, 1);
+ if ((param[0] <= 0xFF) && (rc == 0))
+ adie_codec_read(param[0], &read_data);
+ else
+ rc = -EINVAL;
+ } else if (!strcmp(access_str, "dump")) {
+ pr_info("************** timpani regs *************\n");
+ for (i = 0; i < 0xFF; i++) {
+ read_result = adie_codec_read(i, ®_val);
+ if (read_result < 0) {
+ pr_info("failed to read codec register\n");
+ break;
+ } else
+ pr_info("reg 0x%02X val 0x%02X\n", i, reg_val);
+ }
+ pr_info("*****************************************\n");
+ }
+
+ if (rc == 0)
+ rc = cnt;
+ else
+ pr_err("%s: rc = %d\n", __func__, rc);
+
+ return rc;
+}
+
+static const struct file_operations codec_debug_ops = {
+ .open = codec_debug_open,
+ .write = codec_debug_write,
+ .read = codec_debug_read
+};
+#endif
+
+static int timpani_codec_probe(struct platform_device *pdev)
+{
+ int rc;
+
+ adie_codec.pdrv_ptr = platform_get_drvdata(pdev);
+ adie_codec.codec_pdata = pdev->dev.platform_data;
+
+ if (adie_codec.codec_pdata->snddev_profile_init)
+ adie_codec.codec_pdata->snddev_profile_init();
+
+ timpani_codec_populate_shadow_registers();
+
+ /* Register the timpani ADIE operations */
+ rc = adie_codec_register_codec_operations(&timpani_adie_ops);
+
+#ifdef CONFIG_DEBUG_FS
+ debugfs_timpani_dent = debugfs_create_dir("msm_adie_codec", 0);
+ if (!IS_ERR(debugfs_timpani_dent)) {
+ debugfs_peek = debugfs_create_file("peek",
+ S_IFREG | S_IRUGO, debugfs_timpani_dent,
+ (void *) "peek", &codec_debug_ops);
+
+ debugfs_poke = debugfs_create_file("poke",
+ S_IFREG | S_IRUGO, debugfs_timpani_dent,
+ (void *) "poke", &codec_debug_ops);
+
+ debugfs_power = debugfs_create_file("power",
+ S_IFREG | S_IRUGO, debugfs_timpani_dent,
+ (void *) "power", &codec_debug_ops);
+
+ debugfs_dump = debugfs_create_file("dump",
+ S_IFREG | S_IRUGO, debugfs_timpani_dent,
+ (void *) "dump", &codec_debug_ops);
+
+ }
+#endif
+
+ return rc;
+}
+
+static struct platform_driver timpani_codec_driver = {
+ .probe = timpani_codec_probe,
+ .driver = {
+ .name = "timpani_codec",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init timpani_codec_init(void)
+{
+ s32 rc;
+
+ rc = platform_driver_register(&timpani_codec_driver);
+ if (IS_ERR_VALUE(rc))
+ goto error;
+
+ adie_codec.path[ADIE_CODEC_TX].reg_owner = RA_OWNER_PATH_TX1;
+ adie_codec.path[ADIE_CODEC_RX].reg_owner = RA_OWNER_PATH_RX1;
+ adie_codec.path[ADIE_CODEC_LB].reg_owner = RA_OWNER_PATH_LB;
+ mutex_init(&adie_codec.lock);
+error:
+ return rc;
+}
+
+static void __exit timpani_codec_exit(void)
+{
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove(debugfs_peek);
+ debugfs_remove(debugfs_poke);
+ debugfs_remove(debugfs_power);
+ debugfs_remove(debugfs_dump);
+ debugfs_remove(debugfs_timpani_dent);
+#endif
+ platform_driver_unregister(&timpani_codec_driver);
+}
+
+module_init(timpani_codec_init);
+module_exit(timpani_codec_exit);
+
+MODULE_DESCRIPTION("Timpani codec driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/tps65023.c b/drivers/mfd/tps65023.c
new file mode 100644
index 0000000..e67997c
--- /dev/null
+++ b/drivers/mfd/tps65023.c
@@ -0,0 +1,122 @@
+/* Copyright (c) 2009, Code Aurora Forum. 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 and
+ * only 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.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/mfd/tps65023.h>
+
+/* TPS65023_registers */
+#define TPS65023_VERSION 0
+#define TPS65023_PGOODZ 1
+#define TPS65023_MASK 2
+#define TPS65023_REG_CTRL 3
+#define TPS65023_CON_CTRL 4
+#define TPS65023_CON_CTRL2 5
+#define TPS65023_DEFCORE 6
+#define TPS65023_DEFSLEW 7
+#define TPS65023_LDO_CTRL 8
+#define TPS65023_MAX 9
+
+static struct i2c_client *tpsclient;
+
+int tps65023_set_dcdc1_level(int mvolts)
+{
+ int val;
+ int ret;
+
+ if (!tpsclient)
+ return -ENODEV;
+
+ if (mvolts < 800 || mvolts > 1600)
+ return -EINVAL;
+
+ if (mvolts == 1600)
+ val = 0x1F;
+ else
+ val = ((mvolts - 800)/25) & 0x1F;
+
+ ret = i2c_smbus_write_byte_data(tpsclient, TPS65023_DEFCORE, val);
+
+ if (!ret)
+ ret = i2c_smbus_write_byte_data(tpsclient,
+ TPS65023_CON_CTRL2, 0x80);
+
+ return ret;
+}
+EXPORT_SYMBOL(tps65023_set_dcdc1_level);
+
+int tps65023_get_dcdc1_level(int *mvolts)
+{
+ int val;
+
+ if (!tpsclient)
+ return -ENODEV;
+
+ val = i2c_smbus_read_byte_data(tpsclient, TPS65023_DEFCORE) & 0x1F;
+
+ if (val == 0x1F)
+ *mvolts = 1600;
+ else
+ *mvolts = (val * 25) + 800;
+ return 0;
+}
+EXPORT_SYMBOL(tps65023_get_dcdc1_level);
+
+static int tps65023_probe(struct i2c_client *client,
+ const struct i2c_device_id *dev_id)
+{
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ printk(KERN_ERR "TPS65023 does not support SMBUS_BYTE_DATA.\n");
+ return -EINVAL;
+ }
+
+ tpsclient = client;
+ printk(KERN_INFO "TPS65023: PMIC probed.\n");
+ return 0;
+}
+
+static int __devexit tps65023_remove(struct i2c_client *client)
+{
+ tpsclient = NULL;
+ return 0;
+}
+
+static const struct i2c_device_id tps65023_id[] = {
+ { "tps65023", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tps65023_id);
+
+static struct i2c_driver tps65023_driver = {
+ .driver = {
+ .name = "tps65023",
+ .owner = THIS_MODULE,
+ },
+ .probe = tps65023_probe,
+ .remove = __devexit_p(tps65023_remove),
+ .id_table = tps65023_id,
+};
+
+static int __init tps65023_init(void)
+{
+ return i2c_add_driver(&tps65023_driver);
+}
+
+
+static void __exit tps65023_exit(void)
+{
+ i2c_del_driver(&tps65023_driver);
+}
+
+module_init(tps65023_init);
+module_exit(tps65023_exit);
diff --git a/drivers/mfd/wcd9310-core.c b/drivers/mfd/wcd9310-core.c
new file mode 100644
index 0000000..5a77785
--- /dev/null
+++ b/drivers/mfd/wcd9310-core.c
@@ -0,0 +1,734 @@
+/* Copyright (c) 2011, Code Aurora Forum. 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 and
+ * only 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/pm8xxx/pm8921.h>
+#include <linux/mfd/wcd9310/core.h>
+#include <linux/mfd/wcd9310/pdata.h>
+#include <linux/mfd/wcd9310/registers.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/debugfs.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+
+#define TABLA_REGISTER_START_OFFSET 0x800
+static int tabla_read(struct tabla *tabla, unsigned short reg,
+ int bytes, void *dest, bool interface_reg)
+{
+ int ret;
+ u8 *buf = dest;
+
+ if (bytes <= 0) {
+ dev_err(tabla->dev, "Invalid byte read length %d\n", bytes);
+ return -EINVAL;
+ }
+
+ ret = tabla->read_dev(tabla, reg, bytes, dest, interface_reg);
+ if (ret < 0) {
+ dev_err(tabla->dev, "Tabla read failed\n");
+ return ret;
+ } else
+ dev_dbg(tabla->dev, "Read 0x%02x from R%d(0x%x)\n",
+ *buf, reg, reg);
+
+ return 0;
+}
+int tabla_reg_read(struct tabla *tabla, unsigned short reg)
+{
+ u8 val;
+ int ret;
+
+ mutex_lock(&tabla->io_lock);
+ ret = tabla_read(tabla, reg, 1, &val, false);
+ mutex_unlock(&tabla->io_lock);
+
+ if (ret < 0)
+ return ret;
+ else
+ return val;
+}
+EXPORT_SYMBOL_GPL(tabla_reg_read);
+
+static int tabla_write(struct tabla *tabla, unsigned short reg,
+ int bytes, void *src, bool interface_reg)
+{
+ u8 *buf = src;
+
+ if (bytes <= 0) {
+ pr_err("%s: Error, invalid write length\n", __func__);
+ return -EINVAL;
+ }
+
+ dev_dbg(tabla->dev, "Write %02x to R%d(0x%x)\n",
+ *buf, reg, reg);
+
+ return tabla->write_dev(tabla, reg, bytes, src, interface_reg);
+}
+
+int tabla_reg_write(struct tabla *tabla, unsigned short reg,
+ u8 val)
+{
+ int ret;
+
+ mutex_lock(&tabla->io_lock);
+ ret = tabla_write(tabla, reg, 1, &val, false);
+ mutex_unlock(&tabla->io_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tabla_reg_write);
+
+static u8 tabla_pgd_la;
+static u8 tabla_inf_la;
+
+int tabla_get_logical_addresses(u8 *pgd_la, u8 *inf_la)
+{
+ *pgd_la = tabla_pgd_la;
+ *inf_la = tabla_inf_la;
+ return 0;
+
+}
+EXPORT_SYMBOL_GPL(tabla_get_logical_addresses);
+
+int tabla_interface_reg_read(struct tabla *tabla, unsigned short reg)
+{
+ u8 val;
+ int ret;
+
+ mutex_lock(&tabla->io_lock);
+ ret = tabla_read(tabla, reg, 1, &val, true);
+ mutex_unlock(&tabla->io_lock);
+
+ if (ret < 0)
+ return ret;
+ else
+ return val;
+}
+EXPORT_SYMBOL_GPL(tabla_interface_reg_read);
+
+int tabla_interface_reg_write(struct tabla *tabla, unsigned short reg,
+ u8 val)
+{
+ int ret;
+
+ mutex_lock(&tabla->io_lock);
+ ret = tabla_write(tabla, reg, 1, &val, true);
+ mutex_unlock(&tabla->io_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tabla_interface_reg_write);
+
+int tabla_bulk_read(struct tabla *tabla, unsigned short reg,
+ int count, u8 *buf)
+{
+ int ret;
+
+ mutex_lock(&tabla->io_lock);
+
+ ret = tabla_read(tabla, reg, count, buf, false);
+
+ mutex_unlock(&tabla->io_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tabla_bulk_read);
+
+int tabla_bulk_write(struct tabla *tabla, unsigned short reg,
+ int count, u8 *buf)
+{
+ int ret;
+
+ mutex_lock(&tabla->io_lock);
+
+ ret = tabla_write(tabla, reg, count, buf, false);
+
+ mutex_unlock(&tabla->io_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tabla_bulk_write);
+
+static int tabla_slim_read_device(struct tabla *tabla, unsigned short reg,
+ int bytes, void *dest, bool interface)
+{
+ int ret;
+ struct slim_ele_access msg;
+ msg.start_offset = TABLA_REGISTER_START_OFFSET + reg;
+ msg.num_bytes = bytes;
+ msg.comp = NULL;
+
+ mutex_lock(&tabla->xfer_lock);
+ if (interface)
+ ret = slim_request_val_element(tabla->slim_slave, &msg, dest,
+ bytes);
+ else
+ ret = slim_request_val_element(tabla->slim, &msg, dest, bytes);
+
+ if (ret)
+ pr_err("%s: Error, Tabla read failed\n", __func__);
+
+ mutex_unlock(&tabla->xfer_lock);
+ return ret;
+}
+/* Interface specifies whether the write is to the interface or general
+ * registers.
+ */
+static int tabla_slim_write_device(struct tabla *tabla, unsigned short reg,
+ int bytes, void *src, bool interface)
+{
+ int ret;
+ struct slim_ele_access msg;
+ msg.start_offset = TABLA_REGISTER_START_OFFSET + reg;
+ msg.num_bytes = bytes;
+ msg.comp = NULL;
+
+ mutex_lock(&tabla->xfer_lock);
+ if (interface)
+ ret = slim_change_val_element(tabla->slim_slave, &msg, src,
+ bytes);
+ else
+ ret = slim_change_val_element(tabla->slim, &msg, src, bytes);
+ if (ret)
+ pr_err("%s: Error, Tabla write failed\n", __func__);
+
+ mutex_unlock(&tabla->xfer_lock);
+ return ret;
+}
+
+static struct mfd_cell tabla_devs[] = {
+ {
+ .name = "tabla_codec",
+ },
+};
+
+static void tabla_bring_up(struct tabla *tabla)
+{
+ tabla_reg_write(tabla, TABLA_A_LEAKAGE_CTL, 0x4);
+ tabla_reg_write(tabla, TABLA_A_CDC_CTL, 0);
+ usleep_range(5000, 5000);
+ tabla_reg_write(tabla, TABLA_A_CDC_CTL, 3);
+ tabla_reg_write(tabla, TABLA_A_LEAKAGE_CTL, 3);
+}
+
+static void tabla_bring_down(struct tabla *tabla)
+{
+ tabla_reg_write(tabla, TABLA_A_LEAKAGE_CTL, 0x7);
+ tabla_reg_write(tabla, TABLA_A_LEAKAGE_CTL, 0x6);
+ tabla_reg_write(tabla, TABLA_A_LEAKAGE_CTL, 0xe);
+ tabla_reg_write(tabla, TABLA_A_LEAKAGE_CTL, 0x8);
+}
+
+static int tabla_reset(struct tabla *tabla)
+{
+ int ret;
+ struct pm_gpio param = {
+ .direction = PM_GPIO_DIR_OUT,
+ .output_buffer = PM_GPIO_OUT_BUF_CMOS,
+ .output_value = 1,
+ .pull = PM_GPIO_PULL_NO,
+ .vin_sel = PM_GPIO_VIN_S4,
+ .out_strength = PM_GPIO_STRENGTH_MED,
+ .function = PM_GPIO_FUNC_NORMAL,
+ };
+
+ if (tabla->reset_gpio) {
+ ret = gpio_request(tabla->reset_gpio, "CDC_RESET");
+ if (ret) {
+ pr_err("%s: Failed to request gpio %d\n", __func__,
+ tabla->reset_gpio);
+ tabla->reset_gpio = 0;
+ return ret;
+ }
+
+ ret = pm8xxx_gpio_config(tabla->reset_gpio, ¶m);
+ if (ret)
+ pr_err("%s: Failed to configure gpio\n", __func__);
+
+ gpio_direction_output(tabla->reset_gpio, 1);
+ msleep(20);
+ gpio_direction_output(tabla->reset_gpio, 0);
+ msleep(20);
+ gpio_direction_output(tabla->reset_gpio, 1);
+ msleep(20);
+ }
+ return 0;
+}
+
+static void tabla_free_reset(struct tabla *tabla)
+{
+ if (tabla->reset_gpio) {
+ gpio_free(tabla->reset_gpio);
+ tabla->reset_gpio = 0;
+ }
+}
+
+struct tabla_regulator {
+ const char *name;
+ int min_uV;
+ int max_uV;
+ int optimum_uA;
+ struct regulator *regulator;
+};
+
+
+/*
+ * format : TABLA_<POWER_SUPPLY_PIN_NAME>_CUR_MAX
+ *
+ * <POWER_SUPPLY_PIN_NAME> from Tabla objective spec
+*/
+
+#define TABLA_CDC_VDDA_CP_CUR_MAX 500000
+#define TABLA_CDC_VDDA_RX_CUR_MAX 20000
+#define TABLA_CDC_VDDA_TX_CUR_MAX 20000
+#define TABLA_VDDIO_CDC_CUR_MAX 5000
+
+#define TABLA_VDDD_CDC_D_CUR_MAX 5000
+#define TABLA_VDDD_CDC_A_CUR_MAX 5000
+
+static struct tabla_regulator tabla_regulators[] = {
+ {
+ .name = "CDC_VDD_CP",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .optimum_uA = TABLA_CDC_VDDA_CP_CUR_MAX,
+ },
+ {
+ .name = "CDC_VDDA_RX",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .optimum_uA = TABLA_CDC_VDDA_RX_CUR_MAX,
+ },
+ {
+ .name = "CDC_VDDA_TX",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .optimum_uA = TABLA_CDC_VDDA_TX_CUR_MAX,
+ },
+ {
+ .name = "VDDIO_CDC",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .optimum_uA = TABLA_VDDIO_CDC_CUR_MAX,
+ },
+ {
+ .name = "VDDD_CDC_D",
+ .min_uV = 1225000,
+ .max_uV = 1225000,
+ .optimum_uA = TABLA_VDDD_CDC_D_CUR_MAX,
+ },
+ {
+ .name = "CDC_VDDA_A_1P2V",
+ .min_uV = 1225000,
+ .max_uV = 1225000,
+ .optimum_uA = TABLA_VDDD_CDC_A_CUR_MAX,
+ },
+};
+
+static int tabla_device_init(struct tabla *tabla, int irq)
+{
+ int ret;
+
+ mutex_init(&tabla->io_lock);
+ mutex_init(&tabla->xfer_lock);
+ dev_set_drvdata(tabla->dev, tabla);
+
+ tabla_bring_up(tabla);
+
+ ret = tabla_irq_init(tabla);
+ if (ret) {
+ pr_err("IRQ initialization failed\n");
+ goto err;
+ }
+
+ ret = mfd_add_devices(tabla->dev, -1,
+ tabla_devs, ARRAY_SIZE(tabla_devs),
+ NULL, 0);
+ if (ret != 0) {
+ dev_err(tabla->dev, "Failed to add children: %d\n", ret);
+ goto err_irq;
+ }
+
+ return ret;
+err_irq:
+ tabla_irq_exit(tabla);
+err:
+ tabla_bring_down(tabla);
+ mutex_destroy(&tabla->io_lock);
+ mutex_destroy(&tabla->xfer_lock);
+ return ret;
+}
+static void tabla_device_exit(struct tabla *tabla)
+{
+ tabla_irq_exit(tabla);
+ tabla_bring_down(tabla);
+ tabla_free_reset(tabla);
+ mutex_destroy(&tabla->io_lock);
+ mutex_destroy(&tabla->xfer_lock);
+ slim_remove_device(tabla->slim_slave);
+ kfree(tabla);
+}
+
+
+#ifdef CONFIG_DEBUG_FS
+struct tabla *debugTabla;
+
+static struct dentry *debugfs_tabla_dent;
+static struct dentry *debugfs_peek;
+static struct dentry *debugfs_poke;
+
+static unsigned char read_data;
+
+static int codec_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static int get_parameters(char *buf, long int *param1, int num_of_par)
+{
+ char *token;
+ int base, cnt;
+
+ token = strsep(&buf, " ");
+
+ for (cnt = 0; cnt < num_of_par; cnt++) {
+ if (token != NULL) {
+ if ((token[1] == 'x') || (token[1] == 'X'))
+ base = 16;
+ else
+ base = 10;
+
+ if (strict_strtoul(token, base, ¶m1[cnt]) != 0)
+ return -EINVAL;
+
+ token = strsep(&buf, " ");
+ } else
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static ssize_t codec_debug_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ char lbuf[8];
+
+ snprintf(lbuf, sizeof(lbuf), "0x%x\n", read_data);
+ return simple_read_from_buffer(ubuf, count, ppos, lbuf,
+ strnlen(lbuf, 7));
+}
+
+
+static ssize_t codec_debug_write(struct file *filp,
+ const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+ char *access_str = filp->private_data;
+ char lbuf[32];
+ int rc;
+ long int param[5];
+
+ if (cnt > sizeof(lbuf) - 1)
+ return -EINVAL;
+
+ rc = copy_from_user(lbuf, ubuf, cnt);
+ if (rc)
+ return -EFAULT;
+
+ lbuf[cnt] = '\0';
+
+ if (!strncmp(access_str, "poke", 6)) {
+ /* write */
+ rc = get_parameters(lbuf, param, 2);
+ if ((param[0] <= 0x3FF) && (param[1] <= 0xFF) &&
+ (rc == 0))
+ tabla_interface_reg_write(debugTabla, param[0],
+ param[1]);
+ else
+ rc = -EINVAL;
+ } else if (!strncmp(access_str, "peek", 6)) {
+ /* read */
+ rc = get_parameters(lbuf, param, 1);
+ if ((param[0] <= 0x3FF) && (rc == 0))
+ read_data = tabla_interface_reg_read(debugTabla,
+ param[0]);
+ else
+ rc = -EINVAL;
+ }
+
+ if (rc == 0)
+ rc = cnt;
+ else
+ pr_err("%s: rc = %d\n", __func__, rc);
+
+ return rc;
+}
+
+static const struct file_operations codec_debug_ops = {
+ .open = codec_debug_open,
+ .write = codec_debug_write,
+ .read = codec_debug_read
+};
+#endif
+
+static int tabla_enable_supplies(struct tabla *tabla)
+{
+ int ret;
+ int i;
+
+ tabla->supplies = kzalloc(sizeof(struct regulator_bulk_data) *
+ ARRAY_SIZE(tabla_regulators),
+ GFP_KERNEL);
+ if (!tabla->supplies) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(tabla_regulators); i++)
+ tabla->supplies[i].supply = tabla_regulators[i].name;
+
+ ret = regulator_bulk_get(tabla->dev, ARRAY_SIZE(tabla_regulators),
+ tabla->supplies);
+ if (ret != 0) {
+ dev_err(tabla->dev, "Failed to get supplies: err = %d\n", ret);
+ goto err_supplies;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(tabla_regulators); i++) {
+ ret = regulator_set_voltage(tabla->supplies[i].consumer,
+ tabla_regulators[i].min_uV, tabla_regulators[i].max_uV);
+ if (ret) {
+ pr_err("%s: Setting regulator voltage failed for "
+ "regulator %s err = %d\n", __func__,
+ tabla->supplies[i].supply, ret);
+ goto err_get;
+ }
+
+ ret = regulator_set_optimum_mode(tabla->supplies[i].consumer,
+ tabla_regulators[i].optimum_uA);
+ if (ret < 0) {
+ pr_err("%s: Setting regulator optimum mode failed for "
+ "regulator %s err = %d\n", __func__,
+ tabla->supplies[i].supply, ret);
+ goto err_get;
+ }
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(tabla_regulators),
+ tabla->supplies);
+ if (ret != 0) {
+ dev_err(tabla->dev, "Failed to enable supplies: err = %d\n",
+ ret);
+ goto err_configure;
+ }
+ return ret;
+
+err_configure:
+ for (i = 0; i < ARRAY_SIZE(tabla_regulators); i++) {
+ regulator_set_voltage(tabla->supplies[i].consumer, 0,
+ tabla_regulators[i].max_uV);
+ regulator_set_optimum_mode(tabla->supplies[i].consumer, 0);
+ }
+err_get:
+ regulator_bulk_free(ARRAY_SIZE(tabla_regulators), tabla->supplies);
+err_supplies:
+ kfree(tabla->supplies);
+err:
+ return ret;
+}
+
+static void tabla_disable_supplies(struct tabla *tabla)
+{
+ int i;
+
+ regulator_bulk_disable(ARRAY_SIZE(tabla_regulators),
+ tabla->supplies);
+ for (i = 0; i < ARRAY_SIZE(tabla_regulators); i++) {
+ regulator_set_voltage(tabla->supplies[i].consumer, 0,
+ tabla_regulators[i].max_uV);
+ regulator_set_optimum_mode(tabla->supplies[i].consumer, 0);
+ }
+ regulator_bulk_free(ARRAY_SIZE(tabla_regulators), tabla->supplies);
+ kfree(tabla->supplies);
+}
+
+static int tabla_slim_probe(struct slim_device *slim)
+{
+ struct tabla *tabla;
+ struct tabla_pdata *pdata;
+ int ret = 0;
+
+ pdata = slim->dev.platform_data;
+
+ if (!pdata) {
+ dev_err(&slim->dev, "Error, no platform data\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ tabla = kzalloc(sizeof(struct tabla), GFP_KERNEL);
+ if (tabla == NULL) {
+ pr_err("%s: error, allocation failed\n", __func__);
+ ret = -ENOMEM;
+ goto err;
+ }
+ if (!slim->ctrl) {
+ pr_err("Error, no SLIMBUS control data\n");
+ ret = -EINVAL;
+ goto err_tabla;
+ }
+ tabla->slim = slim;
+ slim_set_clientdata(slim, tabla);
+ tabla->reset_gpio = pdata->reset_gpio;
+ tabla->dev = &slim->dev;
+
+ ret = tabla_enable_supplies(tabla);
+ if (ret) {
+ pr_info("%s: Fail to enable Tabla supplies\n", __func__);
+ goto err_tabla;
+ }
+ usleep_range(5, 5);
+
+ ret = tabla_reset(tabla);
+ if (ret) {
+ pr_err("%s: Resetting Tabla failed\n", __func__);
+ goto err_supplies;
+ }
+
+ ret = slim_get_logical_addr(tabla->slim, tabla->slim->e_addr,
+ ARRAY_SIZE(tabla->slim->e_addr), &tabla->slim->laddr);
+ if (ret) {
+ pr_err("fail to get slimbus logical address %d\n", ret);
+ goto err_reset;
+ }
+ tabla->read_dev = tabla_slim_read_device;
+ tabla->write_dev = tabla_slim_write_device;
+ tabla->irq = pdata->irq;
+ tabla->irq_base = pdata->irq_base;
+ tabla_pgd_la = tabla->slim->laddr;
+
+ if (pdata->num_irqs < TABLA_NUM_IRQS) {
+ pr_err("%s: Error, not enough interrupt lines allocated\n",
+ __func__);
+ goto err_reset;
+ }
+
+ tabla->slim_slave = &pdata->slimbus_slave_device;
+
+ ret = slim_add_device(slim->ctrl, tabla->slim_slave);
+ if (ret) {
+ pr_err("%s: error, adding SLIMBUS device failed\n", __func__);
+ goto err_reset;
+ }
+
+ ret = slim_get_logical_addr(tabla->slim_slave,
+ tabla->slim_slave->e_addr,
+ ARRAY_SIZE(tabla->slim_slave->e_addr),
+ &tabla->slim_slave->laddr);
+ if (ret) {
+ pr_err("fail to get slimbus slave logical address %d\n", ret);
+ goto err_slim_add;
+ }
+ tabla_inf_la = tabla->slim_slave->laddr;
+
+ ret = tabla_device_init(tabla, tabla->irq);
+ if (ret) {
+ pr_err("%s: error, initializing device failed\n", __func__);
+ goto err_slim_add;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ debugTabla = tabla;
+
+ debugfs_tabla_dent = debugfs_create_dir
+ ("wcd9310_slimbus_interface_device", 0);
+ if (!IS_ERR(debugfs_tabla_dent)) {
+ debugfs_peek = debugfs_create_file("peek",
+ S_IFREG | S_IRUGO, debugfs_tabla_dent,
+ (void *) "peek", &codec_debug_ops);
+
+ debugfs_poke = debugfs_create_file("poke",
+ S_IFREG | S_IRUGO, debugfs_tabla_dent,
+ (void *) "poke", &codec_debug_ops);
+ }
+#endif
+
+
+ return ret;
+
+err_slim_add:
+ slim_remove_device(tabla->slim_slave);
+err_reset:
+ tabla_free_reset(tabla);
+err_supplies:
+ tabla_disable_supplies(tabla);
+err_tabla:
+ kfree(tabla);
+err:
+ return ret;
+}
+static int tabla_slim_remove(struct slim_device *pdev)
+{
+ struct tabla *tabla;
+
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove(debugfs_peek);
+ debugfs_remove(debugfs_poke);
+ debugfs_remove(debugfs_tabla_dent);
+#endif
+
+ tabla = slim_get_devicedata(pdev);
+ tabla_device_exit(tabla);
+ tabla_disable_supplies(tabla);
+ kfree(tabla);
+
+ return 0;
+}
+static const struct slim_device_id slimtest_id[] = {
+ {"tabla-slim", 0},
+ {}
+};
+static struct slim_driver tabla_slim_driver = {
+ .driver = {
+ .name = "tabla-slim",
+ .owner = THIS_MODULE,
+ },
+ .probe = tabla_slim_probe,
+ .remove = tabla_slim_remove,
+ .id_table = slimtest_id,
+};
+static int __init tabla_init(void)
+{
+ int ret;
+
+ ret = slim_driver_register(&tabla_slim_driver);
+ if (ret != 0) {
+ pr_err("Failed to register tabla SB driver: %d\n", ret);
+ goto err;
+ }
+err:
+ return ret;
+}
+module_init(tabla_init);
+
+static void __exit tabla_exit(void)
+{
+}
+module_exit(tabla_exit);
+
+MODULE_DESCRIPTION("Tabla core driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/wcd9310-irq.c b/drivers/mfd/wcd9310-irq.c
new file mode 100644
index 0000000..bc7841e
--- /dev/null
+++ b/drivers/mfd/wcd9310-irq.c
@@ -0,0 +1,197 @@
+/* Copyright (c) 2011, Code Aurora Forum. 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 and
+ * only 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.
+ */
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/wcd9310/core.h>
+#include <linux/mfd/wcd9310/registers.h>
+#include <linux/interrupt.h>
+
+#define BYTE_BIT_MASK(nr) (1UL << ((nr) % BITS_PER_BYTE))
+#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE)
+
+struct tabla_irq {
+ bool level;
+};
+
+static struct tabla_irq tabla_irqs[TABLA_NUM_IRQS] = {
+ [0] = { .level = 1},
+/* All other tabla interrupts are edge triggered */
+};
+
+static inline int irq_to_tabla_irq(struct tabla *tabla, int irq)
+{
+ return irq - tabla->irq_base;
+}
+
+static void tabla_irq_lock(struct irq_data *data)
+{
+ struct tabla *tabla = irq_data_get_irq_chip_data(data);
+ mutex_lock(&tabla->irq_lock);
+}
+
+static void tabla_irq_sync_unlock(struct irq_data *data)
+{
+ struct tabla *tabla = irq_data_get_irq_chip_data(data);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tabla->irq_masks_cur); i++) {
+ /* If there's been a change in the mask write it back
+ * to the hardware.
+ */
+ if (tabla->irq_masks_cur[i] != tabla->irq_masks_cache[i]) {
+ tabla->irq_masks_cache[i] = tabla->irq_masks_cur[i];
+ tabla_reg_write(tabla, TABLA_A_INTR_MASK0+i,
+ tabla->irq_masks_cur[i]);
+ }
+ }
+
+ mutex_unlock(&tabla->irq_lock);
+}
+
+static void tabla_irq_enable(struct irq_data *data)
+{
+ struct tabla *tabla = irq_data_get_irq_chip_data(data);
+ int tabla_irq = irq_to_tabla_irq(tabla, data->irq);
+ tabla->irq_masks_cur[BIT_BYTE(tabla_irq)] &=
+ ~(BYTE_BIT_MASK(tabla_irq));
+}
+
+static void tabla_irq_disable(struct irq_data *data)
+{
+ struct tabla *tabla = irq_data_get_irq_chip_data(data);
+ int tabla_irq = irq_to_tabla_irq(tabla, data->irq);
+ tabla->irq_masks_cur[BIT_BYTE(tabla_irq)] |= BYTE_BIT_MASK(tabla_irq);
+}
+
+static struct irq_chip tabla_irq_chip = {
+ .name = "tabla",
+ .irq_bus_lock = tabla_irq_lock,
+ .irq_bus_sync_unlock = tabla_irq_sync_unlock,
+ .irq_disable = tabla_irq_disable,
+ .irq_enable = tabla_irq_enable,
+};
+
+static irqreturn_t tabla_irq_thread(int irq, void *data)
+{
+ int ret;
+ struct tabla *tabla = data;
+ u8 status[TABLA_NUM_IRQ_REGS];
+ unsigned int i;
+
+ ret = tabla_bulk_read(tabla, TABLA_A_INTR_STATUS0,
+ TABLA_NUM_IRQ_REGS, status);
+ if (ret < 0) {
+ dev_err(tabla->dev, "Failed to read interrupt status: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+ /* Apply masking */
+ for (i = 0; i < TABLA_NUM_IRQ_REGS; i++)
+ status[i] &= ~tabla->irq_masks_cur[i];
+
+ /* Find out which interrupt was triggered and call that interrupt's
+ * handler function
+ */
+ for (i = 0; i < TABLA_NUM_IRQS; i++) {
+ if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i)) {
+ if ((i <= TABLA_IRQ_MBHC_INSERTION) &&
+ (i >= TABLA_IRQ_MBHC_REMOVAL)) {
+ tabla_reg_write(tabla, TABLA_A_INTR_CLEAR0 +
+ BIT_BYTE(i), BYTE_BIT_MASK(i));
+ handle_nested_irq(tabla->irq_base + i);
+ } else {
+ handle_nested_irq(tabla->irq_base + i);
+ tabla_reg_write(tabla, TABLA_A_INTR_CLEAR0 +
+ BIT_BYTE(i), BYTE_BIT_MASK(i));
+ }
+ break;
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+int tabla_irq_init(struct tabla *tabla)
+{
+ int ret;
+ unsigned int i, cur_irq;
+
+ mutex_init(&tabla->irq_lock);
+
+ if (!tabla->irq) {
+ dev_warn(tabla->dev,
+ "No interrupt specified, no interrupts\n");
+ tabla->irq_base = 0;
+ return 0;
+ }
+
+ if (!tabla->irq_base) {
+ dev_err(tabla->dev,
+ "No interrupt base specified, no interrupts\n");
+ return 0;
+ }
+ /* Mask the individual interrupt sources */
+ for (i = 0, cur_irq = tabla->irq_base; i < TABLA_NUM_IRQS; i++,
+ cur_irq++) {
+
+ irq_set_chip_data(cur_irq, tabla);
+
+ if (tabla_irqs[i].level)
+ irq_set_chip_and_handler(cur_irq, &tabla_irq_chip,
+ handle_level_irq);
+ else
+ irq_set_chip_and_handler(cur_irq, &tabla_irq_chip,
+ handle_edge_irq);
+
+ irq_set_nested_thread(cur_irq, 1);
+
+ /* ARM needs us to explicitly flag the IRQ as valid
+ * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+ set_irq_flags(cur_irq, IRQF_VALID);
+#else
+ set_irq_noprobe(cur_irq);
+#endif
+
+ tabla->irq_masks_cur[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
+ tabla->irq_masks_cache[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
+ tabla->irq_level[BIT_BYTE(i)] |= tabla_irqs[i].level <<
+ (i % BITS_PER_BYTE);
+ }
+ for (i = 0; i < TABLA_NUM_IRQ_REGS; i++) {
+ /* Initialize interrupt mask and level registers */
+ tabla_reg_write(tabla, TABLA_A_INTR_LEVEL0 + i,
+ tabla->irq_level[i]);
+ tabla_reg_write(tabla, TABLA_A_INTR_MASK0 + i,
+ tabla->irq_masks_cur[i]);
+ }
+
+ ret = request_threaded_irq(tabla->irq, NULL, tabla_irq_thread,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "tabla", tabla);
+
+ if (ret != 0) {
+ dev_err(tabla->dev, "Failed to request IRQ %d: %d\n",
+ tabla->irq, ret);
+ return ret;
+ }
+ return 0;
+}
+void tabla_irq_exit(struct tabla *tabla)
+{
+ if (tabla->irq)
+ free_irq(tabla->irq, tabla);
+ mutex_destroy(&tabla->irq_lock);
+}