Merge commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126' into msm-3.4
AU_LINUX_ANDROID_ICS.04.00.04.00.126 from msm-3.0.
First parent is from google/android-3.4.
* commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126': (8712 commits)
PRNG: Device tree entry for qrng device.
vidc:1080p: Set video core timeout value for Thumbnail mode
msm: sps: improve the debugging support in SPS driver
board-8064 msm: Overlap secure and non secure video firmware heaps.
msm: clock: Add handoff ops for 7x30 and copper XO clocks
msm_fb: display: Wait for external vsync before DTV IOMMU unmap
msm: Fix ciruclar dependency in debug UART settings
msm: gdsc: Add GDSC regulator driver for msm-copper
defconfig: Enable Mobicore Driver.
mobicore: Add mobicore driver.
mobicore: rename variable to lower case.
mobicore: rename folder.
mobicore: add makefiles
mobicore: initial import of kernel driver
ASoC: msm: Add SLIMBUS_2_RX CPU DAI
board-8064-gpio: Update FUNC for EPM SPI CS
msm_fb: display: Remove chicken bit config during video playback
mmc: msm_sdcc: enable the sanitize capability
msm-fb: display: lm2 writeback support on mpq platfroms
msm_fb: display: Disable LVDS phy & pll during panel off
...
Signed-off-by: Steve Muckle <smuckle@codeaurora.org>
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 11e4438..7c61bf3 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -131,6 +131,38 @@
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 MSM_SSBI
+ select MFD_CORE
+ select MFD_PM8XXX
+ help
+ Say yes here for Qualcomm PM8058 chip.
+
+config PMIC8901
+ tristate "PMIC8901 Power Management chip"
+ depends on MSM_SSBI
+ select MFD_CORE
+ select MFD_PM8XXX
+ 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
@@ -214,6 +246,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
@@ -822,6 +881,51 @@
Say M here if you want to include support for PM8921 chip as a module.
This will build a module called "pm8921-core".
+config MFD_PM8821_CORE
+ tristate "Qualcomm PM8821 PMIC chip"
+ depends on MSM_SSBI
+ select MFD_CORE
+ select MFD_PM8XXX
+ help
+ If you say yes to this option, support will be included for the
+ built-in PM8821 PMIC chip.
+
+ This is required if your board has a PM8821 and uses its features,
+ such as: MPPs, and interrupts.
+
+ Say M here if you want to include support for PM8821 chip as a module.
+ This will build a module called "pm8821-core".
+
+config MFD_PM8018_CORE
+ tristate "Qualcomm PM8018 PMIC chip"
+ depends on MSM_SSBI
+ select MFD_CORE
+ select MFD_PM8XXX
+ help
+ If you say yes to this option, support will be included for the
+ built-in PM8018 PMIC chip.
+
+ This is required if your board has a PM8018 and uses its features,
+ such as: MPPs, GPIOs, regulators, interrupts, and PWM.
+
+ Say M here if you want to include support for PM8018 chip as a module.
+ This will build a module called "pm8018-core".
+
+config MFD_PM8038_CORE
+ tristate "Qualcomm PM8038 PMIC chip"
+ depends on MSM_SSBI
+ select MFD_CORE
+ select MFD_PM8XXX
+ help
+ If you say yes to this option, support will be included for the
+ built-in PM8038 PMIC chip.
+
+ This is required if your board has a PM8038 and uses its features,
+ such as: MPPs, GPIOs, regulators, interrupts, and PWM.
+
+ Say M here if you want to include support for PM8038 chip as a module.
+ This will build a module called "pm8038-core".
+
config MFD_PM8XXX_IRQ
bool "Support for Qualcomm PM8xxx IRQ features"
depends on MFD_PM8XXX
@@ -866,6 +970,71 @@
Passage) chip. This chip embeds audio, battery, GPIO, etc.
devices used in Intel Medfield platforms.
+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_SPK
+ tristate "Support for Qualcomm PM8xxx speaker APIs"
+ depends on MFD_PM8XXX
+ help
+ This driver implements several external speaker amplifier 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 WCD9304_CODEC
+ tristate "WCD9304 Codec"
+ select SLIMBUS
+ select MFD_CORE
+ default n
+ help
+ Enables the WCD9304 core driver. The core driver provides
+ read/write capability to registers which are part of the
+ WCD9304 core and gives the ability to use the WCD9304 codec.
+
+config WCD9310_CODEC
+ tristate "WCD9310 Codec"
+ select 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.
+
config MFD_RC5T583
bool "Ricoh RC5T583 Power Management system device"
depends on I2C=y && GENERIC_HARDIRQS
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 05fa538..e81e4b1 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -44,12 +44,26 @@
obj-$(CONFIG_MFD_TPS65912) += tps65912.o
obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o
obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.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
@@ -64,6 +78,9 @@
obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o
obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o
+obj-$(CONFIG_WCD9310_CODEC) += wcd9xxx-core.o wcd9xxx-irq.o wcd9xxx-slimslave.o
+obj-$(CONFIG_WCD9304_CODEC) += wcd9xxx-core.o wcd9xxx-irq.o wcd9xxx-slimslave.o
+
ifeq ($(CONFIG_SA1100_ASSABET),y)
obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o
endif
@@ -107,12 +124,22 @@
obj-$(CONFIG_MFD_WL1273_CORE) += wl1273-core.o
obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o
obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o
-obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o
-obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o
obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
obj-$(CONFIG_MFD_TPS65090) += tps65090.o
obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
obj-$(CONFIG_MFD_S5M_CORE) += s5m-core.o s5m-irq.o
+obj-$(CONFIG_PMIC8058) += pmic8058.o
+obj-$(CONFIG_PMIC8901) += pmic8901.o
+obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o
+obj-$(CONFIG_MFD_PM8821_CORE) += pm8821-core.o
+obj-$(CONFIG_MFD_PM8018_CORE) += pm8018-core.o
+obj-$(CONFIG_MFD_PM8038_CORE) += pm8038-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_SPK) += pm8xxx-spk.o
+obj-$(CONFIG_MFD_PM8XXX_BATT_ALARM) += pm8xxx-batt-alarm.o
obj-$(CONFIG_MFD_ANATOP) += anatop-mfd.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..70ec2ec
--- /dev/null
+++ b/drivers/mfd/marimba-core.c
@@ -0,0 +1,937 @@
+/* Copyright (c) 2009-2012, 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>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+
+#define MARIMBA_MODE 0x00
+
+#define ADIE_ARRY_SIZE (CHIP_ID_MAX * MARIMBA_NUM_CHILD)
+
+static int marimba_shadow[ADIE_ARRY_SIZE][0xff];
+static int mutex_initialized;
+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
+
+#if defined(CONFIG_DEBUG_FS)
+struct adie_dbg_device {
+ struct mutex dbg_mutex;
+ struct dentry *dent;
+ int addr;
+ int mod_id;
+};
+
+static struct adie_dbg_device *marimba_dbg_device;
+static struct adie_dbg_device *timpani_dbg_device;
+static struct adie_dbg_device *bahama_dbg_device;
+#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];
+ if (marimba == NULL) {
+ pr_err("%s: Unable to access Marimba core\n", __func__);
+ return -ENODEV;
+ }
+
+
+ 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];
+ if (marimba->client == NULL) {
+ pr_err("%s: Unable to access the Marimba slave device.\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ 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");
+ ret = -ENODEV;
+ }
+
+ 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");
+ ret = -ENODEV;
+ }
+
+ 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);
+
+#if defined(CONFIG_DEBUG_FS)
+
+static int check_addr(int addr, const char *func_name)
+{
+ if (addr < 0 || addr > 0xFF) {
+ pr_err("%s: Marimba register address is invalid: %d\n",
+ func_name, addr);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int marimba_debugfs_set(void *data, u64 val)
+{
+ struct adie_dbg_device *dbgdev = data;
+ u8 reg = val;
+ int rc;
+ struct marimba marimba_id;
+
+ mutex_lock(&dbgdev->dbg_mutex);
+
+ rc = check_addr(dbgdev->addr, __func__);
+ if (rc)
+ goto done;
+
+ marimba_id.mod_id = dbgdev->mod_id;
+ rc = marimba_write(&marimba_id, dbgdev->addr, ®, 1);
+ rc = (rc == 1) ? 0 : rc;
+
+ if (rc)
+ pr_err("%s: FAIL marimba_write(0x%03X)=0x%02X: rc=%d\n",
+ __func__, dbgdev->addr, reg, rc);
+done:
+ mutex_unlock(&dbgdev->dbg_mutex);
+ return rc;
+}
+
+static int marimba_debugfs_get(void *data, u64 *val)
+{
+ struct adie_dbg_device *dbgdev = data;
+ int rc;
+ u8 reg;
+ struct marimba marimba_id;
+
+ mutex_lock(&dbgdev->dbg_mutex);
+
+ rc = check_addr(dbgdev->addr, __func__);
+ if (rc)
+ goto done;
+
+ marimba_id.mod_id = dbgdev->mod_id;
+ rc = marimba_read(&marimba_id, dbgdev->addr, ®, 1);
+ rc = (rc == 2) ? 0 : rc;
+
+ if (rc) {
+ pr_err("%s: FAIL marimba_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_marimba_fops, marimba_debugfs_get,
+ marimba_debugfs_set, "0x%02llX\n");
+
+static int addr_set(void *data, u64 val)
+{
+ struct adie_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 adie_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 marimba_dbg_init(int adie_type)
+{
+ struct adie_dbg_device *dbgdev;
+ struct dentry *dent;
+ struct dentry *temp;
+
+ dbgdev = kzalloc(sizeof *dbgdev, GFP_KERNEL);
+ if (dbgdev == NULL) {
+ pr_err("%s: kzalloc() failed.\n", __func__);
+ return -ENOMEM;
+ }
+
+ mutex_init(&dbgdev->dbg_mutex);
+ dbgdev->addr = -1;
+
+ if (adie_type == MARIMBA_ID) {
+ marimba_dbg_device = dbgdev;
+ marimba_dbg_device->mod_id = MARIMBA_SLAVE_ID_MARIMBA;
+ dent = debugfs_create_dir("marimba-dbg", NULL);
+ } else if (adie_type == TIMPANI_ID) {
+ timpani_dbg_device = dbgdev;
+ timpani_dbg_device->mod_id = MARIMBA_SLAVE_ID_MARIMBA;
+ dent = debugfs_create_dir("timpani-dbg", NULL);
+ } else if (adie_type == BAHAMA_ID) {
+ bahama_dbg_device = dbgdev;
+ bahama_dbg_device->mod_id = SLAVE_ID_BAHAMA;
+ dent = debugfs_create_dir("bahama-dbg", NULL);
+ }
+ if (dent == NULL || IS_ERR(dent)) {
+ pr_err("%s: ERR debugfs_create_dir: dent=0x%X\n",
+ __func__, (unsigned)dent);
+ kfree(dbgdev);
+ 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_marimba_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;
+
+ return 0;
+
+debug_error:
+ kfree(dbgdev);
+ debugfs_remove_recursive(dent);
+ return -ENOMEM;
+}
+
+static int __devexit marimba_dbg_remove(void)
+{
+ if (marimba_dbg_device) {
+ debugfs_remove_recursive(marimba_dbg_device->dent);
+ kfree(marimba_dbg_device);
+ }
+ if (timpani_dbg_device) {
+ debugfs_remove_recursive(timpani_dbg_device->dent);
+ kfree(timpani_dbg_device);
+ }
+ if (bahama_dbg_device) {
+ debugfs_remove_recursive(bahama_dbg_device->dent);
+ kfree(bahama_dbg_device);
+ }
+ return 0;
+}
+
+#else
+
+static int __devinit marimba_dbg_init(int adie_type)
+{
+ return 0;
+}
+
+static int __devexit marimba_dbg_remove(void)
+{
+ return 0;
+}
+
+#endif
+
+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 __devinit 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");
+ status = -EINVAL;
+ goto fail;
+ }
+
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
+ dev_dbg(&client->dev, "can't talk I2C?\n");
+ status = -EIO;
+ goto fail;
+ }
+ if (!mutex_initialized) {
+ for (i = 0; i < ADIE_ARRY_SIZE; ++i) {
+ marimba = &marimba_modules[i];
+ mutex_init(&marimba->xfer_lock);
+ }
+ mutex_initialized = 1;
+ }
+ /* 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) {
+ status = -EAGAIN;
+ goto fail;
+ }
+ marimba = &marimba_modules[ADIE_ARRY_SIZE - 1];
+ marimba->client = client;
+
+ rc = get_adie_type();
+
+ if (rc < 0) {
+ if (pdata->bahama_setup != NULL)
+ pdata->bahama_shutdown(cur_adie_type);
+ if (pdata->marimba_shutdown != NULL)
+ pdata->marimba_shutdown();
+ status = -ENODEV;
+ goto fail;
+ }
+
+ 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;
+
+ 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));
+
+ }
+
+ if (marimba_dbg_init(rc) != 0)
+ pr_debug("%s: marimba debugfs init failed\n", __func__);
+
+ 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 (mutex_initialized)
+ mutex_destroy(&marimba->xfer_lock);
+
+ }
+ marimba_dbg_remove();
+ mutex_initialized = 0;
+ 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/pm8018-core.c b/drivers/mfd/pm8018-core.c
new file mode 100644
index 0000000..b1b64cb
--- /dev/null
+++ b/drivers/mfd/pm8018-core.c
@@ -0,0 +1,674 @@
+/*
+ * Copyright (c) 2011-2012, 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/err.h>
+#include <linux/msm_ssbi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/pm8xxx/pm8018.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/regulator.h>
+#include <linux/leds-pm8xxx.h>
+
+
+/* PMIC PM8018 SSBI Addresses */
+#define REG_HWREV 0x002 /* PMIC4 revision */
+#define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */
+
+#define REG_MPP_BASE 0x050
+#define REG_IRQ_BASE 0x1BB
+
+#define REG_RTC_BASE 0x11D
+
+#define REG_TEMP_ALARM_CTRL 0x01B
+#define REG_TEMP_ALARM_PWM 0x09B
+
+
+#define PM8018_VERSION_MASK 0xFFF0
+#define PM8018_VERSION_VALUE 0x08F0
+#define PM8018_REVISION_MASK 0x000F
+
+#define REG_PM8018_PON_CNTRL_3 0x01D
+#define PM8018_RESTART_REASON_MASK 0x07
+
+#define SINGLE_IRQ_RESOURCE(_name, _irq) \
+{ \
+ .name = _name, \
+ .start = _irq, \
+ .end = _irq, \
+ .flags = IORESOURCE_IRQ, \
+}
+
+struct pm8018 {
+ struct device *dev;
+ struct pm_irq_chip *irq_chip;
+ struct mfd_cell *mfd_regulators;
+ struct pm8xxx_regulator_core_platform_data *regulator_cdata;
+ u32 rev_registers;
+};
+
+static int pm8018_readb(const struct device *dev, u16 addr, u8 *val)
+{
+ const struct pm8xxx_drvdata *pm8018_drvdata = dev_get_drvdata(dev);
+ const struct pm8018 *pmic = pm8018_drvdata->pm_chip_data;
+
+ return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
+}
+
+static int pm8018_writeb(const struct device *dev, u16 addr, u8 val)
+{
+ const struct pm8xxx_drvdata *pm8018_drvdata = dev_get_drvdata(dev);
+ const struct pm8018 *pmic = pm8018_drvdata->pm_chip_data;
+
+ return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
+}
+
+static int pm8018_read_buf(const struct device *dev, u16 addr, u8 *buf,
+ int cnt)
+{
+ const struct pm8xxx_drvdata *pm8018_drvdata = dev_get_drvdata(dev);
+ const struct pm8018 *pmic = pm8018_drvdata->pm_chip_data;
+
+ return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8018_write_buf(const struct device *dev, u16 addr, u8 *buf,
+ int cnt)
+{
+ const struct pm8xxx_drvdata *pm8018_drvdata = dev_get_drvdata(dev);
+ const struct pm8018 *pmic = pm8018_drvdata->pm_chip_data;
+
+ return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8018_read_irq_stat(const struct device *dev, int irq)
+{
+ const struct pm8xxx_drvdata *pm8018_drvdata = dev_get_drvdata(dev);
+ const struct pm8018 *pmic = pm8018_drvdata->pm_chip_data;
+
+ return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
+}
+
+static enum pm8xxx_version pm8018_get_version(const struct device *dev)
+{
+ const struct pm8xxx_drvdata *pm8018_drvdata = dev_get_drvdata(dev);
+ const struct pm8018 *pmic = pm8018_drvdata->pm_chip_data;
+ enum pm8xxx_version version = -ENODEV;
+
+ if ((pmic->rev_registers & PM8018_VERSION_MASK) == PM8018_VERSION_VALUE)
+ version = PM8XXX_VERSION_8018;
+
+ return version;
+}
+
+static int pm8018_get_revision(const struct device *dev)
+{
+ const struct pm8xxx_drvdata *pm8018_drvdata = dev_get_drvdata(dev);
+ const struct pm8018 *pmic = pm8018_drvdata->pm_chip_data;
+
+ return pmic->rev_registers & PM8018_REVISION_MASK;
+}
+
+static struct pm8xxx_drvdata pm8018_drvdata = {
+ .pmic_readb = pm8018_readb,
+ .pmic_writeb = pm8018_writeb,
+ .pmic_read_buf = pm8018_read_buf,
+ .pmic_write_buf = pm8018_write_buf,
+ .pmic_read_irq_stat = pm8018_read_irq_stat,
+ .pmic_get_version = pm8018_get_version,
+ .pmic_get_revision = pm8018_get_revision,
+};
+
+static const struct resource gpio_cell_resources[] __devinitconst = {
+ [0] = {
+ .start = PM8018_IRQ_BLOCK_BIT(PM8018_GPIO_BLOCK_START, 0),
+ .end = PM8018_IRQ_BLOCK_BIT(PM8018_GPIO_BLOCK_START, 0)
+ + PM8018_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, PM8018_ADC_EOC_USR_IRQ),
+ SINGLE_IRQ_RESOURCE(NULL, PM8018_ADC_BATT_TEMP_WARM_IRQ),
+ SINGLE_IRQ_RESOURCE(NULL, PM8018_ADC_BATT_TEMP_COLD_IRQ),
+};
+
+static struct mfd_cell adc_cell __devinitdata = {
+ .name = PM8XXX_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 = PM8018_IRQ_BLOCK_BIT(PM8018_MPP_BLOCK_START, 0),
+ .end = PM8018_IRQ_BLOCK_BIT(PM8018_MPP_BLOCK_START, 0)
+ + PM8018_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, PM8018_RTC_ALARM_IRQ),
+ [1] = {
+ .name = "pmic_rtc_base",
+ .start = REG_RTC_BASE,
+ .end = REG_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, PM8018_PWRKEY_REL_IRQ),
+ SINGLE_IRQ_RESOURCE(NULL, PM8018_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 struct mfd_cell misc_cell __devinitdata = {
+ .name = PM8XXX_MISC_DEV_NAME,
+ .id = -1,
+};
+
+static struct mfd_cell debugfs_cell __devinitdata = {
+ .name = "pm8xxx-debug",
+ .id = -1,
+ .platform_data = "pm8018-dbg",
+ .pdata_size = sizeof("pm8018-dbg"),
+};
+
+static struct mfd_cell pwm_cell __devinitdata = {
+ .name = PM8XXX_PWM_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("pm8018_tempstat_irq", PM8018_TEMPSTAT_IRQ),
+ SINGLE_IRQ_RESOURCE("pm8018_overtemp_irq", PM8018_OVERTEMP_IRQ),
+};
+
+static struct pm8xxx_tm_core_data thermal_alarm_cdata = {
+ .adc_channel = CHANNEL_DIE_TEMP,
+ .adc_type = PM8XXX_TM_ADC_PM8XXX_ADC,
+ .reg_addr_temp_alarm_ctrl = REG_TEMP_ALARM_CTRL,
+ .reg_addr_temp_alarm_pwm = REG_TEMP_ALARM_PWM,
+ .tm_name = "pm8018_tz",
+ .irq_name_temp_stat = "pm8018_tempstat_irq",
+ .irq_name_over_temp = "pm8018_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 struct pm8xxx_vreg regulator_data[] = {
+ /* name pc_name ctrl test hpm_min */
+ PLDO("8018_l2", "8018_l2_pc", 0x0B0, 0x0B1, LDO_50),
+ PLDO("8018_l3", "8018_l3_pc", 0x0B2, 0x0B3, LDO_50),
+ PLDO("8018_l4", "8018_l4_pc", 0x0B4, 0x0B5, LDO_300),
+ PLDO("8018_l5", "8018_l5_pc", 0x0B6, 0x0B7, LDO_150),
+ PLDO("8018_l6", "8018_l6_pc", 0x0B8, 0x0B9, LDO_150),
+ PLDO("8018_l7", "8018_l7_pc", 0x0BA, 0x0BB, LDO_300),
+ NLDO("8018_l8", "8018_l8_pc", 0x0BC, 0x0BD, LDO_150),
+ NLDO1200("8018_l9", 0x0BE, 0x0BF, LDO_1200),
+ NLDO1200("8018_l10", 0x0C0, 0x0C1, LDO_1200),
+ NLDO1200("8018_l11", 0x0C2, 0x0C3, LDO_1200),
+ NLDO1200("8018_l12", 0x0C4, 0x0C5, LDO_1200),
+ PLDO("8018_l13", "8018_l13_pc", 0x0C8, 0x0C9, LDO_50),
+ PLDO("8018_l14", "8018_l14_pc", 0x0CA, 0x0CB, LDO_50),
+
+ /* name pc_name ctrl test2 clk sleep hpm_min */
+ SMPS("8018_s1", "8018_s1_pc", 0x1D0, 0x1D5, 0x009, 0x1D2, SMPS_1500),
+ SMPS("8018_s2", "8018_s2_pc", 0x1D8, 0x1DD, 0x00A, 0x1DA, SMPS_1500),
+ SMPS("8018_s3", "8018_s3_pc", 0x1E0, 0x1E5, 0x00B, 0x1E2, SMPS_1500),
+ SMPS("8018_s4", "8018_s4_pc", 0x1E8, 0x1ED, 0x00C, 0x1EA, SMPS_1500),
+ SMPS("8018_s5", "8018_s5_pc", 0x1F0, 0x1F5, 0x00D, 0x1F2, SMPS_1500),
+
+ /* name pc_name ctrl test */
+ VS("8018_lvs1", "8018_lvs1_pc", 0x060, 0x061),
+};
+
+#define MAX_NAME_COMPARISON_LEN 32
+
+static int __devinit match_regulator(
+ struct pm8xxx_regulator_core_platform_data *core_data, const char *name)
+{
+ int found = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(regulator_data); i++) {
+ if (regulator_data[i].rdesc.name
+ && strncmp(regulator_data[i].rdesc.name, name,
+ MAX_NAME_COMPARISON_LEN) == 0) {
+ core_data->is_pin_controlled = false;
+ core_data->vreg = ®ulator_data[i];
+ found = 1;
+ break;
+ } else if (regulator_data[i].rdesc_pc.name
+ && strncmp(regulator_data[i].rdesc_pc.name, name,
+ MAX_NAME_COMPARISON_LEN) == 0) {
+ core_data->is_pin_controlled = true;
+ core_data->vreg = ®ulator_data[i];
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ pr_err("could not find a match for regulator: %s\n", name);
+
+ return found;
+}
+
+static int __devinit
+pm8018_add_regulators(const struct pm8018_platform_data *pdata,
+ struct pm8018 *pmic, int irq_base)
+{
+ int ret = 0;
+ struct mfd_cell *mfd_regulators;
+ struct pm8xxx_regulator_core_platform_data *cdata;
+ int i;
+
+ /* Add one device for each regulator used by the board. */
+ mfd_regulators = kzalloc(sizeof(struct mfd_cell)
+ * (pdata->num_regulators), GFP_KERNEL);
+ if (!mfd_regulators) {
+ pr_err("Cannot allocate %d bytes for pm8018 regulator "
+ "mfd cells\n", sizeof(struct mfd_cell)
+ * (pdata->num_regulators));
+ return -ENOMEM;
+ }
+ cdata = kzalloc(sizeof(struct pm8xxx_regulator_core_platform_data)
+ * pdata->num_regulators, GFP_KERNEL);
+ if (!cdata) {
+ pr_err("Cannot allocate %d bytes for pm8018 regulator "
+ "core data\n", pdata->num_regulators
+ * sizeof(struct pm8xxx_regulator_core_platform_data));
+ kfree(mfd_regulators);
+ return -ENOMEM;
+ }
+ for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+ mutex_init(®ulator_data[i].pc_lock);
+
+ for (i = 0; i < pdata->num_regulators; i++) {
+ if (!pdata->regulator_pdatas[i].init_data.constraints.name) {
+ pr_err("name missing for regulator %d\n", i);
+ ret = -EINVAL;
+ goto bail;
+ }
+ if (!match_regulator(&cdata[i],
+ pdata->regulator_pdatas[i].init_data.constraints.name)) {
+ ret = -ENODEV;
+ goto bail;
+ }
+ cdata[i].pdata = &(pdata->regulator_pdatas[i]);
+ mfd_regulators[i].name = PM8XXX_REGULATOR_DEV_NAME;
+ mfd_regulators[i].id = cdata[i].pdata->id;
+ mfd_regulators[i].platform_data = &cdata[i];
+ mfd_regulators[i].pdata_size =
+ sizeof(struct pm8xxx_regulator_core_platform_data);
+ }
+ ret = mfd_add_devices(pmic->dev, 0, mfd_regulators,
+ pdata->num_regulators, NULL, irq_base);
+ if (ret)
+ goto bail;
+
+ pmic->mfd_regulators = mfd_regulators;
+ pmic->regulator_cdata = cdata;
+ return ret;
+
+bail:
+ for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+ mutex_destroy(®ulator_data[i].pc_lock);
+ kfree(mfd_regulators);
+ kfree(cdata);
+ return ret;
+}
+
+static int __devinit
+pm8018_add_subdevices(const struct pm8018_platform_data *pdata,
+ struct pm8018 *pmic)
+{
+ int ret = 0, irq_base = 0;
+ struct pm_irq_chip *irq_chip;
+
+ if (pdata->irq_pdata) {
+ pdata->irq_pdata->irq_cdata.nirqs = PM8018_NR_IRQS;
+ pdata->irq_pdata->irq_cdata.base_addr = REG_IRQ_BASE;
+ irq_base = pdata->irq_pdata->irq_base;
+ irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
+
+ if (IS_ERR(irq_chip)) {
+ pr_err("Failed to init interrupts ret=%ld\n",
+ PTR_ERR(irq_chip));
+ return PTR_ERR(irq_chip);
+ }
+ pmic->irq_chip = irq_chip;
+ }
+
+ if (pdata->gpio_pdata) {
+ pdata->gpio_pdata->gpio_cdata.ngpios = PM8018_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 = PM8018_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->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->adc_pdata) {
+ adc_cell.platform_data = pdata->adc_pdata;
+ adc_cell.pdata_size = sizeof(struct pm8xxx_adc_platform_data);
+ ret = mfd_add_devices(pmic->dev, 0, &adc_cell, 1, NULL,
+ irq_base);
+ if (ret) {
+ pr_err("Failed to add adc subdevice ret=%d\n", ret);
+ }
+ }
+
+ if (pdata->leds_pdata) {
+ leds_cell.platform_data = pdata->leds_pdata;
+ leds_cell.pdata_size = sizeof(struct pm8xxx_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, &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->num_regulators > 0 && pdata->regulator_pdatas) {
+ ret = pm8018_add_regulators(pdata, pmic, irq_base);
+ if (ret) {
+ pr_err("Failed to add regulator subdevices 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;
+ }
+
+ return 0;
+bail:
+ if (pmic->irq_chip) {
+ pm8xxx_irq_exit(pmic->irq_chip);
+ pmic->irq_chip = NULL;
+ }
+ return ret;
+}
+
+static const char * const pm8018_restart_reason[] = {
+ [0] = "Unknown",
+ [1] = "Triggered from CBL (external charger)",
+ [2] = "Triggered from KPD (power key press)",
+ [3] = "Triggered from CHG (usb charger insertion)",
+ [4] = "Triggered from SMPL (sudden momentary power loss)",
+ [5] = "Triggered from RTC (real time clock)",
+ [6] = "Triggered by Hard Reset",
+ [7] = "Triggered by General Purpose Trigger",
+};
+
+static const char * const pm8018_rev_names[] = {
+ [PM8XXX_REVISION_8018_TEST] = "test",
+ [PM8XXX_REVISION_8018_1p0] = "1.0",
+ [PM8XXX_REVISION_8018_2p0] = "2.0",
+ [PM8XXX_REVISION_8018_2p1] = "2.1",
+};
+
+static int __devinit pm8018_probe(struct platform_device *pdev)
+{
+ const struct pm8018_platform_data *pdata = pdev->dev.platform_data;
+ const char *revision_name = "unknown";
+ struct pm8018 *pmic;
+ enum pm8xxx_version version;
+ int revision;
+ int rc;
+ u8 val;
+
+ if (!pdata) {
+ pr_err("missing platform data\n");
+ return -EINVAL;
+ }
+
+ pmic = kzalloc(sizeof(struct pm8018), GFP_KERNEL);
+ if (!pmic) {
+ pr_err("Cannot alloc pm8018 struct\n");
+ return -ENOMEM;
+ }
+
+ /* Read PMIC chip revision */
+ rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val));
+ if (rc) {
+ pr_err("Failed to read hw rev 1 reg %d:rc=%d\n", REG_HWREV, rc);
+ goto err_read_rev;
+ }
+ pr_info("PMIC revision 1: %02X\n", val);
+ pmic->rev_registers = val;
+
+ /* Read PMIC chip revision 2 */
+ rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val));
+ if (rc) {
+ pr_err("Failed to read hw rev 2 reg %d:rc=%d\n", REG_HWREV_2,
+ rc);
+ goto err_read_rev;
+ }
+ pr_info("PMIC revision 2: %02X\n", val);
+ pmic->rev_registers |= val << BITS_PER_BYTE;
+
+ pmic->dev = &pdev->dev;
+ pm8018_drvdata.pm_chip_data = pmic;
+ platform_set_drvdata(pdev, &pm8018_drvdata);
+
+ /* Print out human readable version and revision names. */
+ version = pm8xxx_get_version(pmic->dev);
+ if (version == PM8XXX_VERSION_8018) {
+ revision = pm8xxx_get_revision(pmic->dev);
+ if (revision >= 0 && revision < ARRAY_SIZE(pm8018_rev_names))
+ revision_name = pm8018_rev_names[revision];
+ pr_info("PMIC version: PM8018 rev %s\n", revision_name);
+ } else {
+ WARN_ON(version != PM8XXX_VERSION_8018);
+ }
+ /* Log human readable restart reason */
+ rc = msm_ssbi_read(pdev->dev.parent, REG_PM8018_PON_CNTRL_3, &val, 1);
+ if (rc) {
+ pr_err("Cannot read restart reason rc=%d\n", rc);
+ goto err_read_rev;
+ }
+ val &= PM8018_RESTART_REASON_MASK;
+ pr_info("PMIC Restart Reason: %s\n", pm8018_restart_reason[val]);
+
+ rc = pm8018_add_subdevices(pdata, pmic);
+ if (rc) {
+ pr_err("Cannot add subdevices rc=%d\n", rc);
+ goto err;
+ }
+
+ /* gpio might not work if no irq device is found */
+ WARN_ON(pmic->irq_chip == NULL);
+
+ return 0;
+
+err:
+ mfd_remove_devices(pmic->dev);
+ platform_set_drvdata(pdev, NULL);
+ kfree(pmic->mfd_regulators);
+ kfree(pmic->regulator_cdata);
+err_read_rev:
+ kfree(pmic);
+ return rc;
+}
+
+static int __devexit pm8018_remove(struct platform_device *pdev)
+{
+ struct pm8xxx_drvdata *drvdata;
+ struct pm8018 *pmic = NULL;
+ int i;
+
+ drvdata = platform_get_drvdata(pdev);
+ if (drvdata)
+ pmic = drvdata->pm_chip_data;
+ if (pmic) {
+ if (pmic->dev)
+ mfd_remove_devices(pmic->dev);
+ if (pmic->irq_chip) {
+ pm8xxx_irq_exit(pmic->irq_chip);
+ pmic->irq_chip = NULL;
+ }
+ if (pmic->mfd_regulators) {
+ for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+ mutex_destroy(®ulator_data[i].pc_lock);
+ }
+ kfree(pmic->mfd_regulators);
+ kfree(pmic->regulator_cdata);
+ kfree(pmic);
+ }
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver pm8018_driver = {
+ .probe = pm8018_probe,
+ .remove = __devexit_p(pm8018_remove),
+ .driver = {
+ .name = PM8018_CORE_DEV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pm8018_init(void)
+{
+ return platform_driver_register(&pm8018_driver);
+}
+postcore_initcall(pm8018_init);
+
+static void __exit pm8018_exit(void)
+{
+ platform_driver_unregister(&pm8018_driver);
+}
+module_exit(pm8018_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC 8018 core driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8018_CORE_DEV_NAME);
diff --git a/drivers/mfd/pm8038-core.c b/drivers/mfd/pm8038-core.c
new file mode 100644
index 0000000..4271a2a
--- /dev/null
+++ b/drivers/mfd/pm8038-core.c
@@ -0,0 +1,775 @@
+/*
+ * Copyright (c) 2011-2012, 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/msm_ssbi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/pm8xxx/pm8038.h>
+#include <linux/mfd/pm8xxx/pm8921.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/regulator.h>
+
+#define REG_HWREV 0x002 /* PMIC4 revision */
+#define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */
+
+#define REG_MPP_BASE 0x050
+#define REG_RTC_BASE 0x11D
+#define REG_IRQ_BASE 0x1BB
+
+#define REG_SPK_BASE 0x253
+#define REG_SPK_REGISTERS 3
+
+#define PM8038_VERSION_MASK 0xFFF0
+#define PM8038_VERSION_VALUE 0x09F0
+#define PM8038_REVISION_MASK 0x000F
+
+#define REG_PM8038_PON_CNTRL_3 0x01D
+#define PM8038_RESTART_REASON_MASK 0x07
+
+#define SINGLE_IRQ_RESOURCE(_name, _irq) \
+{ \
+ .name = _name, \
+ .start = _irq, \
+ .end = _irq, \
+ .flags = IORESOURCE_IRQ, \
+}
+
+struct pm8038 {
+ struct device *dev;
+ struct pm_irq_chip *irq_chip;
+ struct mfd_cell *mfd_regulators;
+ struct pm8xxx_regulator_core_platform_data *regulator_cdata;
+ u32 rev_registers;
+};
+
+static int pm8038_readb(const struct device *dev, u16 addr, u8 *val)
+{
+ const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
+ const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
+
+ return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
+}
+
+static int pm8038_writeb(const struct device *dev, u16 addr, u8 val)
+{
+ const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
+ const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
+
+ return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
+}
+
+static int pm8038_read_buf(const struct device *dev, u16 addr, u8 *buf,
+ int cnt)
+{
+ const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
+ const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
+
+ return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8038_write_buf(const struct device *dev, u16 addr, u8 *buf,
+ int cnt)
+{
+ const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
+ const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
+
+ return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8038_read_irq_stat(const struct device *dev, int irq)
+{
+ const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
+ const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
+
+ return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
+}
+
+static enum pm8xxx_version pm8038_get_version(const struct device *dev)
+{
+ const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
+ const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
+ enum pm8xxx_version version = -ENODEV;
+
+ if ((pmic->rev_registers & PM8038_VERSION_MASK) == PM8038_VERSION_VALUE)
+ version = PM8XXX_VERSION_8038;
+
+ return version;
+}
+
+static int pm8038_get_revision(const struct device *dev)
+{
+ const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
+ const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
+
+ return pmic->rev_registers & PM8038_REVISION_MASK;
+}
+
+static struct pm8xxx_drvdata pm8038_drvdata = {
+ .pmic_readb = pm8038_readb,
+ .pmic_writeb = pm8038_writeb,
+ .pmic_read_buf = pm8038_read_buf,
+ .pmic_write_buf = pm8038_write_buf,
+ .pmic_read_irq_stat = pm8038_read_irq_stat,
+ .pmic_get_version = pm8038_get_version,
+ .pmic_get_revision = pm8038_get_revision,
+};
+
+static const struct resource gpio_cell_resources[] __devinitconst = {
+ [0] = {
+ .start = PM8038_IRQ_BLOCK_BIT(PM8038_GPIO_BLOCK_START, 0),
+ .end = PM8038_IRQ_BLOCK_BIT(PM8038_GPIO_BLOCK_START, 0)
+ + PM8038_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, PM8038_ADC_EOC_USR_IRQ),
+ SINGLE_IRQ_RESOURCE(NULL, PM8038_ADC_BATT_TEMP_WARM_IRQ),
+ SINGLE_IRQ_RESOURCE(NULL, PM8038_ADC_BATT_TEMP_COLD_IRQ),
+};
+
+static struct mfd_cell adc_cell __devinitdata = {
+ .name = PM8XXX_ADC_DEV_NAME,
+ .id = -1,
+ .resources = adc_cell_resources,
+ .num_resources = ARRAY_SIZE(adc_cell_resources),
+};
+
+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 const struct resource mpp_cell_resources[] __devinitconst = {
+ {
+ .start = PM8038_IRQ_BLOCK_BIT(PM8038_MPP_BLOCK_START, 0),
+ .end = PM8038_IRQ_BLOCK_BIT(PM8038_MPP_BLOCK_START, 0)
+ + PM8038_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, PM8038_RTC_ALARM_IRQ),
+ [1] = {
+ .name = "pmic_rtc_base",
+ .start = REG_RTC_BASE,
+ .end = REG_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, PM8038_PWRKEY_REL_IRQ),
+ SINGLE_IRQ_RESOURCE(NULL, PM8038_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 struct mfd_cell pwm_cell __devinitdata = {
+ .name = PM8XXX_PWM_DEV_NAME,
+ .id = -1,
+};
+
+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 resources_spk[] __devinitconst = {
+ [0] = {
+ .name = PM8XXX_SPK_DEV_NAME,
+ .start = REG_SPK_BASE,
+ .end = REG_SPK_BASE + REG_SPK_REGISTERS,
+ .flags = IORESOURCE_IO,
+ },
+};
+
+static struct mfd_cell spk_cell __devinitdata = {
+ .name = PM8XXX_SPK_DEV_NAME,
+ .id = -1,
+ .num_resources = ARRAY_SIZE(resources_spk),
+ .resources = resources_spk,
+};
+
+static struct mfd_cell debugfs_cell __devinitdata = {
+ .name = "pm8xxx-debug",
+ .id = 0,
+ .platform_data = "pm8038-dbg",
+ .pdata_size = sizeof("pm8038-dbg"),
+};
+
+static struct pm8xxx_vreg regulator_data[] = {
+ /* name pc_name ctrl test hpm_min */
+ NLDO1200("8038_l1", 0x0AE, 0x0AF, LDO_1200),
+ NLDO("8038_l2", "8038_l2_pc", 0x0B0, 0x0B1, LDO_150),
+ PLDO("8038_l3", "8038_l3_pc", 0x0B2, 0x0B3, LDO_50),
+ PLDO("8038_l4", "8038_l4_pc", 0x0B4, 0x0B5, LDO_50),
+ PLDO("8038_l5", "8038_l5_pc", 0x0B6, 0x0B7, LDO_600),
+ PLDO("8038_l6", "8038_l6_pc", 0x0B8, 0x0B9, LDO_600),
+ PLDO("8038_l7", "8038_l7_pc", 0x0BA, 0x0BB, LDO_600),
+ PLDO("8038_l8", "8038_l8_pc", 0x0BC, 0x0BD, LDO_300),
+ PLDO("8038_l9", "8038_l9_pc", 0x0BE, 0x0BF, LDO_300),
+ PLDO("8038_l10", "8038_l10_pc", 0x0C0, 0x0C1, LDO_600),
+ PLDO("8038_l11", "8038_l11_pc", 0x0C2, 0x0C3, LDO_600),
+ NLDO("8038_l12", "8038_l12_pc", 0x0C4, 0x0C5, LDO_300),
+ PLDO("8038_l14", "8038_l14_pc", 0x0C8, 0x0C9, LDO_50),
+ PLDO("8038_l15", "8038_l15_pc", 0x0CA, 0x0CB, LDO_150),
+ NLDO1200("8038_l16", 0x0CC, 0x0CD, LDO_1200),
+ PLDO("8038_l17", "8038_l17_pc", 0x0CE, 0x0CF, LDO_150),
+ PLDO("8038_l18", "8038_l18_pc", 0x0D0, 0x0D1, LDO_50),
+ NLDO1200("8038_l19", 0x0D2, 0x0D3, LDO_1200),
+ NLDO1200("8038_l20", 0x0D4, 0x0D5, LDO_1200),
+ PLDO("8038_l21", "8038_l21_pc", 0x0D6, 0x0D7, LDO_150),
+ PLDO("8038_l22", "8038_l22_pc", 0x0D8, 0x0D9, LDO_50),
+ PLDO("8038_l23", "8038_l23_pc", 0x0DA, 0x0DB, LDO_50),
+ NLDO1200("8038_l24", 0x0DC, 0x0DD, LDO_1200),
+ NLDO("8038_l26", "8038_l26_pc", 0x0E0, 0x0E1, LDO_150),
+ NLDO1200("8038_l27", 0x0E2, 0x0E3, LDO_1200),
+
+ /* name pc_name ctrl test2 clk sleep hpm_min */
+ SMPS("8038_s1", "8038_s1_pc", 0x1E0, 0x1E5, 0x009, 0x1E2, SMPS_1500),
+ SMPS("8038_s2", "8038_s2_pc", 0x1D8, 0x1DD, 0x00A, 0x1DA, SMPS_1500),
+ SMPS("8038_s3", "8038_s3_pc", 0x1D0, 0x1D5, 0x00B, 0x1D2, SMPS_1500),
+ SMPS("8038_s4", "8038_s4_pc", 0x1E8, 0x1ED, 0x00C, 0x1EA, SMPS_1500),
+
+ /* name ctrl fts_cnfg1 pfm pwr_cnfg hpm_min */
+ FTSMPS("8038_s5", 0x025, 0x02E, 0x026, 0x032, SMPS_2000),
+ FTSMPS("8038_s6", 0x036, 0x03F, 0x037, 0x043, SMPS_2000),
+
+ /* name pc_name ctrl test */
+ VS("8038_lvs1", "8038_lvs1_pc", 0x060, 0x061),
+ VS("8038_lvs2", "8038_lvs2_pc", 0x062, 0x063),
+};
+
+#define MAX_NAME_COMPARISON_LEN 32
+
+static int __devinit match_regulator(
+ struct pm8xxx_regulator_core_platform_data *core_data, const char *name)
+{
+ int found = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(regulator_data); i++) {
+ if (regulator_data[i].rdesc.name
+ && strncmp(regulator_data[i].rdesc.name, name,
+ MAX_NAME_COMPARISON_LEN) == 0) {
+ core_data->is_pin_controlled = false;
+ core_data->vreg = ®ulator_data[i];
+ found = 1;
+ break;
+ } else if (regulator_data[i].rdesc_pc.name
+ && strncmp(regulator_data[i].rdesc_pc.name, name,
+ MAX_NAME_COMPARISON_LEN) == 0) {
+ core_data->is_pin_controlled = true;
+ core_data->vreg = ®ulator_data[i];
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ pr_err("could not find a match for regulator: %s\n", name);
+
+ return found;
+}
+
+static int __devinit
+pm8038_add_regulators(const struct pm8038_platform_data *pdata,
+ struct pm8038 *pmic, int irq_base)
+{
+ int ret = 0;
+ struct mfd_cell *mfd_regulators;
+ struct pm8xxx_regulator_core_platform_data *cdata;
+ int i;
+
+ /* Add one device for each regulator used by the board. */
+ mfd_regulators = kzalloc(sizeof(struct mfd_cell)
+ * (pdata->num_regulators), GFP_KERNEL);
+ if (!mfd_regulators) {
+ pr_err("Cannot allocate %d bytes for pm8038 regulator "
+ "mfd cells\n", sizeof(struct mfd_cell)
+ * (pdata->num_regulators));
+ return -ENOMEM;
+ }
+ cdata = kzalloc(sizeof(struct pm8xxx_regulator_core_platform_data)
+ * pdata->num_regulators, GFP_KERNEL);
+ if (!cdata) {
+ pr_err("Cannot allocate %d bytes for pm8038 regulator "
+ "core data\n", pdata->num_regulators
+ * sizeof(struct pm8xxx_regulator_core_platform_data));
+ kfree(mfd_regulators);
+ return -ENOMEM;
+ }
+ for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+ mutex_init(®ulator_data[i].pc_lock);
+
+ for (i = 0; i < pdata->num_regulators; i++) {
+ if (!pdata->regulator_pdatas[i].init_data.constraints.name) {
+ pr_err("name missing for regulator %d\n", i);
+ ret = -EINVAL;
+ goto bail;
+ }
+ if (!match_regulator(&cdata[i],
+ pdata->regulator_pdatas[i].init_data.constraints.name)) {
+ ret = -ENODEV;
+ goto bail;
+ }
+ cdata[i].pdata = &(pdata->regulator_pdatas[i]);
+ mfd_regulators[i].name = PM8XXX_REGULATOR_DEV_NAME;
+ mfd_regulators[i].id = cdata[i].pdata->id;
+ mfd_regulators[i].platform_data = &cdata[i];
+ mfd_regulators[i].pdata_size =
+ sizeof(struct pm8xxx_regulator_core_platform_data);
+ }
+ ret = mfd_add_devices(pmic->dev, 0, mfd_regulators,
+ pdata->num_regulators, NULL, irq_base);
+ if (ret)
+ goto bail;
+
+ pmic->mfd_regulators = mfd_regulators;
+ pmic->regulator_cdata = cdata;
+ return ret;
+
+bail:
+ for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+ mutex_destroy(®ulator_data[i].pc_lock);
+ kfree(mfd_regulators);
+ kfree(cdata);
+ return ret;
+}
+
+static int __devinit
+pm8038_add_subdevices(const struct pm8038_platform_data *pdata,
+ struct pm8038 *pmic)
+{
+ int ret = 0, irq_base = 0;
+ struct pm_irq_chip *irq_chip;
+
+ if (pdata->irq_pdata) {
+ pdata->irq_pdata->irq_cdata.nirqs = PM8038_NR_IRQS;
+ pdata->irq_pdata->irq_cdata.base_addr = REG_IRQ_BASE;
+ irq_base = pdata->irq_pdata->irq_base;
+ irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
+
+ if (IS_ERR(irq_chip)) {
+ pr_err("Failed to init interrupts ret=%ld\n",
+ PTR_ERR(irq_chip));
+ return PTR_ERR(irq_chip);
+ }
+ pmic->irq_chip = irq_chip;
+ }
+
+ if (pdata->gpio_pdata) {
+ pdata->gpio_pdata->gpio_cdata.ngpios = PM8038_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 = PM8038_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;
+ }
+ }
+
+ 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) {
+ leds_cell.platform_data = pdata->leds_pdata;
+ leds_cell.pdata_size = sizeof(struct pm8xxx_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;
+ }
+ }
+
+ if (pdata->spk_pdata) {
+ spk_cell.platform_data = pdata->spk_pdata;
+ spk_cell.pdata_size = sizeof(struct pm8xxx_spk_platform_data);
+ ret = mfd_add_devices(pmic->dev, 0, &spk_cell, 1, NULL, 0);
+ if (ret) {
+ pr_err("Failed to add spk subdevice ret=%d\n", ret);
+ goto bail;
+ }
+ }
+
+ if (pdata->num_regulators > 0 && pdata->regulator_pdatas) {
+ ret = pm8038_add_regulators(pdata, pmic, irq_base);
+ if (ret) {
+ pr_err("Failed to add regulator subdevices ret=%d\n",
+ ret);
+ goto bail;
+ }
+ }
+
+ 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;
+ }
+
+ if (pdata->adc_pdata) {
+ adc_cell.platform_data = pdata->adc_pdata;
+ adc_cell.pdata_size =
+ sizeof(struct pm8xxx_adc_platform_data);
+ ret = mfd_add_devices(pmic->dev, 0, &adc_cell, 1, NULL,
+ irq_base);
+ if (ret) {
+ pr_err("Failed to add adc subdevices ret=%d\n",
+ ret);
+ }
+ }
+
+ if (pdata->charger_pdata) {
+ pdata->charger_pdata->charger_cdata.vbat_channel = CHANNEL_VBAT;
+ pdata->charger_pdata->charger_cdata.batt_temp_channel
+ = CHANNEL_BATT_THERM;
+ pdata->charger_pdata->charger_cdata.batt_id_channel
+ = CHANNEL_BATT_ID;
+ 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->bms_pdata) {
+ pdata->bms_pdata->bms_cdata.batt_temp_channel
+ = CHANNEL_BATT_THERM;
+ pdata->bms_pdata->bms_cdata.vbat_channel = CHANNEL_VBAT;
+ pdata->bms_pdata->bms_cdata.ref625mv_channel = CHANNEL_625MV;
+ pdata->bms_pdata->bms_cdata.ref1p25v_channel = CHANNEL_125V;
+ pdata->bms_pdata->bms_cdata.batt_id_channel = CHANNEL_BATT_ID;
+ 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;
+ }
+ }
+ return 0;
+bail:
+ if (pmic->irq_chip) {
+ pm8xxx_irq_exit(pmic->irq_chip);
+ pmic->irq_chip = NULL;
+ }
+ return ret;
+}
+
+static const char * const pm8038_restart_reason[] = {
+ [0] = "Unknown",
+ [1] = "Triggered from CBL (external charger)",
+ [2] = "Triggered from KPD (power key press)",
+ [3] = "Triggered from CHG (usb charger insertion)",
+ [4] = "Triggered from SMPL (sudden momentary power loss)",
+ [5] = "Triggered from RTC (real time clock)",
+ [6] = "Triggered by Hard Reset",
+ [7] = "Triggered by General Purpose Trigger",
+};
+
+static const char * const pm8038_rev_names[] = {
+ [PM8XXX_REVISION_8038_TEST] = "test",
+ [PM8XXX_REVISION_8038_1p0] = "1.0",
+ [PM8XXX_REVISION_8038_2p0] = "2.0",
+ [PM8XXX_REVISION_8038_2p1] = "2.1",
+};
+
+static int __devinit pm8038_probe(struct platform_device *pdev)
+{
+ const struct pm8038_platform_data *pdata = pdev->dev.platform_data;
+ const char *revision_name = "unknown";
+ struct pm8038 *pmic;
+ enum pm8xxx_version version;
+ int revision;
+ int rc;
+ u8 val;
+
+ if (!pdata) {
+ pr_err("missing platform data\n");
+ return -EINVAL;
+ }
+
+ pmic = kzalloc(sizeof(struct pm8038), GFP_KERNEL);
+ if (!pmic) {
+ pr_err("Cannot alloc pm8038 struct\n");
+ return -ENOMEM;
+ }
+
+ /* Read PMIC chip revision */
+ rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val));
+ if (rc) {
+ pr_err("Failed to read hw rev reg %d:rc=%d\n", REG_HWREV, rc);
+ goto err_read_rev;
+ }
+ pr_info("PMIC revision 1: PM8038 rev %02X\n", val);
+ pmic->rev_registers = val;
+
+ /* Read PMIC chip revision 2 */
+ rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val));
+ if (rc) {
+ pr_err("Failed to read hw rev 2 reg %d:rc=%d\n",
+ REG_HWREV_2, rc);
+ goto err_read_rev;
+ }
+ pr_info("PMIC revision 2: PM8038 rev %02X\n", val);
+ pmic->rev_registers |= val << BITS_PER_BYTE;
+
+ pmic->dev = &pdev->dev;
+ pm8038_drvdata.pm_chip_data = pmic;
+ platform_set_drvdata(pdev, &pm8038_drvdata);
+
+ /* Print out human readable version and revision names. */
+ version = pm8xxx_get_version(pmic->dev);
+ if (version == PM8XXX_VERSION_8038) {
+ revision = pm8xxx_get_revision(pmic->dev);
+ if (revision >= 0 && revision < ARRAY_SIZE(pm8038_rev_names))
+ revision_name = pm8038_rev_names[revision];
+ pr_info("PMIC version: PM8038 ver %s\n", revision_name);
+ } else {
+ WARN_ON(version != PM8XXX_VERSION_8038);
+ }
+
+ /* Log human readable restart reason */
+ rc = msm_ssbi_read(pdev->dev.parent, REG_PM8038_PON_CNTRL_3, &val, 1);
+ if (rc) {
+ pr_err("Cannot read restart reason rc=%d\n", rc);
+ goto err_read_rev;
+ }
+ val &= PM8038_RESTART_REASON_MASK;
+ pr_info("PMIC Restart Reason: %s\n", pm8038_restart_reason[val]);
+
+ rc = pm8038_add_subdevices(pdata, pmic);
+ if (rc) {
+ pr_err("Cannot add subdevices rc=%d\n", rc);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ mfd_remove_devices(pmic->dev);
+ platform_set_drvdata(pdev, NULL);
+ kfree(pmic->mfd_regulators);
+ kfree(pmic->regulator_cdata);
+err_read_rev:
+ kfree(pmic);
+ return rc;
+}
+
+static int __devexit pm8038_remove(struct platform_device *pdev)
+{
+ struct pm8xxx_drvdata *drvdata;
+ struct pm8038 *pmic = NULL;
+ int i;
+
+ drvdata = platform_get_drvdata(pdev);
+
+ if (drvdata)
+ pmic = drvdata->pm_chip_data;
+
+ if (pmic) {
+ if (pmic->dev)
+ mfd_remove_devices(pmic->dev);
+
+ if (pmic->irq_chip) {
+ pm8xxx_irq_exit(pmic->irq_chip);
+ pmic->irq_chip = NULL;
+ }
+ if (pmic->mfd_regulators) {
+ for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+ mutex_destroy(®ulator_data[i].pc_lock);
+ }
+ kfree(pmic->mfd_regulators);
+ kfree(pmic->regulator_cdata);
+ kfree(pmic);
+ }
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver pm8038_driver = {
+ .probe = pm8038_probe,
+ .remove = __devexit_p(pm8038_remove),
+ .driver = {
+ .name = PM8038_CORE_DEV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pm8038_init(void)
+{
+ return platform_driver_register(&pm8038_driver);
+}
+postcore_initcall(pm8038_init);
+
+static void __exit pm8038_exit(void)
+{
+ platform_driver_unregister(&pm8038_driver);
+}
+module_exit(pm8038_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC 8038 core driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pm8038-core");
diff --git a/drivers/mfd/pm8821-core.c b/drivers/mfd/pm8821-core.c
new file mode 100644
index 0000000..df9d2e1
--- /dev/null
+++ b/drivers/mfd/pm8821-core.c
@@ -0,0 +1,317 @@
+/*
+ * 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/msm_ssbi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/pm8xxx/pm8821.h>
+#include <linux/mfd/pm8xxx/core.h>
+
+#define REG_HWREV 0x002 /* PMIC4 revision */
+#define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */
+
+#define REG_MPP_BASE 0x050
+#define REG_IRQ_BASE 0x1BB
+
+#define PM8821_VERSION_MASK 0xFFF0
+#define PM8821_VERSION_VALUE 0x0BF0
+#define PM8821_REVISION_MASK 0x000F
+
+#define SINGLE_IRQ_RESOURCE(_name, _irq) \
+{ \
+ .name = _name, \
+ .start = _irq, \
+ .end = _irq, \
+ .flags = IORESOURCE_IRQ, \
+}
+
+struct pm8821 {
+ struct device *dev;
+ struct pm_irq_chip *irq_chip;
+ u32 rev_registers;
+};
+
+static int pm8821_readb(const struct device *dev, u16 addr, u8 *val)
+{
+ const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
+ const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
+
+ return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
+}
+
+static int pm8821_writeb(const struct device *dev, u16 addr, u8 val)
+{
+ const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
+ const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
+
+ return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
+}
+
+static int pm8821_read_buf(const struct device *dev, u16 addr, u8 *buf,
+ int cnt)
+{
+ const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
+ const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
+
+ return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8821_write_buf(const struct device *dev, u16 addr, u8 *buf,
+ int cnt)
+{
+ const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
+ const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
+
+ return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8821_read_irq_stat(const struct device *dev, int irq)
+{
+ const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
+ const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
+
+ return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
+}
+
+static enum pm8xxx_version pm8821_get_version(const struct device *dev)
+{
+ const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
+ const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
+ enum pm8xxx_version version = -ENODEV;
+
+ if ((pmic->rev_registers & PM8821_VERSION_MASK) == PM8821_VERSION_VALUE)
+ version = PM8XXX_VERSION_8821;
+
+ return version;
+}
+
+static int pm8821_get_revision(const struct device *dev)
+{
+ const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
+ const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
+
+ return pmic->rev_registers & PM8821_REVISION_MASK;
+}
+
+static struct pm8xxx_drvdata pm8821_drvdata = {
+ .pmic_readb = pm8821_readb,
+ .pmic_writeb = pm8821_writeb,
+ .pmic_read_buf = pm8821_read_buf,
+ .pmic_write_buf = pm8821_write_buf,
+ .pmic_read_irq_stat = pm8821_read_irq_stat,
+ .pmic_get_version = pm8821_get_version,
+ .pmic_get_revision = pm8821_get_revision,
+};
+
+static const struct resource mpp_cell_resources[] __devinitconst = {
+ {
+ .start = PM8821_IRQ_BLOCK_BIT(PM8821_MPP_BLOCK_START, 0),
+ .end = PM8821_IRQ_BLOCK_BIT(PM8821_MPP_BLOCK_START, 0)
+ + PM8821_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 struct mfd_cell debugfs_cell __devinitdata = {
+ .name = "pm8xxx-debug",
+ .id = 1,
+ .platform_data = "pm8821-dbg",
+ .pdata_size = sizeof("pm8821-dbg"),
+};
+
+
+static int __devinit
+pm8821_add_subdevices(const struct pm8821_platform_data *pdata,
+ struct pm8821 *pmic)
+{
+ int ret = 0, irq_base = 0;
+ struct pm_irq_chip *irq_chip;
+
+ if (pdata->irq_pdata) {
+ pdata->irq_pdata->irq_cdata.nirqs = PM8821_NR_IRQS;
+ pdata->irq_pdata->irq_cdata.base_addr = REG_IRQ_BASE;
+ irq_base = pdata->irq_pdata->irq_base;
+ irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
+
+ if (IS_ERR(irq_chip)) {
+ pr_err("Failed to init interrupts ret=%ld\n",
+ PTR_ERR(irq_chip));
+ return PTR_ERR(irq_chip);
+ }
+ pmic->irq_chip = irq_chip;
+ }
+
+ if (pdata->mpp_pdata) {
+ pdata->mpp_pdata->core_data.nmpps = PM8821_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;
+ }
+ }
+
+ 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;
+ }
+
+ return 0;
+bail:
+ if (pmic->irq_chip) {
+ pm8xxx_irq_exit(pmic->irq_chip);
+ pmic->irq_chip = NULL;
+ }
+ return ret;
+}
+
+static const char * const pm8821_rev_names[] = {
+ [PM8XXX_REVISION_8821_TEST] = "test",
+ [PM8XXX_REVISION_8821_1p0] = "1.0",
+ [PM8XXX_REVISION_8821_2p0] = "2.0",
+ [PM8XXX_REVISION_8821_2p1] = "2.1",
+};
+
+static int __devinit pm8821_probe(struct platform_device *pdev)
+{
+ const struct pm8821_platform_data *pdata = pdev->dev.platform_data;
+ const char *revision_name = "unknown";
+ struct pm8821 *pmic;
+ enum pm8xxx_version version;
+ int revision;
+ int rc;
+ u8 val;
+
+ if (!pdata) {
+ pr_err("missing platform data\n");
+ return -EINVAL;
+ }
+
+ pmic = kzalloc(sizeof(struct pm8821), GFP_KERNEL);
+ if (!pmic) {
+ pr_err("Cannot alloc pm8821 struct\n");
+ return -ENOMEM;
+ }
+
+ /* Read PMIC chip revision */
+ rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val));
+ if (rc) {
+ pr_err("Failed to read hw rev reg %d:rc=%d\n", REG_HWREV, rc);
+ goto err_read_rev;
+ }
+ pr_info("PMIC revision 1: PM8821 rev %02X\n", val);
+ pmic->rev_registers = val;
+
+ /* Read PMIC chip revision 2 */
+ rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val));
+ if (rc) {
+ pr_err("Failed to read hw rev 2 reg %d:rc=%d\n",
+ REG_HWREV_2, rc);
+ goto err_read_rev;
+ }
+ pr_info("PMIC revision 2: PM8821 rev %02X\n", val);
+ pmic->rev_registers |= val << BITS_PER_BYTE;
+
+ pmic->dev = &pdev->dev;
+ pm8821_drvdata.pm_chip_data = pmic;
+ platform_set_drvdata(pdev, &pm8821_drvdata);
+
+ /* Print out human readable version and revision names. */
+ version = pm8xxx_get_version(pmic->dev);
+ if (version == PM8XXX_VERSION_8821) {
+ revision = pm8xxx_get_revision(pmic->dev);
+ if (revision >= 0 && revision < ARRAY_SIZE(pm8821_rev_names))
+ revision_name = pm8821_rev_names[revision];
+ pr_info("PMIC version: PM8821 ver %s\n", revision_name);
+ } else {
+ WARN_ON(version != PM8XXX_VERSION_8821);
+ }
+
+ rc = pm8821_add_subdevices(pdata, pmic);
+ if (rc) {
+ pr_err("Cannot add subdevices rc=%d\n", rc);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ mfd_remove_devices(pmic->dev);
+ platform_set_drvdata(pdev, NULL);
+err_read_rev:
+ kfree(pmic);
+ return rc;
+}
+
+static int __devexit pm8821_remove(struct platform_device *pdev)
+{
+ struct pm8xxx_drvdata *drvdata;
+ struct pm8821 *pmic = NULL;
+
+ drvdata = platform_get_drvdata(pdev);
+ if (drvdata)
+ pmic = drvdata->pm_chip_data;
+ if (pmic)
+ mfd_remove_devices(pmic->dev);
+ if (pmic->irq_chip) {
+ pm8xxx_irq_exit(pmic->irq_chip);
+ pmic->irq_chip = NULL;
+ }
+ platform_set_drvdata(pdev, NULL);
+ kfree(pmic);
+
+ return 0;
+}
+
+static struct platform_driver pm8821_driver = {
+ .probe = pm8821_probe,
+ .remove = __devexit_p(pm8821_remove),
+ .driver = {
+ .name = "pm8821-core",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pm8821_init(void)
+{
+ return platform_driver_register(&pm8821_driver);
+}
+postcore_initcall(pm8821_init);
+
+static void __exit pm8821_exit(void)
+{
+ platform_driver_unregister(&pm8821_driver);
+}
+module_exit(pm8821_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC 8821 core driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pm8821-core");
diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index e873b15..f39a19f 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2012, 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
@@ -14,20 +14,55 @@
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <linux/err.h>
#include <linux/msm_ssbi.h>
#include <linux/mfd/core.h>
#include <linux/mfd/pm8xxx/pm8921.h>
#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/regulator.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_IRQ_BASE 0x1BB
+
+#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 0x021
+#define REG_BATT_ALARM_PWM_CTRL 0x020
+
+#define PM8921_VERSION_MASK 0xFFF0
+#define PM8921_VERSION_VALUE 0x06F0
+#define PM8922_VERSION_VALUE 0x0AF0
+#define PM8917_VERSION_VALUE 0x0CF0
+#define PM8921_REVISION_MASK 0x000F
+
+#define REG_PM8921_PON_CNTRL_3 0x01D
+#define PM8921_RESTART_REASON_MASK 0x07
+
+#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 device *dev;
+ struct pm_irq_chip *irq_chip;
+ struct mfd_cell *mfd_regulators;
+ struct pm8xxx_regulator_core_platform_data *regulator_cdata;
+ u32 rev_registers;
};
static int pm8921_readb(const struct device *dev, u16 addr, u8 *val)
@@ -72,25 +107,481 @@
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;
+ else if ((pmic->rev_registers & PM8921_VERSION_MASK)
+ == PM8922_VERSION_VALUE)
+ version = PM8XXX_VERSION_8922;
+ else if ((pmic->rev_registers & PM8921_VERSION_MASK)
+ == PM8917_VERSION_VALUE)
+ version = PM8XXX_VERSION_8917;
+
+ 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 struct resource gpio_cell_resources[] = {
+ [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 = PM8XXX_ADC_DEV_NAME,
+ .id = -1,
+ .resources = adc_cell_resources,
+ .num_resources = ARRAY_SIZE(adc_cell_resources),
+};
+
+static struct resource mpp_cell_resources[] = {
+ {
+ .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 = 0,
+ .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 = 0,
+ .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_PM8XXX_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 const struct resource ccadc_cell_resources[] __devinitconst = {
+ SINGLE_IRQ_RESOURCE("PM8921_BMS_CCADC_EOC", PM8921_BMS_CCADC_EOC),
+};
+
+static struct mfd_cell ccadc_cell __devinitdata = {
+ .name = PM8XXX_CCADC_DEV_NAME,
+ .id = -1,
+ .resources = ccadc_cell_resources,
+ .num_resources = ARRAY_SIZE(ccadc_cell_resources),
+};
+
+static struct mfd_cell vibrator_cell __devinitdata = {
+ .name = PM8XXX_VIBRATOR_DEV_NAME,
+ .id = -1,
+};
+
+static struct pm8xxx_vreg regulator_data[] = {
+ /* name pc_name ctrl test hpm_min */
+ NLDO("8921_l1", "8921_l1_pc", 0x0AE, 0x0AF, LDO_150),
+ NLDO("8921_l2", "8921_l2_pc", 0x0B0, 0x0B1, LDO_150),
+ PLDO("8921_l3", "8921_l3_pc", 0x0B2, 0x0B3, LDO_150),
+ PLDO("8921_l4", "8921_l4_pc", 0x0B4, 0x0B5, LDO_50),
+ PLDO("8921_l5", "8921_l5_pc", 0x0B6, 0x0B7, LDO_300),
+ PLDO("8921_l6", "8921_l6_pc", 0x0B8, 0x0B9, LDO_600),
+ PLDO("8921_l7", "8921_l7_pc", 0x0BA, 0x0BB, LDO_150),
+ PLDO("8921_l8", "8921_l8_pc", 0x0BC, 0x0BD, LDO_300),
+ PLDO("8921_l9", "8921_l9_pc", 0x0BE, 0x0BF, LDO_300),
+ PLDO("8921_l10", "8921_l10_pc", 0x0C0, 0x0C1, LDO_600),
+ PLDO("8921_l11", "8921_l11_pc", 0x0C2, 0x0C3, LDO_150),
+ NLDO("8921_l12", "8921_l12_pc", 0x0C4, 0x0C5, LDO_150),
+ PLDO("8921_l14", "8921_l14_pc", 0x0C8, 0x0C9, LDO_50),
+ PLDO("8921_l15", "8921_l15_pc", 0x0CA, 0x0CB, LDO_150),
+ PLDO("8921_l16", "8921_l16_pc", 0x0CC, 0x0CD, LDO_300),
+ PLDO("8921_l17", "8921_l17_pc", 0x0CE, 0x0CF, LDO_150),
+ NLDO("8921_l18", "8921_l18_pc", 0x0D0, 0x0D1, LDO_150),
+ PLDO("8921_l21", "8921_l21_pc", 0x0D6, 0x0D7, LDO_150),
+ PLDO("8921_l22", "8921_l22_pc", 0x0D8, 0x0D9, LDO_150),
+ PLDO("8921_l23", "8921_l23_pc", 0x0DA, 0x0DB, LDO_150),
+ NLDO1200("8921_l24", 0x0DC, 0x0DD, LDO_1200),
+ NLDO1200("8921_l25", 0x0DE, 0x0DF, LDO_1200),
+ NLDO1200("8921_l26", 0x0E0, 0x0E1, LDO_1200),
+ NLDO1200("8921_l27", 0x0E2, 0x0E3, LDO_1200),
+ NLDO1200("8921_l28", 0x0E4, 0x0E5, LDO_1200),
+ PLDO("8921_l29", "8921_l29_pc", 0x0E6, 0x0E7, LDO_150),
+
+ /* name pc_name ctrl test2 clk sleep hpm_min */
+ SMPS("8921_s1", "8921_s1_pc", 0x1D0, 0x1D5, 0x009, 0x1D2, SMPS_1500),
+ SMPS("8921_s2", "8921_s2_pc", 0x1D8, 0x1DD, 0x00A, 0x1DA, SMPS_1500),
+ SMPS("8921_s3", "8921_s3_pc", 0x1E0, 0x1E5, 0x00B, 0x1E2, SMPS_1500),
+ SMPS("8921_s4", "8921_s4_pc", 0x1E8, 0x1ED, 0x011, 0x1EA, SMPS_1500),
+
+ /* name ctrl fts_cnfg1 pfm pwr_cnfg hpm_min */
+ FTSMPS("8921_s5", 0x025, 0x02E, 0x026, 0x032, SMPS_2000),
+ FTSMPS("8921_s6", 0x036, 0x03F, 0x037, 0x043, SMPS_2000),
+
+ /* name pc_name ctrl test2 clk sleep hpm_min */
+ SMPS("8921_s7", "8921_s7_pc", 0x1F0, 0x1F5, 0x012, 0x1F2, SMPS_1500),
+ SMPS("8921_s8", "8921_s8_pc", 0x1F8, 0x1FD, 0x013, 0x1FA, SMPS_1500),
+
+ /* name pc_name ctrl test */
+ VS("8921_lvs1", "8921_lvs1_pc", 0x060, 0x061),
+ VS300("8921_lvs2", 0x062, 0x063),
+ VS("8921_lvs3", "8921_lvs3_pc", 0x064, 0x065),
+ VS("8921_lvs4", "8921_lvs4_pc", 0x066, 0x067),
+ VS("8921_lvs5", "8921_lvs5_pc", 0x068, 0x069),
+ VS("8921_lvs6", "8921_lvs6_pc", 0x06A, 0x06B),
+ VS("8921_lvs7", "8921_lvs7_pc", 0x06C, 0x06D),
+ VS300("8921_usb_otg", 0x06E, 0x06F),
+ VS300("8921_hdmi_mvs", 0x070, 0x071),
+
+ /* name ctrl */
+ NCP("8921_ncp", 0x090),
+};
+
+/*
+ * PM8917 adds 6 LDOs and a boost regulator beyond those available on PM8921.
+ * It also replaces SMPS 3 with FTSMPS 3. PM8917 does not have an NCP.
+ */
+static struct pm8xxx_vreg pm8917_regulator_data[] = {
+ /* name pc_name ctrl test hpm_min */
+ PLDO("8917_l30", "8917_l30_pc", 0x0A3, 0x0A4, LDO_150),
+ PLDO("8917_l31", "8917_l31_pc", 0x0A5, 0x0A6, LDO_150),
+ PLDO("8917_l32", "8917_l32_pc", 0x0A7, 0x0A8, LDO_150),
+ PLDO("8917_l33", "8917_l33_pc", 0x0C6, 0x0C7, LDO_150),
+ PLDO("8917_l34", "8917_l34_pc", 0x0D2, 0x0D3, LDO_150),
+ PLDO("8917_l35", "8917_l35_pc", 0x0D4, 0x0D5, LDO_300),
+ PLDO("8917_l36", "8917_l36_pc", 0x0A9, 0x0AA, LDO_50),
+
+ /* name ctrl */
+ BOOST("8917_boost", 0x04B),
+};
+
+#define MAX_NAME_COMPARISON_LEN 32
+
+static int __devinit match_regulator(enum pm8xxx_version version,
+ struct pm8xxx_regulator_core_platform_data *core_data, const char *name)
+{
+ int found = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(regulator_data); i++) {
+ if (regulator_data[i].rdesc.name
+ && strncmp(regulator_data[i].rdesc.name, name,
+ MAX_NAME_COMPARISON_LEN) == 0) {
+ core_data->is_pin_controlled = false;
+ core_data->vreg = ®ulator_data[i];
+ found = 1;
+ break;
+ } else if (regulator_data[i].rdesc_pc.name
+ && strncmp(regulator_data[i].rdesc_pc.name, name,
+ MAX_NAME_COMPARISON_LEN) == 0) {
+ core_data->is_pin_controlled = true;
+ core_data->vreg = ®ulator_data[i];
+ found = 1;
+ break;
+ }
+ }
+ if (version == PM8XXX_VERSION_8917) {
+ for (i = 0; i < ARRAY_SIZE(pm8917_regulator_data); i++) {
+ if (pm8917_regulator_data[i].rdesc.name
+ && strncmp(pm8917_regulator_data[i].rdesc.name,
+ name, MAX_NAME_COMPARISON_LEN) == 0) {
+ core_data->is_pin_controlled = false;
+ core_data->vreg = &pm8917_regulator_data[i];
+ found = 1;
+ break;
+ } else if (pm8917_regulator_data[i].rdesc_pc.name
+ && strncmp(pm8917_regulator_data[i].rdesc_pc.name,
+ name, MAX_NAME_COMPARISON_LEN) == 0) {
+ core_data->is_pin_controlled = true;
+ core_data->vreg = &pm8917_regulator_data[i];
+ found = 1;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ pr_err("could not find a match for regulator: %s\n", name);
+
+ return found;
+}
+
+static int __devinit
+pm8921_add_regulators(const struct pm8921_platform_data *pdata,
+ struct pm8921 *pmic, int irq_base)
+{
+ int ret = 0;
+ struct mfd_cell *mfd_regulators;
+ struct pm8xxx_regulator_core_platform_data *cdata;
+ enum pm8xxx_version version;
+ int i;
+
+ version = pm8xxx_get_version(pmic->dev);
+
+ /* Add one device for each regulator used by the board. */
+ 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));
+ return -ENOMEM;
+ }
+ cdata = kzalloc(sizeof(struct pm8xxx_regulator_core_platform_data)
+ * pdata->num_regulators, GFP_KERNEL);
+ if (!cdata) {
+ pr_err("Cannot allocate %d bytes for pm8921 regulator "
+ "core data\n", pdata->num_regulators
+ * sizeof(struct pm8xxx_regulator_core_platform_data));
+ kfree(mfd_regulators);
+ return -ENOMEM;
+ }
+ for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+ mutex_init(®ulator_data[i].pc_lock);
+ for (i = 0; i < ARRAY_SIZE(pm8917_regulator_data); i++)
+ mutex_init(&pm8917_regulator_data[i].pc_lock);
+
+ for (i = 0; i < pdata->num_regulators; i++) {
+ if (!pdata->regulator_pdatas[i].init_data.constraints.name) {
+ pr_err("name missing for regulator %d\n", i);
+ ret = -EINVAL;
+ goto bail;
+ }
+ if (!match_regulator(version, &cdata[i],
+ pdata->regulator_pdatas[i].init_data.constraints.name)) {
+ ret = -ENODEV;
+ goto bail;
+ }
+ cdata[i].pdata = &(pdata->regulator_pdatas[i]);
+ mfd_regulators[i].name = PM8XXX_REGULATOR_DEV_NAME;
+ mfd_regulators[i].id = cdata[i].pdata->id;
+ mfd_regulators[i].platform_data = &cdata[i];
+ mfd_regulators[i].pdata_size =
+ sizeof(struct pm8xxx_regulator_core_platform_data);
+ }
+ ret = mfd_add_devices(pmic->dev, 0, mfd_regulators,
+ pdata->num_regulators, NULL, irq_base);
+ if (ret)
+ goto bail;
+
+ pmic->mfd_regulators = mfd_regulators;
+ pmic->regulator_cdata = cdata;
+ return ret;
+
+bail:
+ for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+ mutex_destroy(®ulator_data[i].pc_lock);
+ for (i = 0; i < ARRAY_SIZE(pm8917_regulator_data); i++)
+ mutex_destroy(&pm8917_regulator_data[i].pc_lock);
+ kfree(mfd_regulators);
+ kfree(cdata);
+ return ret;
+}
+
+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;
+ enum pm8xxx_version version;
+
+ version = pm8xxx_get_version(pmic->dev);
if (pdata->irq_pdata) {
pdata->irq_pdata->irq_cdata.nirqs = PM8921_NR_IRQS;
- pdata->irq_pdata->irq_cdata.rev = rev;
+ pdata->irq_pdata->irq_cdata.base_addr = REG_IRQ_BASE;
irq_base = pdata->irq_pdata->irq_base;
irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
@@ -101,16 +592,270 @@
}
pmic->irq_chip = irq_chip;
}
+
+ if (pdata->gpio_pdata) {
+ if (version == PM8XXX_VERSION_8917) {
+ gpio_cell_resources[0].end = gpio_cell_resources[0].end
+ + PM8917_NR_GPIOS
+ - PM8921_NR_GPIOS;
+ pdata->gpio_pdata->gpio_cdata.ngpios = PM8917_NR_GPIOS;
+ } else {
+ 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) {
+ if (version == PM8XXX_VERSION_8917) {
+ mpp_cell_resources[0].end = mpp_cell_resources[0].end
+ + PM8917_NR_MPPS
+ - PM8921_NR_MPPS;
+ pdata->mpp_pdata->core_data.nmpps = PM8917_NR_MPPS;
+ } else {
+ 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;
+ pdata->charger_pdata->charger_cdata.batt_temp_channel
+ = CHANNEL_BATT_THERM;
+ pdata->charger_pdata->charger_cdata.batt_id_channel
+ = CHANNEL_BATT_ID;
+ 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 pm8xxx_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) {
+ pdata->bms_pdata->bms_cdata.batt_temp_channel
+ = CHANNEL_BATT_THERM;
+ pdata->bms_pdata->bms_cdata.vbat_channel = CHANNEL_VBAT;
+ pdata->bms_pdata->bms_cdata.ref625mv_channel = CHANNEL_625MV;
+ pdata->bms_pdata->bms_cdata.ref1p25v_channel = CHANNEL_125V;
+ pdata->bms_pdata->bms_cdata.batt_id_channel = CHANNEL_BATT_ID;
+ 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;
+ }
+ }
+
+ if (pdata->num_regulators > 0 && pdata->regulator_pdatas) {
+ ret = pm8921_add_regulators(pdata, pmic, irq_base);
+ if (ret) {
+ pr_err("Failed to add regulator subdevices ret=%d\n",
+ ret);
+ goto bail;
+ }
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+ }
+
+ 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;
+ }
+
+ if (version != PM8XXX_VERSION_8917) {
+ if (pdata->pwm_pdata) {
+ pwm_cell.platform_data = pdata->pwm_pdata;
+ pwm_cell.pdata_size =
+ sizeof(struct pm8xxx_pwm_platform_data);
+ }
+ 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->leds_pdata) {
+ leds_cell.platform_data = pdata->leds_pdata;
+ leds_cell.pdata_size =
+ sizeof(struct pm8xxx_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;
+ }
+ }
+
+ if (pdata->vibrator_pdata) {
+ vibrator_cell.platform_data = pdata->vibrator_pdata;
+ vibrator_cell.pdata_size =
+ sizeof(struct pm8xxx_vibrator_platform_data);
+ ret = mfd_add_devices(pmic->dev, 0, &vibrator_cell,
+ 1, NULL, 0);
+ if (ret) {
+ pr_err("Failed to add vibrator ret=%d\n", ret);
+ goto bail;
+ }
+ }
+ }
+
+ if (pdata->ccadc_pdata) {
+ ccadc_cell.platform_data = pdata->ccadc_pdata;
+ ccadc_cell.pdata_size =
+ sizeof(struct pm8xxx_ccadc_platform_data);
+
+ ret = mfd_add_devices(pmic->dev, 0, &ccadc_cell, 1, NULL,
+ irq_base);
+ if (ret) {
+ pr_err("Failed to add ccadc 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_restart_reason[] = {
+ [0] = "Unknown",
+ [1] = "Triggered from CBL (external charger)",
+ [2] = "Triggered from KPD (power key press)",
+ [3] = "Triggered from CHG (usb charger insertion)",
+ [4] = "Triggered from SMPL (sudden momentary power loss)",
+ [5] = "Triggered from RTC (real time clock)",
+ [6] = "Triggered by Hard Reset",
+ [7] = "Triggered by General Purpose Trigger",
+};
+
+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",
+ [PM8XXX_REVISION_8921_3p0] = "3.0",
+ [PM8XXX_REVISION_8921_3p1] = "3.1",
+};
+
+static const char * const pm8922_rev_names[] = {
+ [PM8XXX_REVISION_8922_TEST] = "test",
+ [PM8XXX_REVISION_8922_1p0] = "1.0",
+ [PM8XXX_REVISION_8922_1p1] = "1.1",
+ [PM8XXX_REVISION_8922_2p0] = "2.0",
+};
+
+static const char * const pm8917_rev_names[] = {
+ [PM8XXX_REVISION_8917_TEST] = "test",
+ [PM8XXX_REVISION_8917_1p0] = "1.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 +875,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 +885,43 @@
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);
+ revision = pm8xxx_get_revision(pmic->dev);
+ if (version == PM8XXX_VERSION_8921) {
+ 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 if (version == PM8XXX_VERSION_8922) {
+ if (revision >= 0 && revision < ARRAY_SIZE(pm8922_rev_names))
+ revision_name = pm8922_rev_names[revision];
+ pr_info("PMIC version: PM8922 rev %s\n", revision_name);
+ } else if (version == PM8XXX_VERSION_8917) {
+ if (revision >= 0 && revision < ARRAY_SIZE(pm8917_rev_names))
+ revision_name = pm8917_rev_names[revision];
+ pr_info("PMIC version: PM8917 rev %s\n", revision_name);
+ } else {
+ WARN_ON(version != PM8XXX_VERSION_8921
+ && version != PM8XXX_VERSION_8922
+ && version != PM8XXX_VERSION_8917);
+ }
+
+ /* Log human readable restart reason */
+ rc = msm_ssbi_read(pdev->dev.parent, REG_PM8921_PON_CNTRL_3, &val, 1);
+ if (rc) {
+ pr_err("Cannot read restart reason rc=%d\n", rc);
+ goto err_read_rev;
+ }
+ val &= PM8921_RESTART_REASON_MASK;
+ pr_info("PMIC Restart Reason: %s\n", pm8921_restart_reason[val]);
+
+ rc = pm8921_add_subdevices(pdata, pmic);
if (rc) {
pr_err("Cannot add subdevices rc=%d\n", rc);
goto err;
@@ -160,6 +935,8 @@
err:
mfd_remove_devices(pmic->dev);
platform_set_drvdata(pdev, NULL);
+ kfree(pmic->mfd_regulators);
+ kfree(pmic->regulator_cdata);
err_read_rev:
kfree(pmic);
return rc;
@@ -169,18 +946,28 @@
{
struct pm8xxx_drvdata *drvdata;
struct pm8921 *pmic = NULL;
+ int i;
drvdata = platform_get_drvdata(pdev);
if (drvdata)
pmic = drvdata->pm_chip_data;
- if (pmic)
- mfd_remove_devices(pmic->dev);
- if (pmic->irq_chip) {
- pm8xxx_irq_exit(pmic->irq_chip);
- pmic->irq_chip = NULL;
+ if (pmic) {
+ if (pmic->dev)
+ mfd_remove_devices(pmic->dev);
+ if (pmic->irq_chip)
+ pm8xxx_irq_exit(pmic->irq_chip);
+ if (pmic->mfd_regulators) {
+ for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+ mutex_destroy(®ulator_data[i].pc_lock);
+ for (i = 0; i < ARRAY_SIZE(pm8917_regulator_data); i++)
+ mutex_destroy(
+ &pm8917_regulator_data[i].pc_lock);
+ }
+ kfree(pmic->mfd_regulators);
+ kfree(pmic->regulator_cdata);
+ kfree(pmic);
}
platform_set_drvdata(pdev, NULL);
- kfree(pmic);
return 0;
}
@@ -198,7 +985,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..1d30db9
--- /dev/null
+++ b/drivers/mfd/pm8xxx-batt-alarm.c
@@ -0,0 +1,806 @@
+/* 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)
+ return;
+
+ 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..d450db3
--- /dev/null
+++ b/drivers/mfd/pm8xxx-debug.c
@@ -0,0 +1,216 @@
+/*
+ * 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/module.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;
+ }
+
+ 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;
+ }
+
+ mutex_init(&debugdev->debug_mutex);
+
+ 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);
+ mutex_destroy(&debugdev->debug_mutex);
+ 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..cc156b3 100644
--- a/drivers/mfd/pm8xxx-irq.c
+++ b/drivers/mfd/pm8xxx-irq.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2012, 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
@@ -13,6 +13,7 @@
#define pr_fmt(fmt) "%s: " fmt, __func__
+#include <linux/export.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
@@ -24,17 +25,15 @@
/* PMIC8xxx 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 SSBI_REG_ADDR_IRQ_ROOT(base) (base + 0)
+#define SSBI_REG_ADDR_IRQ_M_STATUS1(base) (base + 1)
+#define SSBI_REG_ADDR_IRQ_M_STATUS2(base) (base + 2)
+#define SSBI_REG_ADDR_IRQ_M_STATUS3(base) (base + 3)
+#define SSBI_REG_ADDR_IRQ_M_STATUS4(base) (base + 4)
+#define SSBI_REG_ADDR_IRQ_BLK_SEL(base) (base + 5)
+#define SSBI_REG_ADDR_IRQ_IT_STATUS(base) (base + 6)
+#define SSBI_REG_ADDR_IRQ_CONFIG(base) (base + 7)
+#define SSBI_REG_ADDR_IRQ_RT_STATUS(base) (base + 8)
#define PM_IRQF_LVL_SEL 0x01 /* level select */
#define PM_IRQF_MASK_FE 0x02 /* mask falling edge */
@@ -50,6 +49,7 @@
struct pm_irq_chip {
struct device *dev;
spinlock_t pm_irq_lock;
+ unsigned int base_addr;
unsigned int devirq;
unsigned int irq_base;
unsigned int num_irqs;
@@ -60,13 +60,14 @@
static int pm8xxx_read_root_irq(const struct pm_irq_chip *chip, u8 *rp)
{
- return pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_ROOT, rp);
+ return pm8xxx_readb(chip->dev,
+ SSBI_REG_ADDR_IRQ_ROOT(chip->base_addr), rp);
}
static int pm8xxx_read_master_irq(const struct pm_irq_chip *chip, u8 m, u8 *bp)
{
return pm8xxx_readb(chip->dev,
- SSBI_REG_ADDR_IRQ_M_STATUS1 + m, bp);
+ SSBI_REG_ADDR_IRQ_M_STATUS1(chip->base_addr) + m, bp);
}
static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, u8 bp, u8 *ip)
@@ -74,13 +75,15 @@
int rc;
spin_lock(&chip->pm_irq_lock);
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
+ rc = pm8xxx_writeb(chip->dev,
+ SSBI_REG_ADDR_IRQ_BLK_SEL(chip->base_addr), bp);
if (rc) {
pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
goto bail;
}
- rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_IT_STATUS, ip);
+ rc = pm8xxx_readb(chip->dev,
+ SSBI_REG_ADDR_IRQ_IT_STATUS(chip->base_addr), ip);
if (rc)
pr_err("Failed Reading Status rc=%d\n", rc);
bail:
@@ -88,19 +91,51 @@
return rc;
}
-static int pm8xxx_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp)
+static int pm8xxx_read_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp, u8 *r)
{
int rc;
spin_lock(&chip->pm_irq_lock);
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
+ rc = pm8xxx_writeb(chip->dev,
+ SSBI_REG_ADDR_IRQ_BLK_SEL(chip->base_addr), bp);
if (rc) {
pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
goto bail;
}
+ cp &= ~PM_IRQF_WRITE;
+ rc = pm8xxx_writeb(chip->dev,
+ SSBI_REG_ADDR_IRQ_CONFIG(chip->base_addr), cp);
+ if (rc)
+ pr_err("Failed Configuring IRQ rc=%d\n", rc);
+
+ rc = pm8xxx_readb(chip->dev,
+ SSBI_REG_ADDR_IRQ_CONFIG(chip->base_addr), r);
+ if (rc)
+ pr_err("Failed reading IRQ rc=%d\n", rc);
+bail:
+ spin_unlock(&chip->pm_irq_lock);
+ return rc;
+}
+
+static int pm8xxx_write_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp)
+{
+ int rc;
+
+ spin_lock(&chip->pm_irq_lock);
+ rc = pm8xxx_writeb(chip->dev,
+ SSBI_REG_ADDR_IRQ_BLK_SEL(chip->base_addr), bp);
+ if (rc) {
+ pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
+ goto bail;
+ }
+ /*
+ * Set the write bit here as this could be a unrequested irq
+ * whose PM_IRQF_WRITE bit is not set
+ */
cp |= PM_IRQF_WRITE;
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_CONFIG, cp);
+ rc = pm8xxx_writeb(chip->dev,
+ SSBI_REG_ADDR_IRQ_CONFIG(chip->base_addr), cp);
if (rc)
pr_err("Failed Configuring IRQ rc=%d\n", rc);
bail:
@@ -157,17 +192,16 @@
return ret;
}
-static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc)
+static irqreturn_t pm8xxx_irq_handler(int irq, void *data)
{
- struct pm_irq_chip *chip = irq_desc_get_handler_data(desc);
- struct irq_chip *irq_chip = irq_desc_get_chip(desc);
+ struct pm_irq_chip *chip = data;
u8 root;
int i, ret, masters = 0;
ret = pm8xxx_read_root_irq(chip, &root);
if (ret) {
pr_err("Can't read root status ret=%d\n", ret);
- return;
+ return IRQ_HANDLED;
}
/* on pm8xxx series masters start from bit 1 of the root */
@@ -178,7 +212,27 @@
if (masters & (1 << i))
pm8xxx_irq_master_handler(chip, i);
- irq_chip->irq_ack(&desc->irq_data);
+ return IRQ_HANDLED;
+}
+
+static void pm8xxx_irq_mask(struct irq_data *d)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = d->irq - chip->irq_base;
+ int master, irq_bit;
+ u8 block, config;
+
+ block = pmirq / 8;
+ master = block / 8;
+ irq_bit = pmirq % 8;
+
+ if (chip->config[pmirq] == 0) {
+ pr_warn("masking rogue irq=%d pmirq=%d\n", d->irq, pmirq);
+ chip->config[pmirq] = irq_bit << PM_IRQF_BITS_SHIFT;
+ }
+
+ config = chip->config[pmirq] | PM_IRQF_MASK_ALL;
+ pm8xxx_write_config_irq(chip, block, config);
}
static void pm8xxx_irq_mask_ack(struct irq_data *d)
@@ -192,8 +246,13 @@
master = block / 8;
irq_bit = pmirq % 8;
+ if (chip->config[pmirq] == 0) {
+ pr_warn("mask acking rogue irq=%d pmirq=%d\n", d->irq, pmirq);
+ chip->config[pmirq] = irq_bit << PM_IRQF_BITS_SHIFT;
+ }
+
config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR;
- pm8xxx_config_irq(chip, block, config);
+ pm8xxx_write_config_irq(chip, block, config);
}
static void pm8xxx_irq_unmask(struct irq_data *d)
@@ -201,14 +260,17 @@
struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
unsigned int pmirq = d->irq - chip->irq_base;
int master, irq_bit;
- u8 block, config;
+ u8 block, config, hw_conf;
block = pmirq / 8;
master = block / 8;
irq_bit = pmirq % 8;
config = chip->config[pmirq];
- pm8xxx_config_irq(chip, block, config);
+ pm8xxx_read_config_irq(chip, block, config, &hw_conf);
+ /* check if it is masked */
+ if ((hw_conf & PM_IRQF_MASK_ALL) == PM_IRQF_MASK_ALL)
+ pm8xxx_write_config_irq(chip, block, config);
}
static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
@@ -238,8 +300,14 @@
chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
}
+ /*
+ * The PM_IRQF_WRITE flag serves as an indication that this interrupt
+ * been requested
+ */
+ chip->config[pmirq] |= PM_IRQF_WRITE;
+
config = chip->config[pmirq] | PM_IRQF_CLR;
- return pm8xxx_config_irq(chip, block, config);
+ return pm8xxx_write_config_irq(chip, block, config);
}
static int pm8xxx_irq_set_wake(struct irq_data *d, unsigned int on)
@@ -247,12 +315,21 @@
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 = pm8xxx_irq_mask,
.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,
};
@@ -286,14 +363,16 @@
spin_lock_irqsave(&chip->pm_irq_lock, flags);
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, block);
+ rc = pm8xxx_writeb(chip->dev,
+ SSBI_REG_ADDR_IRQ_BLK_SEL(chip->base_addr), block);
if (rc) {
pr_err("Failed Selecting block irq=%d pmirq=%d blk=%d rc=%d\n",
irq, pmirq, block, rc);
goto bail_out;
}
- rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits);
+ rc = pm8xxx_readb(chip->dev,
+ SSBI_REG_ADDR_IRQ_RT_STATUS(chip->base_addr), &bits);
if (rc) {
pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n",
irq, pmirq, block, rc);
@@ -339,6 +418,7 @@
chip->devirq = devirq;
chip->irq_base = pdata->irq_base;
chip->num_irqs = pdata->irq_cdata.nirqs;
+ chip->base_addr = pdata->irq_cdata.base_addr;
chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8);
chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8);
spin_lock_init(&chip->pm_irq_lock);
@@ -355,15 +435,22 @@
#endif
}
- 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);
+ if (devirq != 0) {
+ rc = request_irq(devirq, pm8xxx_irq_handler,
+ pdata->irq_trigger_flag,
+ "pm8xxx_usr_irq", chip);
+ if (rc) {
+ pr_err("failed to request_irq for %d rc=%d\n",
+ devirq, rc);
+ } else {
+ irq_set_irq_wake(devirq, 1);
+ }
+ }
return chip;
}
-int __devexit pm8xxx_irq_exit(struct pm_irq_chip *chip)
+int pm8xxx_irq_exit(struct pm_irq_chip *chip)
{
irq_set_chained_handler(chip->devirq, NULL);
kfree(chip);
diff --git a/drivers/mfd/pm8xxx-misc.c b/drivers/mfd/pm8xxx-misc.c
new file mode 100644
index 0000000..0af013e
--- /dev/null
+++ b/drivers/mfd/pm8xxx-misc.c
@@ -0,0 +1,1256 @@
+/*
+ * Copyright (c) 2011-2012, 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/module.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/misc.h>
+
+/* PON CTRL 1 register */
+#define REG_PM8XXX_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
+
+/* PON CNTL registers */
+#define REG_PM8058_PON_CNTL_4 0x098
+#define REG_PM8901_PON_CNTL_4 0x099
+#define REG_PM8018_PON_CNTL_4 0x01E
+#define REG_PM8921_PON_CNTL_4 0x01E
+#define REG_PM8058_PON_CNTL_5 0x07B
+#define REG_PM8901_PON_CNTL_5 0x09A
+#define REG_PM8018_PON_CNTL_5 0x01F
+#define REG_PM8921_PON_CNTL_5 0x01F
+
+#define PON_CTRL_4_RESET_EN_MASK 0x01
+#define PON_CTRL_4_SHUTDOWN_ON_RESET 0x0
+#define PON_CTRL_4_RESTART_ON_RESET 0x1
+#define PON_CTRL_5_HARD_RESET_EN_MASK 0x08
+#define PON_CTRL_5_HARD_RESET_EN 0x08
+#define PON_CTRL_5_HARD_RESET_DIS 0x00
+
+/* Regulator master enable addresses */
+#define REG_PM8058_VREG_EN_MSM 0x018
+#define REG_PM8058_VREG_EN_GRP_5_4 0x1C8
+
+/* Regulator control registers for shutdown/reset */
+#define REG_PM8058_S0_CTRL 0x004
+#define REG_PM8058_S1_CTRL 0x005
+#define REG_PM8058_S3_CTRL 0x111
+#define REG_PM8058_L21_CTRL 0x120
+#define REG_PM8058_L22_CTRL 0x121
+
+#define PM8058_REGULATOR_ENABLE_MASK 0x80
+#define PM8058_REGULATOR_ENABLE 0x80
+#define PM8058_REGULATOR_DISABLE 0x00
+#define PM8058_REGULATOR_PULL_DOWN_MASK 0x40
+#define PM8058_REGULATOR_PULL_DOWN_EN 0x40
+
+/* Buck CTRL register */
+#define PM8058_SMPS_LEGACY_VREF_SEL 0x20
+#define PM8058_SMPS_LEGACY_VPROG_MASK 0x1F
+#define PM8058_SMPS_ADVANCED_BAND_MASK 0xC0
+#define PM8058_SMPS_ADVANCED_BAND_SHIFT 6
+#define PM8058_SMPS_ADVANCED_VPROG_MASK 0x3F
+
+/* Buck TEST2 registers for shutdown/reset */
+#define REG_PM8058_S0_TEST2 0x084
+#define REG_PM8058_S1_TEST2 0x085
+#define REG_PM8058_S3_TEST2 0x11A
+
+#define PM8058_REGULATOR_BANK_WRITE 0x80
+#define PM8058_REGULATOR_BANK_MASK 0x70
+#define PM8058_REGULATOR_BANK_SHIFT 4
+#define PM8058_REGULATOR_BANK_SEL(n) ((n) << PM8058_REGULATOR_BANK_SHIFT)
+
+/* Buck TEST2 register bank 1 */
+#define PM8058_SMPS_LEGACY_VLOW_SEL 0x01
+
+/* Buck TEST2 register bank 7 */
+#define PM8058_SMPS_ADVANCED_MODE_MASK 0x02
+#define PM8058_SMPS_ADVANCED_MODE 0x02
+#define PM8058_SMPS_LEGACY_MODE 0x00
+
+/* SLEEP CTRL register */
+#define REG_PM8058_SLEEP_CTRL 0x02B
+#define REG_PM8921_SLEEP_CTRL 0x10A
+#define REG_PM8018_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
+
+#define SLEEP_CTRL_SMPL_SEL_MASK 0x03
+#define SLEEP_CTRL_SMPL_SEL_MIN 0
+#define SLEEP_CTRL_SMPL_SEL_MAX 3
+
+/* 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
+
+/* COINCELL CHG registers */
+#define REG_PM8058_COIN_CHG 0x02F
+#define REG_PM8921_COIN_CHG 0x09C
+#define REG_PM8018_COIN_CHG 0x09C
+
+#define COINCELL_RESISTOR_SHIFT 0x2
+
+/* GP TEST register */
+#define REG_PM8XXX_GP_TEST_1 0x07A
+
+/* Stay on configuration */
+#define PM8XXX_STAY_ON_CFG 0x92
+
+/* GPIO UART MUX CTRL registers */
+#define REG_PM8XXX_GPIO_MUX_CTRL 0x1CC
+
+#define UART_PATH_SEL_MASK 0x60
+#define UART_PATH_SEL_SHIFT 0x5
+
+#define USB_ID_PU_EN_MASK 0x10 /* PM8921 family only */
+#define USB_ID_PU_EN_SHIFT 4
+
+/* Shutdown/restart delays to allow for LDO 7/dVdd regulator load settling. */
+#define PM8901_DELAY_AFTER_REG_DISABLE_MS 4
+#define PM8901_DELAY_BEFORE_SHUTDOWN_MS 8
+
+#define REG_PM8XXX_XO_CNTRL_2 0x114
+#define MP3_1_MASK 0xE0
+#define MP3_2_MASK 0x1C
+#define MP3_1_SHIFT 5
+#define MP3_2_SHIFT 2
+
+#define REG_HSED_BIAS0_CNTL2 0xA1
+#define REG_HSED_BIAS1_CNTL2 0x135
+#define REG_HSED_BIAS2_CNTL2 0x138
+#define HSED_EN_MASK 0xC0
+
+struct pm8xxx_misc_chip {
+ struct list_head link;
+ struct pm8xxx_misc_platform_data pdata;
+ struct device *dev;
+ enum pm8xxx_version version;
+ u64 osc_halt_count;
+};
+
+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;
+}
+
+/*
+ * Set an SMPS regulator to be disabled in its CTRL register, but enabled
+ * in the master enable register. Also set it's pull down enable bit.
+ * Take care to make sure that the output voltage doesn't change if switching
+ * from advanced mode to legacy mode.
+ */
+static int
+__pm8058_disable_smps_locally_set_pull_down(struct pm8xxx_misc_chip *chip,
+ u16 ctrl_addr, u16 test2_addr, u16 master_enable_addr,
+ u8 master_enable_bit)
+{
+ int rc = 0;
+ u8 vref_sel, vlow_sel, band, vprog, bank, reg;
+
+ bank = PM8058_REGULATOR_BANK_SEL(7);
+ rc = pm8xxx_writeb(chip->dev->parent, test2_addr, bank);
+ if (rc) {
+ pr_err("%s: pm8xxx_writeb(0x%03X) failed: rc=%d\n", __func__,
+ test2_addr, rc);
+ goto done;
+ }
+
+ rc = pm8xxx_readb(chip->dev->parent, test2_addr, ®);
+ if (rc) {
+ pr_err("%s: FAIL pm8xxx_readb(0x%03X): rc=%d\n",
+ __func__, test2_addr, rc);
+ goto done;
+ }
+
+ /* Check if in advanced mode. */
+ if ((reg & PM8058_SMPS_ADVANCED_MODE_MASK) ==
+ PM8058_SMPS_ADVANCED_MODE) {
+ /* Determine current output voltage. */
+ rc = pm8xxx_readb(chip->dev->parent, ctrl_addr, ®);
+ if (rc) {
+ pr_err("%s: FAIL pm8xxx_readb(0x%03X): rc=%d\n",
+ __func__, ctrl_addr, rc);
+ goto done;
+ }
+
+ band = (reg & PM8058_SMPS_ADVANCED_BAND_MASK)
+ >> PM8058_SMPS_ADVANCED_BAND_SHIFT;
+ switch (band) {
+ case 3:
+ vref_sel = 0;
+ vlow_sel = 0;
+ break;
+ case 2:
+ vref_sel = PM8058_SMPS_LEGACY_VREF_SEL;
+ vlow_sel = 0;
+ break;
+ case 1:
+ vref_sel = PM8058_SMPS_LEGACY_VREF_SEL;
+ vlow_sel = PM8058_SMPS_LEGACY_VLOW_SEL;
+ break;
+ default:
+ pr_err("%s: regulator already disabled\n", __func__);
+ return -EPERM;
+ }
+ vprog = (reg & PM8058_SMPS_ADVANCED_VPROG_MASK);
+ /* Round up if fine step is in use. */
+ vprog = (vprog + 1) >> 1;
+ if (vprog > PM8058_SMPS_LEGACY_VPROG_MASK)
+ vprog = PM8058_SMPS_LEGACY_VPROG_MASK;
+
+ /* Set VLOW_SEL bit. */
+ bank = PM8058_REGULATOR_BANK_SEL(1);
+ rc = pm8xxx_writeb(chip->dev->parent, test2_addr, bank);
+ if (rc) {
+ pr_err("%s: FAIL pm8xxx_writeb(0x%03X): rc=%d\n",
+ __func__, test2_addr, rc);
+ goto done;
+ }
+
+ rc = pm8xxx_misc_masked_write(chip, test2_addr,
+ PM8058_REGULATOR_BANK_WRITE | PM8058_REGULATOR_BANK_MASK
+ | PM8058_SMPS_LEGACY_VLOW_SEL,
+ PM8058_REGULATOR_BANK_WRITE |
+ PM8058_REGULATOR_BANK_SEL(1) | vlow_sel);
+ if (rc)
+ goto done;
+
+ /* Switch to legacy mode */
+ bank = PM8058_REGULATOR_BANK_SEL(7);
+ rc = pm8xxx_writeb(chip->dev->parent, test2_addr, bank);
+ if (rc) {
+ pr_err("%s: FAIL pm8xxx_writeb(0x%03X): rc=%d\n",
+ __func__, test2_addr, rc);
+ goto done;
+ }
+ rc = pm8xxx_misc_masked_write(chip, test2_addr,
+ PM8058_REGULATOR_BANK_WRITE |
+ PM8058_REGULATOR_BANK_MASK |
+ PM8058_SMPS_ADVANCED_MODE_MASK,
+ PM8058_REGULATOR_BANK_WRITE |
+ PM8058_REGULATOR_BANK_SEL(7) |
+ PM8058_SMPS_LEGACY_MODE);
+ if (rc)
+ goto done;
+
+ /* Enable locally, enable pull down, keep voltage the same. */
+ rc = pm8xxx_misc_masked_write(chip, ctrl_addr,
+ PM8058_REGULATOR_ENABLE_MASK |
+ PM8058_REGULATOR_PULL_DOWN_MASK |
+ PM8058_SMPS_LEGACY_VREF_SEL |
+ PM8058_SMPS_LEGACY_VPROG_MASK,
+ PM8058_REGULATOR_ENABLE | PM8058_REGULATOR_PULL_DOWN_EN
+ | vref_sel | vprog);
+ if (rc)
+ goto done;
+ }
+
+ /* Enable in master control register. */
+ rc = pm8xxx_misc_masked_write(chip, master_enable_addr,
+ master_enable_bit, master_enable_bit);
+ if (rc)
+ goto done;
+
+ /* Disable locally and enable pull down. */
+ rc = pm8xxx_misc_masked_write(chip, ctrl_addr,
+ PM8058_REGULATOR_ENABLE_MASK | PM8058_REGULATOR_PULL_DOWN_MASK,
+ PM8058_REGULATOR_DISABLE | PM8058_REGULATOR_PULL_DOWN_EN);
+
+done:
+ return rc;
+}
+
+static int
+__pm8058_disable_ldo_locally_set_pull_down(struct pm8xxx_misc_chip *chip,
+ u16 ctrl_addr, u16 master_enable_addr, u8 master_enable_bit)
+{
+ int rc;
+
+ /* Enable LDO in master control register. */
+ rc = pm8xxx_misc_masked_write(chip, master_enable_addr,
+ master_enable_bit, master_enable_bit);
+ if (rc)
+ goto done;
+
+ /* Disable LDO in CTRL register and set pull down */
+ rc = pm8xxx_misc_masked_write(chip, ctrl_addr,
+ PM8058_REGULATOR_ENABLE_MASK | PM8058_REGULATOR_PULL_DOWN_MASK,
+ PM8058_REGULATOR_DISABLE | PM8058_REGULATOR_PULL_DOWN_EN);
+
+done:
+ return rc;
+}
+
+static int __pm8018_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_PM8018_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);
+ return rc;
+ }
+
+ /*
+ * 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_PM8XXX_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);
+
+ return rc;
+}
+
+static int __pm8058_reset_pwr_off(struct pm8xxx_misc_chip *chip, int reset)
+{
+ int rc;
+
+ /* When shutting down, enable active pulldowns on important rails. */
+ if (!reset) {
+ /* Disable SMPS's 0,1,3 locally and set pulldown enable bits. */
+ __pm8058_disable_smps_locally_set_pull_down(chip,
+ REG_PM8058_S0_CTRL, REG_PM8058_S0_TEST2,
+ REG_PM8058_VREG_EN_MSM, BIT(7));
+ __pm8058_disable_smps_locally_set_pull_down(chip,
+ REG_PM8058_S1_CTRL, REG_PM8058_S1_TEST2,
+ REG_PM8058_VREG_EN_MSM, BIT(6));
+ __pm8058_disable_smps_locally_set_pull_down(chip,
+ REG_PM8058_S3_CTRL, REG_PM8058_S3_TEST2,
+ REG_PM8058_VREG_EN_GRP_5_4, BIT(7) | BIT(4));
+ /* Disable LDO 21 locally and set pulldown enable bit. */
+ __pm8058_disable_ldo_locally_set_pull_down(chip,
+ REG_PM8058_L21_CTRL, REG_PM8058_VREG_EN_GRP_5_4,
+ BIT(1));
+ }
+
+ /*
+ * 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_PM8XXX_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;
+ }
+ mdelay(PM8901_DELAY_AFTER_REG_DISABLE_MS);
+ }
+ }
+
+read_write_err:
+ mdelay(PM8901_DELAY_BEFORE_SHUTDOWN_MS);
+ 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_PM8XXX_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_8018:
+ rc = __pm8018_reset_pwr_off(chip, reset);
+ break;
+ 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_8038:
+ case PM8XXX_VERSION_8917:
+ 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);
+
+/**
+ * pm8xxx_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 pm8xxx_smpl_control(int enable)
+{
+ 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_8018:
+ rc = pm8xxx_misc_masked_write(chip,
+ REG_PM8018_SLEEP_CTRL, SLEEP_CTRL_SMPL_EN_MASK,
+ (enable ? SLEEP_CTRL_SMPL_EN_RESET
+ : SLEEP_CTRL_SMPL_EN_PWR_OFF));
+ break;
+ case PM8XXX_VERSION_8058:
+ rc = pm8xxx_misc_masked_write(chip,
+ REG_PM8058_SLEEP_CTRL, SLEEP_CTRL_SMPL_EN_MASK,
+ (enable ? SLEEP_CTRL_SMPL_EN_RESET
+ : SLEEP_CTRL_SMPL_EN_PWR_OFF));
+ break;
+ case PM8XXX_VERSION_8921:
+ rc = pm8xxx_misc_masked_write(chip,
+ REG_PM8921_SLEEP_CTRL, SLEEP_CTRL_SMPL_EN_MASK,
+ (enable ? SLEEP_CTRL_SMPL_EN_RESET
+ : SLEEP_CTRL_SMPL_EN_PWR_OFF));
+ break;
+ default:
+ /* PMIC doesn't have reset_pwr_off; do nothing. */
+ break;
+ }
+ if (rc) {
+ pr_err("setting smpl control failed, rc=%d\n", rc);
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+ return rc;
+}
+EXPORT_SYMBOL(pm8xxx_smpl_control);
+
+
+/**
+ * pm8xxx_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 pm8xxx_smpl_set_delay(enum pm8xxx_smpl_delay delay)
+{
+ struct pm8xxx_misc_chip *chip;
+ unsigned long flags;
+ int rc = 0;
+
+ if (delay < SLEEP_CTRL_SMPL_SEL_MIN
+ || delay > SLEEP_CTRL_SMPL_SEL_MAX) {
+ pr_err("%s: invalid delay specified: %d\n", __func__, delay);
+ return -EINVAL;
+ }
+
+ 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_8018:
+ rc = pm8xxx_misc_masked_write(chip,
+ REG_PM8018_SLEEP_CTRL, SLEEP_CTRL_SMPL_SEL_MASK,
+ delay);
+ break;
+ case PM8XXX_VERSION_8058:
+ rc = pm8xxx_misc_masked_write(chip,
+ REG_PM8058_SLEEP_CTRL, SLEEP_CTRL_SMPL_SEL_MASK,
+ delay);
+ break;
+ case PM8XXX_VERSION_8921:
+ rc = pm8xxx_misc_masked_write(chip,
+ REG_PM8921_SLEEP_CTRL, SLEEP_CTRL_SMPL_SEL_MASK,
+ delay);
+ break;
+ default:
+ /* PMIC doesn't have reset_pwr_off; do nothing. */
+ break;
+ }
+ if (rc) {
+ pr_err("setting smpl delay failed, rc=%d\n", rc);
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+ return rc;
+}
+EXPORT_SYMBOL(pm8xxx_smpl_set_delay);
+
+/**
+ * pm8xxx_coincell_chg_config - Disables or enables the coincell charger, and
+ * configures its voltage and resistor settings.
+ * @chg_config: Holds both voltage and resistor values, and a
+ * switch to change the state of charger.
+ * If state is to disable the charger then
+ * both voltage and resistor are disregarded.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_coincell_chg_config(struct pm8xxx_coincell_chg *chg_config)
+{
+ struct pm8xxx_misc_chip *chip;
+ unsigned long flags;
+ u8 reg = 0, voltage, resistor;
+ int rc = 0;
+
+ if (chg_config == NULL) {
+ pr_err("chg_config is NULL\n");
+ return -EINVAL;
+ }
+
+ voltage = chg_config->voltage;
+ resistor = chg_config->resistor;
+
+ if (resistor < PM8XXX_COINCELL_RESISTOR_2100_OHMS ||
+ resistor > PM8XXX_COINCELL_RESISTOR_800_OHMS) {
+ pr_err("Invalid resistor value provided\n");
+ return -EINVAL;
+ }
+
+ if (voltage < PM8XXX_COINCELL_VOLTAGE_3p2V ||
+ (voltage > PM8XXX_COINCELL_VOLTAGE_3p0V &&
+ voltage != PM8XXX_COINCELL_VOLTAGE_2p5V)) {
+ pr_err("Invalid voltage value provided\n");
+ return -EINVAL;
+ }
+
+ if (chg_config->state == PM8XXX_COINCELL_CHG_DISABLE) {
+ reg = 0;
+ } else {
+ reg |= voltage;
+ reg |= (resistor << COINCELL_RESISTOR_SHIFT);
+ }
+
+ 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_8018:
+ rc = pm8xxx_writeb(chip->dev->parent,
+ REG_PM8018_COIN_CHG, reg);
+ break;
+ case PM8XXX_VERSION_8058:
+ rc = pm8xxx_writeb(chip->dev->parent,
+ REG_PM8058_COIN_CHG, reg);
+ break;
+ case PM8XXX_VERSION_8921:
+ rc = pm8xxx_writeb(chip->dev->parent,
+ REG_PM8921_COIN_CHG, reg);
+ break;
+ default:
+ /* PMIC doesn't have reset_pwr_off; do nothing. */
+ break;
+ }
+ if (rc) {
+ pr_err("coincell chg. config failed, rc=%d\n", rc);
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+ return rc;
+}
+EXPORT_SYMBOL(pm8xxx_coincell_chg_config);
+
+/**
+ * pm8xxx_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 pm8xxx_watchdog_reset_control(int enable)
+{
+ 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_8018:
+ case PM8XXX_VERSION_8058:
+ case PM8XXX_VERSION_8921:
+ rc = pm8xxx_misc_masked_write(chip,
+ REG_PM8XXX_PON_CTRL_1, PON_CTRL_1_WD_EN_MASK,
+ (enable ? PON_CTRL_1_WD_EN_RESET
+ : PON_CTRL_1_WD_EN_PWR_OFF));
+ break;
+ default:
+ /* WD reset control not supported */
+ break;
+ }
+ if (rc) {
+ pr_err("setting WD reset control failed, rc=%d\n", rc);
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+ return rc;
+}
+EXPORT_SYMBOL(pm8xxx_watchdog_reset_control);
+
+/**
+ * pm8xxx_stay_on - enables stay_on feature
+ *
+ * PMIC stay-on feature allows PMIC to ignore MSM PS_HOLD=low
+ * signal so that some special functions like debugging could be
+ * performed.
+ *
+ * This feature should not be used in any product release.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_stay_on(void)
+{
+ 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_8018:
+ case PM8XXX_VERSION_8058:
+ case PM8XXX_VERSION_8921:
+ rc = pm8xxx_writeb(chip->dev->parent,
+ REG_PM8XXX_GP_TEST_1, PM8XXX_STAY_ON_CFG);
+ break;
+ default:
+ /* stay on not supported */
+ break;
+ }
+ if (rc) {
+ pr_err("stay_on failed failed, rc=%d\n", rc);
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+ return rc;
+}
+EXPORT_SYMBOL(pm8xxx_stay_on);
+
+static int
+__pm8xxx_hard_reset_config(struct pm8xxx_misc_chip *chip,
+ enum pm8xxx_pon_config config, u16 pon4_addr, u16 pon5_addr)
+{
+ int rc = 0;
+
+ switch (config) {
+ case PM8XXX_DISABLE_HARD_RESET:
+ rc = pm8xxx_misc_masked_write(chip, pon5_addr,
+ PON_CTRL_5_HARD_RESET_EN_MASK,
+ PON_CTRL_5_HARD_RESET_DIS);
+ break;
+ case PM8XXX_SHUTDOWN_ON_HARD_RESET:
+ rc = pm8xxx_misc_masked_write(chip, pon5_addr,
+ PON_CTRL_5_HARD_RESET_EN_MASK,
+ PON_CTRL_5_HARD_RESET_EN);
+ if (!rc) {
+ rc = pm8xxx_misc_masked_write(chip, pon4_addr,
+ PON_CTRL_4_RESET_EN_MASK,
+ PON_CTRL_4_SHUTDOWN_ON_RESET);
+ }
+ break;
+ case PM8XXX_RESTART_ON_HARD_RESET:
+ rc = pm8xxx_misc_masked_write(chip, pon5_addr,
+ PON_CTRL_5_HARD_RESET_EN_MASK,
+ PON_CTRL_5_HARD_RESET_EN);
+ if (!rc) {
+ rc = pm8xxx_misc_masked_write(chip, pon4_addr,
+ PON_CTRL_4_RESET_EN_MASK,
+ PON_CTRL_4_RESTART_ON_RESET);
+ }
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+/**
+ * pm8xxx_hard_reset_config - Allows different reset configurations
+ *
+ * config = PM8XXX_DISABLE_HARD_RESET to disable hard reset
+ * = PM8XXX_SHUTDOWN_ON_HARD_RESET to turn off the system on hard reset
+ * = PM8XXX_RESTART_ON_HARD_RESET to restart the system on hard reset
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_hard_reset_config(enum pm8xxx_pon_config config)
+{
+ 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_8018:
+ __pm8xxx_hard_reset_config(chip, config,
+ REG_PM8018_PON_CNTL_4, REG_PM8018_PON_CNTL_5);
+ break;
+ case PM8XXX_VERSION_8058:
+ __pm8xxx_hard_reset_config(chip, config,
+ REG_PM8058_PON_CNTL_4, REG_PM8058_PON_CNTL_5);
+ break;
+ case PM8XXX_VERSION_8901:
+ __pm8xxx_hard_reset_config(chip, config,
+ REG_PM8901_PON_CNTL_4, REG_PM8901_PON_CNTL_5);
+ break;
+ case PM8XXX_VERSION_8921:
+ __pm8xxx_hard_reset_config(chip, config,
+ REG_PM8921_PON_CNTL_4, REG_PM8921_PON_CNTL_5);
+ break;
+ default:
+ /* hard reset config. no supported */
+ break;
+ }
+ if (rc) {
+ pr_err("hard reset config. failed, rc=%d\n", rc);
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+ return rc;
+}
+EXPORT_SYMBOL(pm8xxx_hard_reset_config);
+
+/* Handle the OSC_HALT interrupt: 32 kHz XTAL oscillator has stopped. */
+static irqreturn_t pm8xxx_osc_halt_isr(int irq, void *data)
+{
+ struct pm8xxx_misc_chip *chip = data;
+ u64 count = 0;
+
+ if (chip) {
+ chip->osc_halt_count++;
+ count = chip->osc_halt_count;
+ }
+
+ pr_crit("%s: OSC_HALT interrupt has triggered, 32 kHz XTAL oscillator"
+ " has halted (%llu)!\n", __func__, count);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * pm8xxx_uart_gpio_mux_ctrl - Mux configuration to select the UART
+ *
+ * @uart_path_sel: Input argument to select either UART1/2/3
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_uart_gpio_mux_ctrl(enum pm8xxx_uart_path_sel uart_path_sel)
+{
+ 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_8018:
+ case PM8XXX_VERSION_8058:
+ case PM8XXX_VERSION_8921:
+ rc = pm8xxx_misc_masked_write(chip,
+ REG_PM8XXX_GPIO_MUX_CTRL, UART_PATH_SEL_MASK,
+ uart_path_sel << UART_PATH_SEL_SHIFT);
+ break;
+ default:
+ /* Functionality not supported */
+ break;
+ }
+ if (rc) {
+ pr_err("uart_gpio_mux_ctrl failed, rc=%d\n", rc);
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+ return rc;
+}
+EXPORT_SYMBOL(pm8xxx_uart_gpio_mux_ctrl);
+
+/**
+ * pm8xxx_usb_id_pullup - Control a pullup for USB ID
+ *
+ * @enable: enable (1) or disable (0) the pullup
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_usb_id_pullup(int enable)
+{
+ struct pm8xxx_misc_chip *chip;
+ unsigned long flags;
+ int rc = -ENXIO;
+
+ 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_8921:
+ case PM8XXX_VERSION_8922:
+ case PM8XXX_VERSION_8917:
+ case PM8XXX_VERSION_8038:
+ rc = pm8xxx_misc_masked_write(chip,
+ REG_PM8XXX_GPIO_MUX_CTRL, USB_ID_PU_EN_MASK,
+ enable << USB_ID_PU_EN_SHIFT);
+
+ if (rc)
+ pr_err("Fail: reg=%x, rc=%d\n",
+ REG_PM8XXX_GPIO_MUX_CTRL, rc);
+ break;
+ default:
+ /* Functionality not supported */
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+ return rc;
+}
+EXPORT_SYMBOL(pm8xxx_usb_id_pullup);
+
+static int __pm8901_preload_dVdd(struct pm8xxx_misc_chip *chip)
+{
+ int rc;
+
+ /* dVdd preloading is not needed for PMIC PM8901 rev 2.3 and beyond. */
+ if (pm8xxx_get_revision(chip->dev->parent) >= PM8XXX_REVISION_8901_2p3)
+ return 0;
+
+ rc = pm8xxx_writeb(chip->dev->parent, 0x0BD, 0x0F);
+ if (rc)
+ pr_err("pm8xxx_writeb failed for 0x0BD, rc=%d\n", rc);
+
+ rc = pm8xxx_writeb(chip->dev->parent, 0x001, 0xB4);
+ if (rc)
+ pr_err("pm8xxx_writeb failed for 0x001, rc=%d\n", rc);
+
+ pr_info("dVdd preloaded\n");
+
+ return rc;
+}
+
+/**
+ * pm8xxx_preload_dVdd - preload the dVdd regulator during off state.
+ *
+ * This can help to reduce fluctuations in the dVdd voltage during startup
+ * at the cost of additional off state current draw.
+ *
+ * This API should only be called if dVdd startup issues are suspected.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_preload_dVdd(void)
+{
+ 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_8901:
+ rc = __pm8901_preload_dVdd(chip);
+ break;
+ default:
+ /* PMIC doesn't have preload_dVdd; do nothing. */
+ break;
+ }
+ if (rc) {
+ pr_err("preload_dVdd failed, rc=%d\n", rc);
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_preload_dVdd);
+
+int pm8xxx_aux_clk_control(enum pm8xxx_aux_clk_id clk_id,
+ enum pm8xxx_aux_clk_div divider, bool enable)
+{
+ struct pm8xxx_misc_chip *chip;
+ unsigned long flags;
+ u8 clk_mask = 0, value = 0;
+
+ if (clk_id == CLK_MP3_1) {
+ clk_mask = MP3_1_MASK;
+ value = divider << MP3_1_SHIFT;
+ } else if (clk_id == CLK_MP3_2) {
+ clk_mask = MP3_2_MASK;
+ value = divider << MP3_2_SHIFT;
+ } else {
+ pr_err("Invalid clock id of %d\n", clk_id);
+ return -EINVAL;
+ }
+ if (!enable)
+ value = 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_8038:
+ case PM8XXX_VERSION_8921:
+ pm8xxx_misc_masked_write(chip,
+ REG_PM8XXX_XO_CNTRL_2, clk_mask, value);
+ break;
+ default:
+ /* Functionality not supported */
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_aux_clk_control);
+
+int pm8xxx_hsed_bias_control(enum pm8xxx_hsed_bias bias, bool enable)
+{
+ struct pm8xxx_misc_chip *chip;
+ unsigned long flags;
+ int rc = 0;
+ u16 addr;
+
+ switch (bias) {
+ case PM8XXX_HSED_BIAS0:
+ addr = REG_HSED_BIAS0_CNTL2;
+ break;
+ case PM8XXX_HSED_BIAS1:
+ addr = REG_HSED_BIAS1_CNTL2;
+ break;
+ case PM8XXX_HSED_BIAS2:
+ addr = REG_HSED_BIAS2_CNTL2;
+ break;
+ default:
+ pr_err("Invalid BIAS line\n");
+ return -EINVAL;
+ }
+
+ 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:
+ case PM8XXX_VERSION_8921:
+ rc = pm8xxx_misc_masked_write(chip, addr,
+ HSED_EN_MASK, enable ? HSED_EN_MASK : 0);
+ if (rc < 0)
+ pr_err("Enable HSED BIAS failed rc=%d\n", rc);
+ break;
+ default:
+ /* Functionality not supported */
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+ return rc;
+}
+EXPORT_SYMBOL(pm8xxx_hsed_bias_control);
+
+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, irq;
+
+ 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));
+
+ irq = platform_get_irq_byname(pdev, "pm8xxx_osc_halt_irq");
+ if (irq > 0) {
+ rc = request_any_context_irq(irq, pm8xxx_osc_halt_isr,
+ IRQF_TRIGGER_RISING | IRQF_DISABLED,
+ "pm8xxx_osc_halt_irq", chip);
+ if (rc < 0) {
+ pr_err("%s: request_any_context_irq(%d) FAIL: %d\n",
+ __func__, irq, rc);
+ goto fail_irq;
+ }
+ }
+
+ /* 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;
+
+fail_irq:
+ platform_set_drvdata(pdev, NULL);
+ kfree(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;
+ int irq = platform_get_irq_byname(pdev, "pm8xxx_osc_halt_irq");
+ if (irq > 0)
+ free_irq(irq, chip);
+
+ 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..022cfb6
--- /dev/null
+++ b/drivers/mfd/pm8xxx-pwm.c
@@ -0,0 +1,1471 @@
+/* Copyright (c) 2011-2012, 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_PWM_CHANNELS 3
+
+/*
+ * For the lack of better term to distinguish functional
+ * differences, hereby, LPG version 0 (V0, v0) denotes
+ * PM8058/8921, and version 1 (V1, v1) denotes
+ * PM8922/8038.
+ */
+#define PM8XXX_LPG_V0_PWM_CHANNELS 8
+#define PM8XXX_LPG_V1_PWM_CHANNELS 6
+#define PM8XXX_LPG_CTL_REGS 7
+
+/* PM8XXX PWM */
+#define SSBI_REG_ADDR_PWM1_CTRL1 0x88
+#define SSBI_REG_ADDR_PWM1_CTRL2 0x89
+#define SSBI_REG_ADDR_PWM_CTL(id, base) (id == 0 ? base : (base + (id << 1)))
+#define SSBI_REG_ADDR_PWM_CTL1(id) SSBI_REG_ADDR_PWM_CTL(id, \
+ SSBI_REG_ADDR_PWM1_CTRL1)
+#define SSBI_REG_ADDR_PWM_CTL2(id) SSBI_REG_ADDR_PWM_CTL(id, \
+ SSBI_REG_ADDR_PWM1_CTRL2)
+
+#define PM8XXX_PWM_CLK_SEL_SHIFT 6
+#define PM8XXX_PWM_CLK_SEL_MASK 0xC0
+#define PM8XXX_PWM_PREDIVIDE_SHIFT 5
+#define PM8XXX_PWM_PREDIVIDE_MASK 0x20
+#define PM8XXX_PWM_M_SHIFT 2
+#define PM8XXX_PWM_M_MASK 0x1C
+#define PM8XXX_PWM_SIZE_SHIFT 1
+#define PM8XXX_PWM_SIZE_MASK 0x02
+#define PM8XXX_PWM_VALUE_BIT0 0x01
+#define PM8XXX_PWM_DISABLE 0x3F
+
+/* PM8XXX LPG 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
+#define SSBI_REG_ADDR_LPG_TEST 0x147
+
+/* LPG 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)
+
+/* LPG Control 1 */
+#define PM8XXX_PWM_REVERSE_EN 0x80
+#define PM8XXX_PWM_BYPASS_LUT 0x40
+#define PM8XXX_PWM_HIGH_INDEX_MASK 0x3F
+
+/* LPG Control 2 */
+#define PM8XXX_PWM_LOOP_EN 0x80
+#define PM8XXX_PWM_RAMP_UP 0x40
+#define PM8XXX_PWM_LOW_INDEX_MASK 0x3F
+
+/* LPG Control 3 */
+#define PM8XXX_PWM_VALUE_BIT7_0 0xFF
+#define PM8XXX_PWM_VALUE_BIT5_0 0x3F
+
+/* LPG Control 4 */
+#define PM8XXX_PWM_VALUE_BIT8 0x80
+
+#define PM8XXX_LPG_PWM_CLK_SEL_MASK 0x60
+#define PM8XXX_LPG_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_LPG_PWM_PREDIVIDE_MASK 0x18
+#define PM8XXX_LPG_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_LPG_PWM_M_MASK 0x07
+#define PM8XXX_PWM_M_MIN 0
+#define PM8XXX_PWM_M_MAX 7
+
+/* LPG 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
+
+/* LPG 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 */
+
+/* LPG LUT_CFG1 */
+#define PM8XXX_PWM_LUT_READ 0x40
+
+/* TEST */
+#define PM8XXX_PWM_DTEST_MASK 0x38
+#define PM8XXX_PWM_DTEST_SHIFT 3
+#define PM8XXX_PWM_DTEST_BANK_MASK 0x07
+
+/*
+ * 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) = (Pre-divide * Clock Period) * 2^m
+ */
+#define NUM_CLOCKS 3
+
+#define NSEC_1024HZ (NSEC_PER_SEC / 1024)
+#define NSEC_32768HZ (NSEC_PER_SEC / 32768)
+#define NSEC_19P2MHZ (NSEC_PER_SEC / 19200000)
+
+#define NUM_LPG_PRE_DIVIDE 4
+#define NUM_PWM_PRE_DIVIDE 2
+
+#define PRE_DIVIDE_1 1 /* v1 */
+#define PRE_DIVIDE_2 2
+#define PRE_DIVIDE_3 3
+#define PRE_DIVIDE_5 5
+#define PRE_DIVIDE_6 6
+
+static unsigned int pt_t[NUM_LPG_PRE_DIVIDE][NUM_CLOCKS] = {
+ { PRE_DIVIDE_2 * NSEC_1024HZ,
+ PRE_DIVIDE_2 * NSEC_32768HZ,
+ PRE_DIVIDE_2 * NSEC_19P2MHZ,
+ },
+ { PRE_DIVIDE_3 * NSEC_1024HZ,
+ PRE_DIVIDE_3 * NSEC_32768HZ,
+ PRE_DIVIDE_3 * NSEC_19P2MHZ,
+ },
+ { PRE_DIVIDE_5 * NSEC_1024HZ,
+ PRE_DIVIDE_5 * NSEC_32768HZ,
+ PRE_DIVIDE_5 * NSEC_19P2MHZ,
+ },
+ { PRE_DIVIDE_6 * NSEC_1024HZ,
+ PRE_DIVIDE_6 * NSEC_32768HZ,
+ PRE_DIVIDE_6 * NSEC_19P2MHZ,
+ },
+};
+
+/* Private data */
+struct pm8xxx_pwm_chip;
+
+struct pwm_device {
+ int pwm_id; /* = bank/channel id */
+ int in_use;
+ const char *label;
+ struct pm8xxx_pwm_period period;
+ int pwm_value;
+ int pwm_period;
+ int pwm_duty;
+ u8 pwm_lpg_ctl[PM8XXX_LPG_CTL_REGS];
+ u8 pwm_ctl1;
+ u8 pwm_ctl2;
+ int irq;
+ struct pm8xxx_pwm_chip *chip;
+ int bypass_lut;
+ int dtest_mode_supported;
+};
+
+struct pm8xxx_pwm_chip {
+ struct pwm_device *pwm_dev;
+ u8 pwm_channels;
+ u8 pwm_total_pre_divs;
+ u8 bank_mask;
+ struct mutex pwm_mutex;
+ struct device *dev;
+ bool is_lpg_supported;
+};
+
+static struct pm8xxx_pwm_chip *pwm_chip;
+
+struct pm8xxx_pwm_lut {
+ /* LUT parameters */
+ 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 void pm8xxx_pwm_save(u8 *u8p, u8 mask, u8 val)
+{
+ *u8p &= ~mask;
+ *u8p |= val & mask;
+}
+
+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_writeb(): 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_writeb(): 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_lpg_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_lpg_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_writeb(): rc=%d (Enable PWM Ctl 0)\n", rc);
+ else
+ pwm->pwm_lpg_ctl[0] = reg;
+ return rc;
+}
+
+static int pm8xxx_pwm_disable(struct pwm_device *pwm)
+{
+ int rc;
+ u8 reg;
+
+ reg = pwm->pwm_ctl1 & PM8XXX_PWM_DISABLE;
+
+ rc = pm8xxx_writeb(pwm->chip->dev->parent,
+ SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id), reg);
+
+ if (rc)
+ pr_err("pm8xxx_writeb(): rc=%d (Disable PWM Ctl %d)\n", rc,
+ pwm->pwm_id);
+ return rc;
+}
+
+static int pm8xxx_pwm_enable(struct pwm_device *pwm)
+{
+ /**
+ * A kind of best Effort: Just write the clock information that
+ * we have in the register.
+ */
+ int rc;
+
+ rc = pm8xxx_writeb(pwm->chip->dev->parent,
+ SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id), pwm->pwm_ctl1);
+
+ if (rc)
+ pr_err("pm8xxx_writeb(): rc=%d (Enable PWM Ctl %d)\n", rc,
+ pwm->pwm_id);
+ return rc;
+}
+
+static void pm8xxx_pwm_calc_period(unsigned int period_us,
+ struct pm8xxx_pwm_period *period)
+{
+ int n, m, clk, div;
+ int best_m, best_div, best_clk;
+ unsigned int last_err, cur_err, min_err;
+ unsigned int tmp_p, period_n;
+
+ /* PWM Period / N */
+ if (period_us < ((unsigned)(-1) / NSEC_PER_USEC)) {
+ period_n = (period_us * NSEC_PER_USEC) >> 6;
+ n = 6;
+ } else {
+ period_n = (period_us >> 9) * NSEC_PER_USEC;
+ n = 9;
+ }
+
+ min_err = last_err = (unsigned)(-1);
+ best_m = 0;
+ best_clk = 0;
+ best_div = 0;
+ for (clk = 0; clk < NUM_CLOCKS; clk++) {
+ for (div = 0; div < pwm_chip->pwm_total_pre_divs; div++) {
+ /* period_n = (PWM Period / N) */
+ /* tmp_p = (Pre-divide * Clock Period) * 2^m */
+ tmp_p = pt_t[div][clk];
+ for (m = 0; m <= PM8XXX_PWM_M_MAX; m++) {
+ if (period_n > tmp_p)
+ cur_err = period_n - tmp_p;
+ else
+ cur_err = tmp_p - period_n;
+
+ if (cur_err < min_err) {
+ min_err = cur_err;
+ best_m = m;
+ best_clk = clk;
+ best_div = div;
+ }
+
+ if (m && cur_err > last_err)
+ /* Break for bigger cur_err */
+ break;
+
+ last_err = cur_err;
+ tmp_p <<= 1;
+ }
+ }
+ }
+
+ /* Use higher resolution */
+ if (best_m >= 3 && n == 6) {
+ n += 3;
+ best_m -= 3;
+ }
+
+ period->pwm_size = n;
+ period->clk = best_clk;
+ period->pre_div = best_div;
+ period->pre_div_exp = best_m;
+}
+
+static void pm8xxx_pwm_calc_pwm_value(struct pwm_device *pwm,
+ unsigned int period_us,
+ unsigned int duty_us)
+{
+ unsigned int max_pwm_value, tmp;
+
+ /* Figure out pwm_value with overflow handling */
+ tmp = 1 << (sizeof(tmp) * 8 - pwm->period.pwm_size);
+ if (duty_us < tmp) {
+ tmp = duty_us << pwm->period.pwm_size;
+ pwm->pwm_value = tmp / period_us;
+ } else {
+ tmp = period_us >> pwm->period.pwm_size;
+ pwm->pwm_value = duty_us / tmp;
+ }
+ max_pwm_value = (1 << pwm->period.pwm_size) - 1;
+ if (pwm->pwm_value > max_pwm_value)
+ pwm->pwm_value = max_pwm_value;
+}
+
+static int pm8xxx_pwm_change_table(struct pwm_device *pwm, int duty_pct[],
+ int start_idx, int len, int raw_value)
+{
+ unsigned int pwm_value, max_pwm_value;
+ u8 cfg0, cfg1;
+ int i, pwm_size;
+ int rc = 0;
+
+ pwm_size = (pwm->pwm_lpg_ctl[5] & PM8XXX_PWM_SIZE_9_BIT) ? 9 : 6;
+ max_pwm_value = (1 << pwm_size) - 1;
+ for (i = 0; i < len; i++) {
+ if (raw_value)
+ pwm_value = duty_pct[i];
+ else
+ pwm_value = (duty_pct[i] << pwm_size) / 100;
+
+ if (pwm_value > max_pwm_value)
+ pwm_value = max_pwm_value;
+ cfg0 = pwm_value;
+ cfg1 = (pwm_value >> 1) & 0x80;
+ cfg1 |= start_idx + i;
+
+ rc = pm8xxx_writeb(pwm->chip->dev->parent,
+ SSBI_REG_ADDR_LPG_LUT_CFG0, cfg0);
+ if (rc)
+ break;
+
+ rc = pm8xxx_writeb(pwm->chip->dev->parent,
+ SSBI_REG_ADDR_LPG_LUT_CFG1, cfg1);
+ if (rc)
+ break;
+ }
+ return rc;
+}
+
+static void pm8xxx_pwm_save_index(struct pwm_device *pwm,
+ int low_idx, int high_idx, int flags)
+{
+ pwm->pwm_lpg_ctl[1] = high_idx & PM8XXX_PWM_HIGH_INDEX_MASK;
+ pwm->pwm_lpg_ctl[2] = low_idx & PM8XXX_PWM_LOW_INDEX_MASK;
+
+ if (flags & PM_PWM_LUT_REVERSE)
+ pwm->pwm_lpg_ctl[1] |= PM8XXX_PWM_REVERSE_EN;
+ if (flags & PM_PWM_LUT_RAMP_UP)
+ pwm->pwm_lpg_ctl[2] |= PM8XXX_PWM_RAMP_UP;
+ if (flags & PM_PWM_LUT_LOOP)
+ pwm->pwm_lpg_ctl[2] |= PM8XXX_PWM_LOOP_EN;
+}
+
+static void pm8xxx_pwm_save_period(struct pwm_device *pwm)
+{
+ u8 mask, val;
+
+ if (pwm_chip->is_lpg_supported) {
+ val = ((pwm->period.clk + 1) << PM8XXX_LPG_PWM_CLK_SEL_SHIFT)
+ & PM8XXX_LPG_PWM_CLK_SEL_MASK;
+ val |= (pwm->period.pre_div << PM8XXX_LPG_PWM_PREDIVIDE_SHIFT)
+ & PM8XXX_LPG_PWM_PREDIVIDE_MASK;
+ val |= pwm->period.pre_div_exp & PM8XXX_LPG_PWM_M_MASK;
+ mask = PM8XXX_LPG_PWM_CLK_SEL_MASK |
+ PM8XXX_LPG_PWM_PREDIVIDE_MASK | PM8XXX_LPG_PWM_M_MASK;
+ pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[4], mask, val);
+
+ val = (pwm->period.pwm_size > 6) ? PM8XXX_PWM_SIZE_9_BIT : 0;
+ mask = PM8XXX_PWM_SIZE_9_BIT;
+ pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[5], mask, val);
+ } else {
+ val = ((pwm->period.clk + 1) << PM8XXX_PWM_CLK_SEL_SHIFT)
+ & PM8XXX_PWM_CLK_SEL_MASK;
+ val |= (pwm->period.pre_div << PM8XXX_PWM_PREDIVIDE_SHIFT)
+ & PM8XXX_PWM_PREDIVIDE_MASK;
+ val |= (pwm->period.pre_div_exp << PM8XXX_PWM_M_SHIFT)
+ & PM8XXX_PWM_M_MASK;
+ val |= (((pwm->period.pwm_size > 6) ? PM8XXX_PWM_SIZE_9_BIT : 0)
+ << PM8XXX_PWM_SIZE_SHIFT) & PM8XXX_PWM_SIZE_MASK;
+
+ mask = PM8XXX_PWM_CLK_SEL_MASK | PM8XXX_PWM_PREDIVIDE_MASK |
+ PM8XXX_PWM_M_MASK | PM8XXX_PWM_SIZE_MASK;
+ pm8xxx_pwm_save(&pwm->pwm_ctl1, mask, val);
+ }
+}
+
+static void pm8xxx_pwm_save_pwm_value(struct pwm_device *pwm)
+{
+ u8 mask, val;
+
+ if (pwm_chip->is_lpg_supported) {
+ val = (pwm->period.pwm_size > 6) ? (pwm->pwm_value >> 1) : 0;
+ pwm->pwm_lpg_ctl[3] = pwm->pwm_value;
+ mask = PM8XXX_PWM_VALUE_BIT8;
+ pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[4], mask, val);
+ } else {
+ val = (pwm->period.pwm_size > 6) ? (pwm->pwm_value >> 8) : 0;
+ pwm->pwm_ctl2 = pwm->pwm_value;
+ mask = PM8XXX_PWM_VALUE_BIT0;
+ pm8xxx_pwm_save(&pwm->pwm_ctl1, mask, val);
+ }
+}
+
+static void pm8xxx_pwm_save_duty_time(struct pwm_device *pwm,
+ struct pm8xxx_pwm_lut *lut)
+{
+ int i;
+ u8 mask, val;
+
+ /* Linear search for duty time */
+ for (i = 0; i < PM8XXX_PWM_1KHZ_COUNT_MAX; i++) {
+ if (duty_msec[i] >= lut->lut_duty_ms)
+ break;
+ }
+ val = i << PM8XXX_PWM_1KHZ_COUNT_SHIFT;
+
+ mask = PM8XXX_PWM_1KHZ_COUNT_MASK;
+ pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[0], mask, val);
+}
+
+static void pm8xxx_pwm_save_pause(struct pwm_device *pwm,
+ struct pm8xxx_pwm_lut *lut)
+{
+ int i, pause_cnt, time_cnt;
+ u8 mask, val;
+
+ time_cnt = (pwm->pwm_lpg_ctl[0] & PM8XXX_PWM_1KHZ_COUNT_MASK)
+ >> PM8XXX_PWM_1KHZ_COUNT_SHIFT;
+ if (lut->flags & PM_PWM_LUT_PAUSE_HI_EN) {
+ pause_cnt = (lut->lut_pause_hi + duty_msec[time_cnt] / 2)
+ / duty_msec[time_cnt];
+ /* Linear search for pause time */
+ for (i = 0; i < PM8XXX_PWM_PAUSE_COUNT_MAX; i++) {
+ if (pause_count[i] >= pause_cnt)
+ break;
+ }
+ val = (i << PM8XXX_PWM_PAUSE_COUNT_HI_SHIFT) &
+ PM8XXX_PWM_PAUSE_COUNT_HI_MASK;
+ val |= PM8XXX_PWM_PAUSE_ENABLE_HIGH;
+ } else {
+ val = 0;
+ }
+
+ mask = PM8XXX_PWM_PAUSE_COUNT_HI_MASK | PM8XXX_PWM_PAUSE_ENABLE_HIGH;
+ pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[5], mask, val);
+
+ if (lut->flags & PM_PWM_LUT_PAUSE_LO_EN) {
+ /* Linear search for pause time */
+ pause_cnt = (lut->lut_pause_lo + duty_msec[time_cnt] / 2)
+ / duty_msec[time_cnt];
+ for (i = 0; i < PM8XXX_PWM_PAUSE_COUNT_MAX; i++) {
+ if (pause_count[i] >= pause_cnt)
+ break;
+ }
+ val = (i << PM8XXX_PWM_PAUSE_COUNT_LO_SHIFT) &
+ PM8XXX_PWM_PAUSE_COUNT_LO_MASK;
+ val |= PM8XXX_PWM_PAUSE_ENABLE_LOW;
+ } else {
+ val = 0;
+ }
+
+ mask = PM8XXX_PWM_PAUSE_COUNT_LO_MASK | PM8XXX_PWM_PAUSE_ENABLE_LOW;
+ pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[6], mask, val);
+}
+
+static int pm8xxx_pwm_write(struct pwm_device *pwm)
+{
+ int rc = 0;
+
+ rc = pm8xxx_writeb(pwm->chip->dev->parent,
+ SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id),
+ pwm->pwm_ctl1);
+ if (rc) {
+ pr_err("pm8xxx_writeb() failed: rc=%d (PWM Ctl1[%d])\n",
+ rc, pwm->pwm_id);
+ return rc;
+ }
+
+ rc = pm8xxx_writeb(pwm->chip->dev->parent,
+ SSBI_REG_ADDR_PWM_CTL2(pwm->pwm_id),
+ pwm->pwm_ctl2);
+ if (rc) {
+ pr_err("pm8xxx_writeb() failed: rc=%d (PWM Ctl2[%d])\n",
+ rc, pwm->pwm_id);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int pm8xxx_lpg_pwm_write(struct pwm_device *pwm, int start, int end)
+{
+ int i, rc;
+
+ /* Write in reverse way so 0 would be the last */
+ for (i = end - 1; i >= start; i--) {
+ rc = pm8xxx_writeb(pwm->chip->dev->parent,
+ SSBI_REG_ADDR_LPG_CTL(i),
+ pwm->pwm_lpg_ctl[i]);
+ if (rc) {
+ pr_err("pm8xxx_writeb(): rc=%d (PWM Ctl[%d])\n", rc, i);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int pm8xxx_pwm_change_lut(struct pwm_device *pwm,
+ struct pm8xxx_pwm_lut *lut)
+{
+ int rc;
+
+ pm8xxx_pwm_save_index(pwm, lut->lut_lo_index,
+ lut->lut_hi_index, lut->flags);
+ pm8xxx_pwm_save_duty_time(pwm, lut);
+ pm8xxx_pwm_save_pause(pwm, lut);
+ pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1], PM8XXX_PWM_BYPASS_LUT, 0);
+
+ pm8xxx_pwm_bank_sel(pwm);
+ rc = pm8xxx_lpg_pwm_write(pwm, 0, 7);
+
+ return rc;
+}
+
+static int pm8xxx_pwm_set_dtest(struct pwm_device *pwm, int enable)
+{
+ int rc;
+ u8 reg;
+
+ reg = pwm->pwm_id & PM8XXX_PWM_DTEST_BANK_MASK;
+
+ if (enable) {
+ /* Observe LPG_OUT on DTEST1*/
+ reg |= (1 << PM8XXX_PWM_DTEST_SHIFT) &
+ PM8XXX_PWM_DTEST_MASK;
+ }
+
+ rc = pm8xxx_writeb(pwm->chip->dev->parent,
+ SSBI_REG_ADDR_LPG_TEST, reg);
+ if (rc)
+ pr_err("pm8xxx_write(DTEST=0x%x) failed: rc=%d\n",
+ reg, rc);
+
+ 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_chip == NULL) {
+ pr_err("No pwm_chip\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ if (pwm_id >= pwm_chip->pwm_channels || pwm_id < 0) {
+ pr_err("Invalid pwm_id: %d with %s\n",
+ pwm_id, label ? label : ".");
+ return ERR_PTR(-EINVAL);
+ }
+
+ 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) {
+ if (pwm_chip->is_lpg_supported) {
+ pm8xxx_pwm_bank_sel(pwm);
+ pm8xxx_pwm_start(pwm, 0, 0);
+ } else {
+ pm8xxx_pwm_disable(pwm);
+ }
+ pwm->in_use = 0;
+ pwm->label = NULL;
+ }
+ if (pwm_chip->is_lpg_supported)
+ 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_period *period;
+ int rc = 0;
+
+ 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;
+ }
+
+ period = &pwm->period;
+
+ mutex_lock(&pwm->chip->pwm_mutex);
+
+ if (!pwm->in_use) {
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (pwm->pwm_period != period_us) {
+ pm8xxx_pwm_calc_period(period_us, period);
+ pm8xxx_pwm_save_period(pwm);
+ pwm->pwm_period = period_us;
+ }
+
+ pm8xxx_pwm_calc_pwm_value(pwm, period_us, duty_us);
+ pm8xxx_pwm_save_pwm_value(pwm);
+
+ if (pwm_chip->is_lpg_supported) {
+ pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1],
+ PM8XXX_PWM_BYPASS_LUT, PM8XXX_PWM_BYPASS_LUT);
+
+ pm8xxx_pwm_bank_sel(pwm);
+ rc = pm8xxx_lpg_pwm_write(pwm, 1, 6);
+ } else {
+ rc = pm8xxx_pwm_write(pwm);
+ }
+
+ pr_debug("duty/period=%u/%u usec: pwm_value=%d (of %d)\n",
+ (unsigned)duty_us, (unsigned)period_us,
+ pwm->pwm_value, 1 << period->pwm_size);
+
+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 = 0;
+
+ 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 {
+ if (pwm_chip->is_lpg_supported) {
+ if (pwm->dtest_mode_supported)
+ pm8xxx_pwm_set_dtest(pwm, 1);
+ rc = pm8xxx_pwm_bank_enable(pwm, 1);
+ pm8xxx_pwm_bank_sel(pwm);
+ pm8xxx_pwm_start(pwm, 1, 0);
+ } else {
+ pm8xxx_pwm_enable(pwm);
+ }
+ }
+ 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) {
+ if (pwm_chip->is_lpg_supported) {
+ if (pwm->dtest_mode_supported)
+ pm8xxx_pwm_set_dtest(pwm, 0);
+ pm8xxx_pwm_bank_sel(pwm);
+ pm8xxx_pwm_start(pwm, 0, 0);
+ pm8xxx_pwm_bank_enable(pwm, 0);
+ } else {
+ pm8xxx_pwm_disable(pwm);
+ }
+ }
+ mutex_unlock(&pwm->chip->pwm_mutex);
+}
+EXPORT_SYMBOL_GPL(pwm_disable);
+
+/**
+ * pm8xxx_pwm_config_period - change PWM period
+ *
+ * @pwm: the PWM device
+ * @pwm_p: period in struct pm8xxx_pwm_period
+ */
+int pm8xxx_pwm_config_period(struct pwm_device *pwm,
+ struct pm8xxx_pwm_period *period)
+{
+ int rc;
+
+ if (pwm == NULL || IS_ERR(pwm) || period == NULL)
+ return -EINVAL;
+ if (pwm->chip == NULL)
+ return -ENODEV;
+
+ mutex_lock(&pwm->chip->pwm_mutex);
+
+ if (!pwm->in_use) {
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+
+ pwm->period.pwm_size = period->pwm_size;
+ pwm->period.clk = period->clk;
+ pwm->period.pre_div = period->pre_div;
+ pwm->period.pre_div_exp = period->pre_div_exp;
+
+ pm8xxx_pwm_save_period(pwm);
+
+ if (pwm_chip->is_lpg_supported) {
+ pm8xxx_pwm_bank_sel(pwm);
+ rc = pm8xxx_lpg_pwm_write(pwm, 4, 6);
+ } else {
+ rc = pm8xxx_pwm_write(pwm);
+ }
+
+
+out_unlock:
+ mutex_unlock(&pwm->chip->pwm_mutex);
+ return rc;
+}
+EXPORT_SYMBOL(pm8xxx_pwm_config_period);
+
+/**
+ * pm8xxx_pwm_config_pwm_value - change a PWM device configuration
+ * @pwm: the PWM device
+ * @pwm_value: the duty cycle in raw PWM value (< 2^pwm_size)
+ */
+int pm8xxx_pwm_config_pwm_value(struct pwm_device *pwm, int pwm_value)
+{
+ int rc = 0;
+
+ if (pwm == NULL || IS_ERR(pwm))
+ return -EINVAL;
+ if (pwm->chip == NULL)
+ return -ENODEV;
+
+ mutex_lock(&pwm->chip->pwm_mutex);
+
+ if (!pwm->in_use || !pwm->pwm_period) {
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (pwm->pwm_value == pwm_value)
+ goto out_unlock;
+
+ pwm->pwm_value = pwm_value;
+
+ pm8xxx_pwm_save_pwm_value(pwm);
+
+ if (pwm_chip->is_lpg_supported) {
+ pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1],
+ PM8XXX_PWM_BYPASS_LUT, PM8XXX_PWM_BYPASS_LUT);
+ pm8xxx_pwm_bank_sel(pwm);
+ rc = pm8xxx_lpg_pwm_write(pwm, 1, 6);
+ } else {
+ rc = pm8xxx_pwm_write(pwm);
+ }
+
+ if (rc)
+ pr_err("[%d]: pm8xxx_pwm_write: rc=%d\n", pwm->pwm_id, rc);
+
+out_unlock:
+ mutex_unlock(&pwm->chip->pwm_mutex);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_pwm_config_pwm_value);
+
+/**
+ * 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_lut lut;
+ struct pm8xxx_pwm_period *period;
+ int 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 (pwm->chip->is_lpg_supported == 0) {
+ pr_err("LPG module isn't supported\n");
+ return -EINVAL;
+ }
+
+ 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;
+ }
+
+ period = &pwm->period;
+ 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;
+ }
+
+ if (pwm->pwm_period != period_us) {
+ pm8xxx_pwm_calc_period(period_us, period);
+ pm8xxx_pwm_save_period(pwm);
+ pwm->pwm_period = period_us;
+ }
+
+ len = (idx_len > PM_PWM_LUT_SIZE) ? PM_PWM_LUT_SIZE : idx_len;
+
+ if (flags & PM_PWM_LUT_NO_TABLE)
+ goto after_table_write;
+
+ rc = pm8xxx_pwm_change_table(pwm, duty_pct, start_idx, len, 0);
+ if (rc) {
+ pr_err("pm8xxx_pwm_change_table: rc=%d\n", rc);
+ goto out_unlock;
+ }
+
+after_table_write:
+ lut.lut_duty_ms = duty_time_ms;
+ lut.lut_lo_index = start_idx;
+ lut.lut_hi_index = start_idx + len - 1;
+ lut.lut_pause_lo = pause_lo;
+ lut.lut_pause_hi = pause_hi;
+ lut.flags = flags;
+ pwm->bypass_lut = 0;
+
+ rc = pm8xxx_pwm_change_lut(pwm, &lut);
+
+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;
+ }
+ if (pwm->chip->is_lpg_supported == 0) {
+ pr_err("LPG module isn't supported\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&pwm->chip->pwm_mutex);
+ if (start) {
+ if (pwm->dtest_mode_supported)
+ pm8xxx_pwm_set_dtest(pwm, 1);
+
+ pm8xxx_pwm_bank_enable(pwm, 1);
+
+ pm8xxx_pwm_bank_sel(pwm);
+ pm8xxx_pwm_start(pwm, 1, 1);
+ } else {
+ if (pwm->dtest_mode_supported)
+ pm8xxx_pwm_set_dtest(pwm, 0);
+
+ 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;
+};
+
+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;
+ } else {
+ pr_debug("[id=%d] pwm_request ok\n", puser->pwm_id);
+ puser->pwm = tmp;
+ }
+ }
+}
+
+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 / 100;
+ pwm_config(puser->pwm, duty_us, 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;
+ int rc = 0;
+
+ 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;
+ }
+
+ dbgdev->user = kcalloc(pwm_chip->pwm_channels,
+ sizeof(struct pm8xxx_pwm_user), GFP_KERNEL);
+ if (dbgdev->user == NULL) {
+ pr_err("kcalloc() failed.\n");
+ rc = -ENOMEM;
+ goto user_error;
+ }
+
+ 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);
+ rc = -ENOMEM;
+ goto dir_error;
+ }
+
+ dbgdev->dent = dent;
+
+ for (i = 0; i < pwm_chip->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);
+ rc = -ENOMEM;
+ 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);
+ rc = -ENOMEM;
+ 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);
+ rc = -ENOMEM;
+ 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);
+ rc = -ENOMEM;
+ goto debug_error;
+ }
+ }
+
+ pmic_dbg_device = dbgdev;
+
+ return 0;
+
+debug_error:
+ debugfs_remove_recursive(dbgdev->dent);
+dir_error:
+ kfree(dbgdev->user);
+user_error:
+ kfree(dbgdev);
+ return rc;
+}
+
+static int __devexit pm8xxx_pwm_dbg_remove(void)
+{
+ if (pmic_dbg_device) {
+ kfree(pmic_dbg_device->user);
+ 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)
+{
+ const struct pm8xxx_pwm_platform_data *pdata = pdev->dev.platform_data;
+ struct pm8xxx_pwm_chip *chip;
+ int i, dtest_channel;
+ enum pm8xxx_version version;
+
+ chip = kzalloc(sizeof *chip, GFP_KERNEL);
+ if (chip == NULL) {
+ pr_err("kzalloc() failed.\n");
+ return -ENOMEM;
+ }
+
+ if (pdata != NULL)
+ dtest_channel = pdata->dtest_channel;
+ else
+ dtest_channel = -1;
+
+ mutex_init(&chip->pwm_mutex);
+
+ chip->dev = &pdev->dev;
+ pwm_chip = chip;
+
+ version = pm8xxx_get_version(chip->dev->parent);
+
+ if (version == PM8XXX_VERSION_8921 ||
+ version == PM8XXX_VERSION_8058 ||
+ version == PM8XXX_VERSION_8922 ||
+ version == PM8XXX_VERSION_8038) {
+ chip->is_lpg_supported = 1;
+ }
+ if (chip->is_lpg_supported) {
+ if (version == PM8XXX_VERSION_8922 ||
+ version == PM8XXX_VERSION_8038) {
+ for (i = 0; i < NUM_CLOCKS; i++)
+ pt_t[0][i] /= PRE_DIVIDE_2;
+ chip->pwm_channels = PM8XXX_LPG_V1_PWM_CHANNELS;
+ } else {
+ chip->pwm_channels = PM8XXX_LPG_V0_PWM_CHANNELS;
+ }
+ chip->pwm_total_pre_divs = NUM_LPG_PRE_DIVIDE;
+ } else {
+ chip->pwm_channels = PM8XXX_PWM_CHANNELS;
+ chip->pwm_total_pre_divs = NUM_PWM_PRE_DIVIDE;
+ }
+
+ chip->pwm_dev = kcalloc(chip->pwm_channels, sizeof(struct pwm_device),
+ GFP_KERNEL);
+ if (chip->pwm_dev == NULL) {
+ pr_err("kcalloc() failed.\n");
+ mutex_destroy(&chip->pwm_mutex);
+ kfree(chip);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < chip->pwm_channels; i++) {
+ chip->pwm_dev[i].pwm_id = i;
+ chip->pwm_dev[i].chip = chip;
+ if (i == dtest_channel)
+ chip->pwm_dev[i].dtest_mode_supported = 1;
+ }
+
+ 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();
+ kfree(chip->pwm_dev);
+ 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/pm8xxx-spk.c b/drivers/mfd/pm8xxx-spk.c
new file mode 100644
index 0000000..297ddfa
--- /dev/null
+++ b/drivers/mfd/pm8xxx-spk.c
@@ -0,0 +1,279 @@
+/* Copyright (c) 2012, 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/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/stddef.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/spk.h>
+
+#define PM8XXX_SPK_CTL1_REG_OFF 0
+#define PM8XXX_SPK_TEST_REG_1_OFF 1
+#define PM8XXX_SPK_TEST_REG_2_OFF 2
+
+#define PM8XXX_SPK_BANK_SEL 4
+#define PM8XXX_SPK_BANK_WRITE 0x80
+#define PM8XXX_SPK_BANK_VAL_MASK 0xF
+
+#define BOOST_6DB_GAIN_EN_MASK 0x8
+#define VSEL_LD0_1P1 0x0
+#define VSEL_LD0_1P2 0x2
+#define VSEL_LD0_1P0 0x4
+
+#define PWM_EN_MASK 0xF
+#define PM8XXX_SPK_TEST_REG_1_BANKS 8
+#define PM8XXX_SPK_TEST_REG_2_BANKS 2
+
+#define PM8XXX_SPK_GAIN 0x5
+#define PM8XXX_ADD_EN 0x1
+
+struct pm8xxx_spk_chip {
+ struct list_head link;
+ struct pm8xxx_spk_platform_data pdata;
+ struct device *dev;
+ enum pm8xxx_version version;
+ struct mutex spk_mutex;
+ u16 base;
+ u16 end;
+};
+
+static struct pm8xxx_spk_chip *the_spk_chip;
+
+static inline bool spk_defined(void)
+{
+ if (the_spk_chip == NULL || IS_ERR(the_spk_chip))
+ return false;
+ return true;
+}
+
+static int pm8xxx_spk_bank_write(u16 reg, u16 bank, u8 val)
+{
+ int rc = 0;
+ u8 bank_val = PM8XXX_SPK_BANK_WRITE | (bank << PM8XXX_SPK_BANK_SEL);
+
+ bank_val |= (val & PM8XXX_SPK_BANK_VAL_MASK);
+ mutex_lock(&the_spk_chip->spk_mutex);
+ rc = pm8xxx_writeb(the_spk_chip->dev->parent, reg, bank_val);
+ if (rc)
+ pr_err("pm8xxx_writeb(): rc=%d\n", rc);
+ mutex_unlock(&the_spk_chip->spk_mutex);
+ return rc;
+}
+
+
+static int pm8xxx_spk_read(u16 addr)
+{
+ int rc = 0;
+ u8 val = 0;
+
+ mutex_lock(&the_spk_chip->spk_mutex);
+ rc = pm8xxx_readb(the_spk_chip->dev->parent,
+ the_spk_chip->base + addr, &val);
+ if (rc) {
+ pr_err("pm8xxx_spk_readb() failed: rc=%d\n", rc);
+ val = rc;
+ }
+ mutex_unlock(&the_spk_chip->spk_mutex);
+
+ return val;
+}
+
+static int pm8xxx_spk_write(u16 addr, u8 val)
+{
+ int rc = 0;
+
+ mutex_lock(&the_spk_chip->spk_mutex);
+ rc = pm8xxx_writeb(the_spk_chip->dev->parent,
+ the_spk_chip->base + addr, val);
+ if (rc)
+ pr_err("pm8xxx_writeb() failed: rc=%d\n", rc);
+ mutex_unlock(&the_spk_chip->spk_mutex);
+ return rc;
+}
+
+int pm8xxx_spk_mute(bool mute)
+{
+ u8 val = 0;
+ int ret = 0;
+ if (spk_defined() == false) {
+ pr_err("Invalid spk handle or no spk_chip\n");
+ return -ENODEV;
+ }
+
+ val = pm8xxx_spk_read(PM8XXX_SPK_CTL1_REG_OFF);
+ if (val < 0)
+ return val;
+ val |= mute << 2;
+ ret = pm8xxx_spk_write(PM8XXX_SPK_CTL1_REG_OFF, val);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_spk_mute);
+
+int pm8xxx_spk_gain(u8 gain)
+{
+ u8 val;
+ int ret = 0;
+
+ if (spk_defined() == false) {
+ pr_err("Invalid spk handle or no spk_chip\n");
+ return -ENODEV;
+ }
+
+ val = pm8xxx_spk_read(PM8XXX_SPK_CTL1_REG_OFF);
+ if (val < 0)
+ return val;
+ val |= (gain << 4);
+ ret = pm8xxx_spk_write(PM8XXX_SPK_CTL1_REG_OFF, val);
+ if (!ret) {
+ pm8xxx_spk_bank_write(the_spk_chip->base
+ + PM8XXX_SPK_TEST_REG_1_OFF,
+ 0, BOOST_6DB_GAIN_EN_MASK | VSEL_LD0_1P2);
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_spk_gain);
+
+int pm8xxx_spk_enable(int enable)
+{
+ int val = 0;
+ u16 addr;
+ int ret = 0;
+
+ if (spk_defined() == false) {
+ pr_err("Invalid spk handle or no spk_chip\n");
+ return -ENODEV;
+ }
+
+ addr = the_spk_chip->base + PM8XXX_SPK_TEST_REG_1_OFF;
+ val = pm8xxx_spk_read(PM8XXX_SPK_CTL1_REG_OFF);
+ if (val < 0)
+ return val;
+ val |= (enable << 3);
+ ret = pm8xxx_spk_write(PM8XXX_SPK_CTL1_REG_OFF, val);
+ if (!ret)
+ ret = pm8xxx_spk_bank_write(addr, 6, PWM_EN_MASK);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_spk_enable);
+
+static int pm8xxx_spk_config(void)
+{
+ u16 addr;
+ int ret = 0;
+
+ if (spk_defined() == false) {
+ pr_err("Invalid spk handle or no spk_chip\n");
+ return -ENODEV;
+ }
+
+ addr = the_spk_chip->base + PM8XXX_SPK_TEST_REG_1_OFF;
+ ret = pm8xxx_spk_bank_write(addr, 6, PWM_EN_MASK & 0);
+ if (!ret)
+ ret = pm8xxx_spk_gain(PM8XXX_SPK_GAIN);
+ return ret;
+}
+
+static int __devinit pm8xxx_spk_probe(struct platform_device *pdev)
+{
+ const struct pm8xxx_spk_platform_data *pdata = pdev->dev.platform_data;
+ int ret = 0;
+
+ if (!pdata) {
+ pr_err("missing platform data\n");
+ return -EINVAL;
+ }
+
+ the_spk_chip = kzalloc(sizeof(struct pm8xxx_spk_chip), GFP_KERNEL);
+ if (the_spk_chip == NULL) {
+ pr_err("kzalloc() failed.\n");
+ return -ENOMEM;
+ }
+
+ mutex_init(&the_spk_chip->spk_mutex);
+
+ the_spk_chip->dev = &pdev->dev;
+ the_spk_chip->version = pm8xxx_get_version(the_spk_chip->dev->parent);
+ switch (pm8xxx_get_version(the_spk_chip->dev->parent)) {
+ case PM8XXX_VERSION_8038:
+ break;
+ default:
+ ret = -ENODEV;
+ goto err_handle;
+ }
+
+ memcpy(&(the_spk_chip->pdata), pdata,
+ sizeof(struct pm8xxx_spk_platform_data));
+
+ the_spk_chip->base = pdev->resource[0].start;
+ the_spk_chip->end = pdev->resource[0].end;
+
+ if (the_spk_chip->pdata.spk_add_enable) {
+ int val;
+ val = pm8xxx_spk_read(PM8XXX_SPK_CTL1_REG_OFF);
+ if (val < 0) {
+ ret = val;
+ goto err_handle;
+ }
+ val |= (the_spk_chip->pdata.spk_add_enable & PM8XXX_ADD_EN);
+ ret = pm8xxx_spk_write(PM8XXX_SPK_CTL1_REG_OFF, val);
+ if (ret < 0)
+ goto err_handle;
+ }
+ return pm8xxx_spk_config();
+err_handle:
+ pr_err("pm8xxx_spk_probe failed."
+ "Audio unavailable on speaker.\n");
+ mutex_destroy(&the_spk_chip->spk_mutex);
+ kfree(the_spk_chip);
+ return ret;
+}
+
+static int __devexit pm8xxx_spk_remove(struct platform_device *pdev)
+{
+ if (spk_defined() == false) {
+ pr_err("Invalid spk handle or no spk_chip\n");
+ return -ENODEV;
+ }
+ mutex_destroy(&the_spk_chip->spk_mutex);
+ kfree(the_spk_chip);
+ return 0;
+}
+
+static struct platform_driver pm8xxx_spk_driver = {
+ .probe = pm8xxx_spk_probe,
+ .remove = __devexit_p(pm8xxx_spk_remove),
+ .driver = {
+ .name = PM8XXX_SPK_DEV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pm8xxx_spk_init(void)
+{
+ return platform_driver_register(&pm8xxx_spk_driver);
+}
+subsys_initcall(pm8xxx_spk_init);
+
+static void __exit pm8xxx_spk_exit(void)
+{
+ platform_driver_unregister(&pm8xxx_spk_driver);
+}
+module_exit(pm8xxx_spk_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PM8XXX SPK driver");
+MODULE_ALIAS("platform:" PM8XXX_SPK_DEV_NAME);
diff --git a/drivers/mfd/pmic8058.c b/drivers/mfd/pmic8058.c
new file mode 100644
index 0000000..8f7ab2d
--- /dev/null
+++ b/drivers/mfd/pmic8058.c
@@ -0,0 +1,792 @@
+/* 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/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/msm_ssbi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/msm_adc.h>
+#include <linux/module.h>
+
+#define REG_MPP_BASE 0x50
+#define REG_IRQ_BASE 0x1BB
+
+/* PMIC8058 Revision */
+#define PM8058_REG_REV 0x002 /* PMIC4 revision */
+#define PM8058_VERSION_MASK 0xF0
+#define PM8058_REVISION_MASK 0x0F
+#define PM8058_VERSION_VALUE 0xE0
+
+/* PMIC 8058 Battery Alarm SSBI registers */
+#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 REG_TEMP_ALRM_CTRL 0x1B
+#define REG_TEMP_ALRM_PWM 0x9B
+
+/* 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
+
+/* GP_TEST1 register */
+#define SSBI_REG_ADDR_GP_TEST_1 0x07A
+
+#define PM8058_RTC_BASE 0x1E8
+#define PM8058_OTHC_CNTR_BASE0 0xA0
+#define PM8058_OTHC_CNTR_BASE1 0x134
+#define PM8058_OTHC_CNTR_BASE2 0x137
+
+#define SINGLE_IRQ_RESOURCE(_name, _irq) \
+{ \
+ .name = _name, \
+ .start = _irq, \
+ .end = _irq, \
+ .flags = IORESOURCE_IRQ, \
+}
+
+struct pm8058_chip {
+ struct pm8058_platform_data pdata;
+ struct device *dev;
+ struct pm_irq_chip *irq_chip;
+ struct mfd_cell *mfd_regulators, *mfd_xo_buffers;
+
+ u8 revision;
+};
+
+static int pm8058_readb(const struct device *dev, u16 addr, u8 *val)
+{
+ const struct pm8xxx_drvdata *pm8058_drvdata = dev_get_drvdata(dev);
+ const struct pm8058_chip *pmic = pm8058_drvdata->pm_chip_data;
+
+ return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
+}
+
+static int pm8058_writeb(const struct device *dev, u16 addr, u8 val)
+{
+ const struct pm8xxx_drvdata *pm8058_drvdata = dev_get_drvdata(dev);
+ const struct pm8058_chip *pmic = pm8058_drvdata->pm_chip_data;
+
+ return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
+}
+
+static int pm8058_read_buf(const struct device *dev, u16 addr, u8 *buf,
+ int cnt)
+{
+ const struct pm8xxx_drvdata *pm8058_drvdata = dev_get_drvdata(dev);
+ const struct pm8058_chip *pmic = pm8058_drvdata->pm_chip_data;
+
+ return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8058_write_buf(const struct device *dev, u16 addr, u8 *buf,
+ int cnt)
+{
+ const struct pm8xxx_drvdata *pm8058_drvdata = dev_get_drvdata(dev);
+ const struct pm8058_chip *pmic = pm8058_drvdata->pm_chip_data;
+
+ return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8058_read_irq_stat(const struct device *dev, int irq)
+{
+ const struct pm8xxx_drvdata *pm8058_drvdata = dev_get_drvdata(dev);
+ const struct pm8058_chip *pmic = pm8058_drvdata->pm_chip_data;
+
+ return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
+
+ return 0;
+}
+
+static enum pm8xxx_version pm8058_get_version(const struct device *dev)
+{
+ const struct pm8xxx_drvdata *pm8058_drvdata = dev_get_drvdata(dev);
+ const struct pm8058_chip *pmic = pm8058_drvdata->pm_chip_data;
+ enum pm8xxx_version version = -ENODEV;
+
+ if ((pmic->revision & PM8058_VERSION_MASK) == PM8058_VERSION_VALUE)
+ version = PM8XXX_VERSION_8058;
+
+ return version;
+}
+
+static int pm8058_get_revision(const struct device *dev)
+{
+ const struct pm8xxx_drvdata *pm8058_drvdata = dev_get_drvdata(dev);
+ const struct pm8058_chip *pmic = pm8058_drvdata->pm_chip_data;
+
+ return pmic->revision & PM8058_REVISION_MASK;
+}
+
+static struct pm8xxx_drvdata pm8058_drvdata = {
+ .pmic_readb = pm8058_readb,
+ .pmic_writeb = pm8058_writeb,
+ .pmic_read_buf = pm8058_read_buf,
+ .pmic_write_buf = pm8058_write_buf,
+ .pmic_read_irq_stat = pm8058_read_irq_stat,
+ .pmic_get_version = pm8058_get_version,
+ .pmic_get_revision = pm8058_get_revision,
+};
+
+static const struct resource pm8058_charger_resources[] __devinitconst = {
+ SINGLE_IRQ_RESOURCE("CHGVAL", PM8058_CHGVAL_IRQ),
+ SINGLE_IRQ_RESOURCE("CHGINVAL", PM8058_CHGINVAL_IRQ),
+ SINGLE_IRQ_RESOURCE("CHGILIM", PM8058_CHGILIM_IRQ),
+ SINGLE_IRQ_RESOURCE("VCP", PM8058_VCP_IRQ),
+ SINGLE_IRQ_RESOURCE("ATC_DONE", PM8058_ATC_DONE_IRQ),
+ SINGLE_IRQ_RESOURCE("ATCFAIL", PM8058_ATCFAIL_IRQ),
+ SINGLE_IRQ_RESOURCE("AUTO_CHGDONE", PM8058_AUTO_CHGDONE_IRQ),
+ SINGLE_IRQ_RESOURCE("AUTO_CHGFAIL", PM8058_AUTO_CHGFAIL_IRQ),
+ SINGLE_IRQ_RESOURCE("CHGSTATE", PM8058_CHGSTATE_IRQ),
+ SINGLE_IRQ_RESOURCE("FASTCHG", PM8058_FASTCHG_IRQ),
+ SINGLE_IRQ_RESOURCE("CHG_END", PM8058_CHG_END_IRQ),
+ SINGLE_IRQ_RESOURCE("BATTTEMP", PM8058_BATTTEMP_IRQ),
+ SINGLE_IRQ_RESOURCE("CHGHOT", PM8058_CHGHOT_IRQ),
+ SINGLE_IRQ_RESOURCE("CHGTLIMIT", PM8058_CHGTLIMIT_IRQ),
+ SINGLE_IRQ_RESOURCE("CHG_GONE", PM8058_CHG_GONE_IRQ),
+ SINGLE_IRQ_RESOURCE("VCPMAJOR", PM8058_VCPMAJOR_IRQ),
+ SINGLE_IRQ_RESOURCE("VBATDET", PM8058_VBATDET_IRQ),
+ SINGLE_IRQ_RESOURCE("BATFET", PM8058_BATFET_IRQ),
+ SINGLE_IRQ_RESOURCE("BATT_REPLACE", PM8058_BATT_REPLACE_IRQ),
+ SINGLE_IRQ_RESOURCE("BATTCONNECT", PM8058_BATTCONNECT_IRQ),
+ SINGLE_IRQ_RESOURCE("VBATDET_LOW", PM8058_VBATDET_LOW_IRQ),
+};
+
+static struct mfd_cell pm8058_charger_cell __devinitdata = {
+ .name = "pm8058-charger",
+ .id = -1,
+ .resources = pm8058_charger_resources,
+ .num_resources = ARRAY_SIZE(pm8058_charger_resources),
+};
+
+static const struct resource misc_cell_resources[] __devinitconst = {
+ SINGLE_IRQ_RESOURCE("pm8xxx_osc_halt_irq", PM8058_OSCHALT_IRQ),
+};
+
+static struct mfd_cell misc_cell __devinitdata = {
+ .name = PM8XXX_MISC_DEV_NAME,
+ .id = -1,
+ .resources = misc_cell_resources,
+ .num_resources = ARRAY_SIZE(misc_cell_resources),
+};
+
+static struct mfd_cell pm8058_pwm_cell __devinitdata = {
+ .name = "pm8058-pwm",
+ .id = -1,
+};
+
+static struct resource xoadc_resources[] = {
+ SINGLE_IRQ_RESOURCE(NULL, PM8058_ADC_IRQ),
+};
+
+static struct mfd_cell xoadc_cell __devinitdata = {
+ .name = "pm8058-xoadc",
+ .id = -1,
+ .resources = xoadc_resources,
+ .num_resources = ARRAY_SIZE(xoadc_resources),
+};
+
+static const struct resource thermal_alarm_cell_resources[] __devinitconst = {
+ SINGLE_IRQ_RESOURCE("pm8058_tempstat_irq", PM8058_TEMPSTAT_IRQ),
+ SINGLE_IRQ_RESOURCE("pm8058_overtemp_irq", PM8058_OVERTEMP_IRQ),
+};
+
+static struct pm8xxx_tm_core_data thermal_alarm_cdata = {
+ .adc_channel = CHANNEL_ADC_DIE_TEMP,
+ .adc_type = PM8XXX_TM_ADC_PM8058_ADC,
+ .reg_addr_temp_alarm_ctrl = REG_TEMP_ALRM_CTRL,
+ .reg_addr_temp_alarm_pwm = REG_TEMP_ALRM_PWM,
+ .tm_name = "pm8058_tz",
+ .irq_name_temp_stat = "pm8058_tempstat_irq",
+ .irq_name_over_temp = "pm8058_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 struct mfd_cell debugfs_cell __devinitdata = {
+ .name = "pm8xxx-debug",
+ .id = -1,
+ .platform_data = "pm8058-dbg",
+ .pdata_size = sizeof("pm8058-dbg"),
+};
+
+static const struct resource othc0_cell_resources[] __devinitconst = {
+ {
+ .name = "othc_base",
+ .start = PM8058_OTHC_CNTR_BASE0,
+ .end = PM8058_OTHC_CNTR_BASE0,
+ .flags = IORESOURCE_IO,
+ },
+};
+
+static const struct resource othc1_cell_resources[] __devinitconst = {
+ SINGLE_IRQ_RESOURCE(NULL, PM8058_SW_1_IRQ),
+ SINGLE_IRQ_RESOURCE(NULL, PM8058_IR_1_IRQ),
+ {
+ .name = "othc_base",
+ .start = PM8058_OTHC_CNTR_BASE1,
+ .end = PM8058_OTHC_CNTR_BASE1,
+ .flags = IORESOURCE_IO,
+ },
+};
+
+static const struct resource othc2_cell_resources[] __devinitconst = {
+ {
+ .name = "othc_base",
+ .start = PM8058_OTHC_CNTR_BASE2,
+ .end = PM8058_OTHC_CNTR_BASE2,
+ .flags = IORESOURCE_IO,
+ },
+};
+
+static const struct resource batt_alarm_cell_resources[] __devinitconst = {
+ SINGLE_IRQ_RESOURCE("pm8058_batt_alarm_irq", PM8058_BATT_ALARM_IRQ),
+};
+
+static struct mfd_cell leds_cell __devinitdata = {
+ .name = "pm8058-led",
+ .id = -1,
+};
+
+static struct mfd_cell othc0_cell __devinitdata = {
+ .name = "pm8058-othc",
+ .id = 0,
+ .resources = othc0_cell_resources,
+ .num_resources = ARRAY_SIZE(othc0_cell_resources),
+};
+
+static struct mfd_cell othc1_cell __devinitdata = {
+ .name = "pm8058-othc",
+ .id = 1,
+ .resources = othc1_cell_resources,
+ .num_resources = ARRAY_SIZE(othc1_cell_resources),
+};
+
+static struct mfd_cell othc2_cell __devinitdata = {
+ .name = "pm8058-othc",
+ .id = 2,
+ .resources = othc2_cell_resources,
+ .num_resources = ARRAY_SIZE(othc2_cell_resources),
+};
+
+static struct pm8xxx_batt_alarm_core_data batt_alarm_cdata = {
+ .irq_name = "pm8058_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 struct mfd_cell upl_cell __devinitdata = {
+ .name = PM8XXX_UPL_DEV_NAME,
+ .id = -1,
+};
+
+static struct mfd_cell nfc_cell __devinitdata = {
+ .name = PM8XXX_NFC_DEV_NAME,
+ .id = -1,
+};
+
+static const struct resource rtc_cell_resources[] __devinitconst = {
+ [0] = SINGLE_IRQ_RESOURCE(NULL, PM8058_RTC_ALARM_IRQ),
+ [1] = {
+ .name = "pmic_rtc_base",
+ .start = PM8058_RTC_BASE,
+ .end = PM8058_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, PM8058_PWRKEY_REL_IRQ),
+ SINGLE_IRQ_RESOURCE(NULL, PM8058_PWRKEY_PRESS_IRQ),
+};
+
+static struct mfd_cell vibrator_cell __devinitdata = {
+ .name = PM8XXX_VIBRATOR_DEV_NAME,
+ .id = -1,
+};
+
+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, PM8058_KEYPAD_IRQ),
+ SINGLE_IRQ_RESOURCE(NULL, PM8058_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 const struct resource mpp_cell_resources[] __devinitconst = {
+ {
+ .start = PM8058_IRQ_BLOCK_BIT(PM8058_MPP_BLOCK_START, 0),
+ .end = PM8058_IRQ_BLOCK_BIT(PM8058_MPP_BLOCK_START, 0)
+ + PM8058_MPPS - 1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell mpp_cell __devinitdata = {
+ .name = PM8XXX_MPP_DEV_NAME,
+ .id = 0,
+ .resources = mpp_cell_resources,
+ .num_resources = ARRAY_SIZE(mpp_cell_resources),
+};
+
+static const struct resource gpio_cell_resources[] __devinitconst = {
+ [0] = {
+ .start = PM8058_IRQ_BLOCK_BIT(PM8058_GPIO_BLOCK_START, 0),
+ .end = PM8058_IRQ_BLOCK_BIT(PM8058_GPIO_BLOCK_START, 0)
+ + PM8058_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 int __devinit
+pm8058_add_subdevices(const struct pm8058_platform_data *pdata,
+ struct pm8058_chip *pmic)
+{
+ int rc = 0, irq_base = 0, i;
+ struct pm_irq_chip *irq_chip;
+ static struct mfd_cell *mfd_regulators, *mfd_xo_buffers;
+
+ if (pdata->irq_pdata) {
+ pdata->irq_pdata->irq_cdata.nirqs = PM8058_NR_IRQS;
+ pdata->irq_pdata->irq_cdata.base_addr = REG_IRQ_BASE;
+ irq_base = pdata->irq_pdata->irq_base;
+ irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
+
+ if (IS_ERR(irq_chip)) {
+ pr_err("Failed to init interrupts ret=%ld\n",
+ PTR_ERR(irq_chip));
+ return PTR_ERR(irq_chip);
+ }
+ pmic->irq_chip = irq_chip;
+ }
+
+ if (pdata->gpio_pdata) {
+ pdata->gpio_pdata->gpio_cdata.ngpios = PM8058_GPIOS;
+ gpio_cell.platform_data = pdata->gpio_pdata;
+ gpio_cell.pdata_size = sizeof(struct pm8xxx_gpio_platform_data);
+ rc = mfd_add_devices(pmic->dev, 0, &gpio_cell, 1,
+ NULL, irq_base);
+ if (rc) {
+ pr_err("Failed to add gpio subdevice ret=%d\n", rc);
+ goto bail;
+ }
+ }
+
+ if (pdata->mpp_pdata) {
+ pdata->mpp_pdata->core_data.nmpps = PM8058_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);
+ rc = mfd_add_devices(pmic->dev, 0, &mpp_cell, 1, NULL,
+ irq_base);
+ if (rc) {
+ pr_err("Failed to add mpp subdevice ret=%d\n", rc);
+ goto bail;
+ }
+ }
+
+ 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 pm8058 regulator "
+ "mfd cells\n", sizeof(struct mfd_cell)
+ * (pdata->num_regulators));
+ rc = -ENOMEM;
+ goto bail;
+ }
+ for (i = 0; i < pdata->num_regulators; i++) {
+ mfd_regulators[i].name = "pm8058-regulator";
+ 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 pm8058_vreg_pdata);
+ }
+ rc = mfd_add_devices(pmic->dev, 0, mfd_regulators,
+ pdata->num_regulators, NULL, irq_base);
+ if (rc) {
+ pr_err("Failed to add regulator subdevices ret=%d\n",
+ rc);
+ kfree(mfd_regulators);
+ goto bail;
+ }
+ pmic->mfd_regulators = mfd_regulators;
+ }
+
+ if (pdata->num_xo_buffers > 0 && pdata->xo_buffer_pdata) {
+ mfd_xo_buffers = kzalloc(sizeof(struct mfd_cell)
+ * (pdata->num_xo_buffers), GFP_KERNEL);
+ if (!mfd_xo_buffers) {
+ pr_err("Cannot allocate %d bytes for pm8058 XO buffer "
+ "mfd cells\n", sizeof(struct mfd_cell)
+ * (pdata->num_xo_buffers));
+ rc = -ENOMEM;
+ goto bail;
+ }
+ for (i = 0; i < pdata->num_xo_buffers; i++) {
+ mfd_xo_buffers[i].name = PM8058_XO_BUFFER_DEV_NAME;
+ mfd_xo_buffers[i].id = pdata->xo_buffer_pdata[i].id;
+ mfd_xo_buffers[i].platform_data =
+ &(pdata->xo_buffer_pdata[i]);
+ mfd_xo_buffers[i].pdata_size =
+ sizeof(struct pm8058_xo_pdata);
+ }
+ rc = mfd_add_devices(pmic->dev, 0, mfd_xo_buffers,
+ pdata->num_xo_buffers, NULL, irq_base);
+ if (rc) {
+ pr_err("Failed to add XO buffer subdevices ret=%d\n",
+ rc);
+ kfree(mfd_xo_buffers);
+ goto bail;
+ }
+ pmic->mfd_xo_buffers = mfd_xo_buffers;
+ }
+
+ if (pdata->keypad_pdata) {
+ keypad_cell.platform_data = pdata->keypad_pdata;
+ keypad_cell.pdata_size =
+ sizeof(struct pm8xxx_keypad_platform_data);
+ rc = mfd_add_devices(pmic->dev, 0, &keypad_cell, 1, NULL,
+ irq_base);
+ if (rc) {
+ pr_err("Failed to add keypad subdevice ret=%d\n", rc);
+ goto bail;
+ }
+ }
+
+ if (pdata->rtc_pdata) {
+ rtc_cell.platform_data = pdata->rtc_pdata;
+ rtc_cell.pdata_size = sizeof(struct pm8xxx_rtc_platform_data);
+ rc = mfd_add_devices(pmic->dev, 0, &rtc_cell, 1, NULL,
+ irq_base);
+ if (rc) {
+ pr_err("Failed to add rtc subdevice ret=%d\n", rc);
+ goto bail;
+ }
+ }
+
+ if (pdata->pwrkey_pdata) {
+ pwrkey_cell.platform_data = pdata->pwrkey_pdata;
+ pwrkey_cell.pdata_size =
+ sizeof(struct pm8xxx_pwrkey_platform_data);
+ rc = mfd_add_devices(pmic->dev, 0, &pwrkey_cell, 1, NULL,
+ irq_base);
+ if (rc) {
+ pr_err("Failed to add pwrkey subdevice ret=%d\n", rc);
+ goto bail;
+ }
+ }
+
+ if (pdata->vibrator_pdata) {
+ vibrator_cell.platform_data = pdata->vibrator_pdata;
+ vibrator_cell.pdata_size =
+ sizeof(struct pm8xxx_vibrator_platform_data);
+ rc = mfd_add_devices(pmic->dev, 0, &vibrator_cell, 1, NULL,
+ irq_base);
+ if (rc) {
+ pr_err("Failed to add vibrator subdevice ret=%d\n",
+ rc);
+ goto bail;
+ }
+ }
+
+ if (pdata->leds_pdata) {
+ leds_cell.platform_data = pdata->leds_pdata;
+ leds_cell.pdata_size =
+ sizeof(struct pmic8058_leds_platform_data);
+ rc = mfd_add_devices(pmic->dev, 0, &leds_cell, 1, NULL,
+ irq_base);
+ if (rc) {
+ pr_err("Failed to add leds subdevice ret=%d\n", rc);
+ goto bail;
+ }
+ }
+
+ if (pdata->xoadc_pdata) {
+ xoadc_cell.platform_data = pdata->xoadc_pdata;
+ xoadc_cell.pdata_size =
+ sizeof(struct xoadc_platform_data);
+ rc = mfd_add_devices(pmic->dev, 0, &xoadc_cell, 1, NULL,
+ irq_base);
+ if (rc) {
+ pr_err("Failed to add leds subdevice ret=%d\n", rc);
+ goto bail;
+ }
+ }
+
+ if (pdata->othc0_pdata) {
+ othc0_cell.platform_data = pdata->othc0_pdata;
+ othc0_cell.pdata_size =
+ sizeof(struct pmic8058_othc_config_pdata);
+ rc = mfd_add_devices(pmic->dev, 0, &othc0_cell, 1, NULL, 0);
+ if (rc) {
+ pr_err("Failed to add othc0 subdevice ret=%d\n", rc);
+ goto bail;
+ }
+ }
+
+ if (pdata->othc1_pdata) {
+ othc1_cell.platform_data = pdata->othc1_pdata;
+ othc1_cell.pdata_size =
+ sizeof(struct pmic8058_othc_config_pdata);
+ rc = mfd_add_devices(pmic->dev, 0, &othc1_cell, 1, NULL,
+ irq_base);
+ if (rc) {
+ pr_err("Failed to add othc1 subdevice ret=%d\n", rc);
+ goto bail;
+ }
+ }
+
+ if (pdata->othc2_pdata) {
+ othc2_cell.platform_data = pdata->othc2_pdata;
+ othc2_cell.pdata_size =
+ sizeof(struct pmic8058_othc_config_pdata);
+ rc = mfd_add_devices(pmic->dev, 0, &othc2_cell, 1, NULL, 0);
+ if (rc) {
+ pr_err("Failed to add othc2 subdevice ret=%d\n", rc);
+ goto bail;
+ }
+ }
+
+ if (pdata->pwm_pdata) {
+ pm8058_pwm_cell.platform_data = pdata->pwm_pdata;
+ pm8058_pwm_cell.pdata_size = sizeof(struct pm8058_pwm_pdata);
+ rc = mfd_add_devices(pmic->dev, 0, &pm8058_pwm_cell, 1, NULL,
+ irq_base);
+ if (rc) {
+ pr_err("Failed to add pwm subdevice ret=%d\n", rc);
+ goto bail;
+ }
+ }
+
+ if (pdata->misc_pdata) {
+ misc_cell.platform_data = pdata->misc_pdata;
+ misc_cell.pdata_size = sizeof(struct pm8xxx_misc_platform_data);
+ rc = mfd_add_devices(pmic->dev, 0, &misc_cell, 1, NULL,
+ irq_base);
+ if (rc) {
+ pr_err("Failed to add misc subdevice ret=%d\n", rc);
+ goto bail;
+ }
+ }
+
+ rc = mfd_add_devices(pmic->dev, 0, &thermal_alarm_cell, 1, NULL,
+ irq_base);
+ if (rc) {
+ pr_err("Failed to add thermal alarm subdevice ret=%d\n",
+ rc);
+ goto bail;
+ }
+
+ rc = mfd_add_devices(pmic->dev, 0, &batt_alarm_cell, 1, NULL,
+ irq_base);
+ if (rc) {
+ pr_err("Failed to add battery alarm subdevice ret=%d\n",
+ rc);
+ goto bail;
+ }
+
+ rc = mfd_add_devices(pmic->dev, 0, &upl_cell, 1, NULL, 0);
+ if (rc) {
+ pr_err("Failed to add upl subdevice ret=%d\n", rc);
+ goto bail;
+ }
+
+ rc = mfd_add_devices(pmic->dev, 0, &nfc_cell, 1, NULL, 0);
+ if (rc) {
+ pr_err("Failed to add upl subdevice ret=%d\n", rc);
+ goto bail;
+ }
+
+ if (pdata->charger_pdata) {
+ pm8058_charger_cell.platform_data = pdata->charger_pdata;
+ pm8058_charger_cell.pdata_size = sizeof(struct
+ pmic8058_charger_data);
+ rc = mfd_add_devices(pmic->dev, 0, &pm8058_charger_cell,
+ 1, NULL, irq_base);
+ if (rc) {
+ pr_err("Failed to add charger subdevice ret=%d\n", rc);
+ goto bail;
+ }
+ }
+
+ rc = mfd_add_devices(pmic->dev, 0, &debugfs_cell, 1, NULL, irq_base);
+ if (rc) {
+ pr_err("Failed to add debugfs subdevice ret=%d\n", rc);
+ goto bail;
+ }
+
+ return rc;
+bail:
+ if (pmic->irq_chip) {
+ pm8xxx_irq_exit(pmic->irq_chip);
+ pmic->irq_chip = NULL;
+ }
+ return rc;
+}
+
+static int __devinit pm8058_probe(struct platform_device *pdev)
+{
+ int rc;
+ struct pm8058_platform_data *pdata = pdev->dev.platform_data;
+ struct pm8058_chip *pmic;
+
+ if (pdata == NULL) {
+ pr_err("%s: No platform_data or IRQ.\n", __func__);
+ return -ENODEV;
+ }
+
+ pmic = kzalloc(sizeof *pmic, GFP_KERNEL);
+ if (pmic == NULL) {
+ pr_err("%s: kzalloc() failed.\n", __func__);
+ return -ENOMEM;
+ }
+
+ pmic->dev = &pdev->dev;
+
+ pm8058_drvdata.pm_chip_data = pmic;
+ platform_set_drvdata(pdev, &pm8058_drvdata);
+
+ /* Read PMIC chip revision */
+ rc = pm8058_readb(pmic->dev, PM8058_REG_REV, &pmic->revision);
+ if (rc)
+ pr_err("%s: Failed on pm8058_readb for revision: rc=%d.\n",
+ __func__, rc);
+
+ pr_info("%s: PMIC revision: %X\n", __func__, pmic->revision);
+
+ (void) memcpy((void *)&pmic->pdata, (const void *)pdata,
+ sizeof(pmic->pdata));
+
+ rc = pm8058_add_subdevices(pdata, pmic);
+ if (rc) {
+ pr_err("Cannot add subdevices rc=%d\n", rc);
+ goto err;
+ }
+
+ rc = pm8xxx_hard_reset_config(PM8XXX_SHUTDOWN_ON_HARD_RESET);
+ if (rc < 0)
+ pr_err("%s: failed to config shutdown on hard reset: %d\n",
+ __func__, rc);
+
+ return 0;
+
+err:
+ mfd_remove_devices(pmic->dev);
+ platform_set_drvdata(pdev, NULL);
+ kfree(pmic);
+ return rc;
+}
+
+static int __devexit pm8058_remove(struct platform_device *pdev)
+{
+ struct pm8xxx_drvdata *drvdata;
+ struct pm8058_chip *pmic = NULL;
+
+ drvdata = platform_get_drvdata(pdev);
+ if (drvdata)
+ pmic = drvdata->pm_chip_data;
+ if (pmic) {
+ if (pmic->dev)
+ mfd_remove_devices(pmic->dev);
+ if (pmic->irq_chip)
+ pm8xxx_irq_exit(pmic->irq_chip);
+ kfree(pmic->mfd_regulators);
+ kfree(pmic);
+ }
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver pm8058_driver = {
+ .probe = pm8058_probe,
+ .remove = __devexit_p(pm8058_remove),
+ .driver = {
+ .name = "pm8058-core",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pm8058_init(void)
+{
+ return platform_driver_register(&pm8058_driver);
+}
+postcore_initcall(pm8058_init);
+
+static void __exit pm8058_exit(void)
+{
+ platform_driver_unregister(&pm8058_driver);
+}
+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..955258c
--- /dev/null
+++ b/drivers/mfd/pmic8901.c
@@ -0,0 +1,382 @@
+/* Copyright (c) 2010-2012, 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/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mfd/core.h>
+#include <linux/msm_ssbi.h>
+#include <linux/mfd/pmic8901.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/module.h>
+
+/* PMIC8901 Revision */
+#define PM8901_REG_REV 0x002
+#define PM8901_VERSION_MASK 0xF0
+#define PM8901_REVISION_MASK 0x0F
+#define PM8901_VERSION_VALUE 0xF0
+
+#define REG_IRQ_BASE 0xD5
+#define REG_MPP_BASE 0x27
+
+#define REG_TEMP_ALRM_CTRL 0x23
+#define REG_TEMP_ALRM_PWM 0x24
+
+#define SINGLE_IRQ_RESOURCE(_name, _irq) \
+{ \
+ .name = _name, \
+ .start = _irq, \
+ .end = _irq, \
+ .flags = IORESOURCE_IRQ, \
+}
+
+struct pm8901_chip {
+ struct pm8901_platform_data pdata;
+ struct device *dev;
+ struct pm_irq_chip *irq_chip;
+ struct mfd_cell *mfd_regulators;
+ u8 revision;
+};
+
+static int pm8901_readb(const struct device *dev, u16 addr, u8 *val)
+{
+ const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
+ const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
+
+ return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
+}
+
+static int pm8901_writeb(const struct device *dev, u16 addr, u8 val)
+{
+ const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
+ const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
+
+ return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
+}
+
+static int pm8901_read_buf(const struct device *dev, u16 addr, u8 *buf,
+ int cnt)
+{
+ const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
+ const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
+
+ return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8901_write_buf(const struct device *dev, u16 addr, u8 *buf,
+ int cnt)
+{
+ const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
+ const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
+
+ return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8901_read_irq_stat(const struct device *dev, int irq)
+{
+ const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
+ const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
+
+ return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
+
+ return 0;
+}
+
+static enum pm8xxx_version pm8901_get_version(const struct device *dev)
+{
+ const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
+ const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
+ enum pm8xxx_version version = -ENODEV;
+
+ if ((pmic->revision & PM8901_VERSION_MASK) == PM8901_VERSION_VALUE)
+ version = PM8XXX_VERSION_8901;
+
+ return version;
+}
+
+static int pm8901_get_revision(const struct device *dev)
+{
+ const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
+ const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
+
+ return pmic->revision & PM8901_REVISION_MASK;
+}
+
+static struct pm8xxx_drvdata pm8901_drvdata = {
+ .pmic_readb = pm8901_readb,
+ .pmic_writeb = pm8901_writeb,
+ .pmic_read_buf = pm8901_read_buf,
+ .pmic_write_buf = pm8901_write_buf,
+ .pmic_read_irq_stat = pm8901_read_irq_stat,
+ .pmic_get_version = pm8901_get_version,
+ .pmic_get_revision = pm8901_get_revision,
+};
+
+static struct mfd_cell misc_cell = {
+ .name = PM8XXX_MISC_DEV_NAME,
+ .id = 1,
+};
+
+static struct mfd_cell debugfs_cell = {
+ .name = "pm8xxx-debug",
+ .id = 1,
+ .platform_data = "pm8901-dbg",
+ .pdata_size = sizeof("pm8901-dbg"),
+};
+
+static const struct resource thermal_alarm_cell_resources[] = {
+ SINGLE_IRQ_RESOURCE("pm8901_tempstat_irq", PM8901_TEMPSTAT_IRQ),
+ SINGLE_IRQ_RESOURCE("pm8901_overtemp_irq", PM8901_OVERTEMP_IRQ),
+};
+
+static struct pm8xxx_tm_core_data thermal_alarm_cdata = {
+ .adc_type = PM8XXX_TM_ADC_NONE,
+ .reg_addr_temp_alarm_ctrl = REG_TEMP_ALRM_CTRL,
+ .reg_addr_temp_alarm_pwm = REG_TEMP_ALRM_PWM,
+ .tm_name = "pm8901_tz",
+ .irq_name_temp_stat = "pm8901_tempstat_irq",
+ .irq_name_over_temp = "pm8901_overtemp_irq",
+};
+
+static struct mfd_cell thermal_alarm_cell = {
+ .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 mpp_cell_resources[] = {
+ {
+ .start = PM8901_IRQ_BLOCK_BIT(PM8901_MPP_BLOCK_START, 0),
+ .end = PM8901_IRQ_BLOCK_BIT(PM8901_MPP_BLOCK_START, 0)
+ + PM8901_MPPS - 1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell mpp_cell = {
+ .name = PM8XXX_MPP_DEV_NAME,
+ .id = 1,
+ .resources = mpp_cell_resources,
+ .num_resources = ARRAY_SIZE(mpp_cell_resources),
+};
+
+static int __devinit
+pm8901_add_subdevices(const struct pm8901_platform_data *pdata,
+ struct pm8901_chip *pmic)
+{
+ int rc = 0, irq_base = 0, i;
+ struct pm_irq_chip *irq_chip;
+ static struct mfd_cell *mfd_regulators;
+
+ if (pdata->irq_pdata) {
+ pdata->irq_pdata->irq_cdata.nirqs = PM8901_NR_IRQS;
+ pdata->irq_pdata->irq_cdata.base_addr = REG_IRQ_BASE;
+ irq_base = pdata->irq_pdata->irq_base;
+ irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
+
+ if (IS_ERR(irq_chip)) {
+ pr_err("Failed to init interrupts ret=%ld\n",
+ PTR_ERR(irq_chip));
+ return PTR_ERR(irq_chip);
+ }
+ pmic->irq_chip = irq_chip;
+ }
+
+ if (pdata->mpp_pdata) {
+ pdata->mpp_pdata->core_data.nmpps = PM8901_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);
+ rc = mfd_add_devices(pmic->dev, 0, &mpp_cell, 1, NULL,
+ irq_base);
+ if (rc) {
+ pr_err("Failed to add mpp subdevice ret=%d\n", rc);
+ goto bail;
+ }
+ }
+
+ 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 pm8901 regulator "
+ "mfd cells\n", sizeof(struct mfd_cell)
+ * (pdata->num_regulators));
+ rc = -ENOMEM;
+ goto bail;
+ }
+ for (i = 0; i < pdata->num_regulators; i++) {
+ mfd_regulators[i].name = "pm8901-regulator";
+ 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 pm8901_vreg_pdata);
+ }
+ rc = mfd_add_devices(pmic->dev, 0, mfd_regulators,
+ pdata->num_regulators, NULL, irq_base);
+ if (rc) {
+ pr_err("Failed to add regulator subdevices ret=%d\n",
+ rc);
+ kfree(mfd_regulators);
+ goto bail;
+ }
+ pmic->mfd_regulators = mfd_regulators;
+ }
+
+ rc = mfd_add_devices(pmic->dev, 0, &thermal_alarm_cell, 1, NULL,
+ irq_base);
+ if (rc) {
+ pr_err("Failed to add thermal alarm subdevice ret=%d\n",
+ rc);
+ goto bail;
+ }
+
+ rc = mfd_add_devices(pmic->dev, 0, &debugfs_cell, 1, NULL, irq_base);
+ if (rc) {
+ pr_err("Failed to add debugfs subdevice ret=%d\n", rc);
+ goto bail;
+ }
+
+ if (pdata->misc_pdata) {
+ misc_cell.platform_data = pdata->misc_pdata;
+ misc_cell.pdata_size = sizeof(struct pm8xxx_misc_platform_data);
+ rc = mfd_add_devices(pmic->dev, 0, &misc_cell, 1, NULL,
+ irq_base);
+ if (rc) {
+ pr_err("Failed to add misc subdevice ret=%d\n", rc);
+ goto bail;
+ }
+ }
+
+ return rc;
+
+bail:
+ if (pmic->irq_chip) {
+ pm8xxx_irq_exit(pmic->irq_chip);
+ pmic->irq_chip = NULL;
+ }
+ return rc;
+}
+
+static const char * const pm8901_rev_names[] = {
+ [PM8XXX_REVISION_8901_TEST] = "test",
+ [PM8XXX_REVISION_8901_1p0] = "1.0",
+ [PM8XXX_REVISION_8901_1p1] = "1.1",
+ [PM8XXX_REVISION_8901_2p0] = "2.0",
+ [PM8XXX_REVISION_8901_2p1] = "2.1",
+ [PM8XXX_REVISION_8901_2p2] = "2.2",
+ [PM8XXX_REVISION_8901_2p3] = "2.3",
+};
+
+static int __devinit pm8901_probe(struct platform_device *pdev)
+{
+ int rc;
+ struct pm8901_platform_data *pdata = pdev->dev.platform_data;
+ const char *revision_name = "unknown";
+ struct pm8901_chip *pmic;
+ int revision;
+
+ if (pdata == NULL) {
+ pr_err("%s: No platform_data or IRQ.\n", __func__);
+ return -ENODEV;
+ }
+
+ pmic = kzalloc(sizeof *pmic, GFP_KERNEL);
+ if (pmic == NULL) {
+ pr_err("%s: kzalloc() failed.\n", __func__);
+ return -ENOMEM;
+ }
+
+ pmic->dev = &pdev->dev;
+
+ pm8901_drvdata.pm_chip_data = pmic;
+ platform_set_drvdata(pdev, &pm8901_drvdata);
+
+ /* Read PMIC chip revision */
+ rc = pm8901_readb(pmic->dev, PM8901_REG_REV, &pmic->revision);
+ if (rc)
+ pr_err("%s: Failed reading version register rc=%d.\n",
+ __func__, rc);
+
+ pr_info("%s: PMIC revision reg: %02X\n", __func__, pmic->revision);
+ revision = pm8xxx_get_revision(pmic->dev);
+ if (revision >= 0 && revision < ARRAY_SIZE(pm8901_rev_names))
+ revision_name = pm8901_rev_names[revision];
+ pr_info("%s: PMIC version: PM8901 rev %s\n", __func__, revision_name);
+
+ (void) memcpy((void *)&pmic->pdata, (const void *)pdata,
+ sizeof(pmic->pdata));
+
+ rc = pm8901_add_subdevices(pdata, pmic);
+ if (rc) {
+ pr_err("Cannot add subdevices rc=%d\n", rc);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ platform_set_drvdata(pdev, NULL);
+ kfree(pmic);
+ return rc;
+}
+
+static int __devexit pm8901_remove(struct platform_device *pdev)
+{
+ struct pm8xxx_drvdata *drvdata;
+ struct pm8901_chip *pmic = NULL;
+
+ drvdata = platform_get_drvdata(pdev);
+ if (drvdata)
+ pmic = drvdata->pm_chip_data;
+ if (pmic) {
+ if (pmic->dev)
+ mfd_remove_devices(pmic->dev);
+ if (pmic->irq_chip)
+ pm8xxx_irq_exit(pmic->irq_chip);
+ kfree(pmic->mfd_regulators);
+ kfree(pmic);
+ }
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver pm8901_driver = {
+ .probe = pm8901_probe,
+ .remove = __devexit_p(pm8901_remove),
+ .driver = {
+ .name = "pm8901-core",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pm8901_init(void)
+{
+ return platform_driver_register(&pm8901_driver);
+}
+postcore_initcall(pm8901_init);
+
+static void __exit pm8901_exit(void)
+{
+ platform_driver_unregister(&pm8901_driver);
+}
+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..1e0a839
--- /dev/null
+++ b/drivers/mfd/timpani-codec.c
@@ -0,0 +1,3661 @@
+/* Copyright (c) 2010-2012, 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)
+#define MAX_SHADOW_RIGISTERS TIMPANI_A_CDC_COMP_HALT
+
+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:
+ case TIMPANI_A_AUXPGA_LR_GAIN:
+ case TIMPANI_A_CDC_ANC_INPUT_MUX:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static int adie_codec_write(u8 reg, u8 mask, u8 val)
+{
+ int rc = 0;
+ u8 new_val;
+
+ if (reg > MAX_SHADOW_RIGISTERS) {
+ pr_debug("register number is out of bound for shadow"
+ " registers reg = %d\n", reg);
+ new_val = (val & mask);
+ 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;
+ }
+ return rc;
+ }
+ 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 < ARRAY_SIZE(timpani_regset); 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..318692f
--- /dev/null
+++ b/drivers/mfd/tps65023.c
@@ -0,0 +1,123 @@
+/* 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>
+#include <linux/module.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/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c
new file mode 100644
index 0000000..e2dff4b
--- /dev/null
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -0,0 +1,1168 @@
+/* Copyright (c) 2011-2012, 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/wcd9xxx/wcd9xxx-slimslave.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/debugfs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/i2c.h>
+#include <sound/soc.h>
+
+#define WCD9XXX_SLIM_GLA_MAX_RETRIES 5
+#define WCD9XXX_REGISTER_START_OFFSET 0x800
+#define WCD9XXX_SLIM_RW_MAX_TRIES 3
+
+#define MAX_WCD9XXX_DEVICE 4
+#define WCD9XXX_I2C_MODE 0x03
+
+struct wcd9xxx_i2c {
+ struct i2c_client *client;
+ struct i2c_msg xfer_msg[2];
+ struct mutex xfer_lock;
+ int mod_id;
+};
+
+struct wcd9xxx_i2c wcd9xxx_modules[MAX_WCD9XXX_DEVICE];
+static int wcd9xxx_intf = -1;
+
+static int wcd9xxx_read(struct wcd9xxx *wcd9xxx, unsigned short reg,
+ int bytes, void *dest, bool interface_reg)
+{
+ int ret;
+ u8 *buf = dest;
+
+ if (bytes <= 0) {
+ dev_err(wcd9xxx->dev, "Invalid byte read length %d\n", bytes);
+ return -EINVAL;
+ }
+
+ ret = wcd9xxx->read_dev(wcd9xxx, reg, bytes, dest, interface_reg);
+ if (ret < 0) {
+ dev_err(wcd9xxx->dev, "Codec read failed\n");
+ return ret;
+ } else
+ dev_dbg(wcd9xxx->dev, "Read 0x%02x from 0x%x\n",
+ *buf, reg);
+
+ return 0;
+}
+int wcd9xxx_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg)
+{
+ u8 val;
+ int ret;
+
+ mutex_lock(&wcd9xxx->io_lock);
+ ret = wcd9xxx_read(wcd9xxx, reg, 1, &val, false);
+ mutex_unlock(&wcd9xxx->io_lock);
+
+ if (ret < 0)
+ return ret;
+ else
+ return val;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_reg_read);
+
+static int wcd9xxx_write(struct wcd9xxx *wcd9xxx, 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(wcd9xxx->dev, "Write %02x to 0x%x\n",
+ *buf, reg);
+
+ return wcd9xxx->write_dev(wcd9xxx, reg, bytes, src, interface_reg);
+}
+
+int wcd9xxx_reg_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
+ u8 val)
+{
+ int ret;
+
+ mutex_lock(&wcd9xxx->io_lock);
+ ret = wcd9xxx_write(wcd9xxx, reg, 1, &val, false);
+ mutex_unlock(&wcd9xxx->io_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_reg_write);
+
+static u8 wcd9xxx_pgd_la;
+static u8 wcd9xxx_inf_la;
+
+int wcd9xxx_interface_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg)
+{
+ u8 val;
+ int ret;
+
+ mutex_lock(&wcd9xxx->io_lock);
+ ret = wcd9xxx_read(wcd9xxx, reg, 1, &val, true);
+ mutex_unlock(&wcd9xxx->io_lock);
+
+ if (ret < 0)
+ return ret;
+ else
+ return val;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_interface_reg_read);
+
+int wcd9xxx_interface_reg_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
+ u8 val)
+{
+ int ret;
+
+ mutex_lock(&wcd9xxx->io_lock);
+ ret = wcd9xxx_write(wcd9xxx, reg, 1, &val, true);
+ mutex_unlock(&wcd9xxx->io_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_interface_reg_write);
+
+int wcd9xxx_bulk_read(struct wcd9xxx *wcd9xxx, unsigned short reg,
+ int count, u8 *buf)
+{
+ int ret;
+
+ mutex_lock(&wcd9xxx->io_lock);
+
+ ret = wcd9xxx_read(wcd9xxx, reg, count, buf, false);
+
+ mutex_unlock(&wcd9xxx->io_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_bulk_read);
+
+int wcd9xxx_bulk_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
+ int count, u8 *buf)
+{
+ int ret;
+
+ mutex_lock(&wcd9xxx->io_lock);
+
+ ret = wcd9xxx_write(wcd9xxx, reg, count, buf, false);
+
+ mutex_unlock(&wcd9xxx->io_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_bulk_write);
+
+static int wcd9xxx_slim_read_device(struct wcd9xxx *wcd9xxx, unsigned short reg,
+ int bytes, void *dest, bool interface)
+{
+ int ret;
+ struct slim_ele_access msg;
+ int slim_read_tries = WCD9XXX_SLIM_RW_MAX_TRIES;
+ msg.start_offset = WCD9XXX_REGISTER_START_OFFSET + reg;
+ msg.num_bytes = bytes;
+ msg.comp = NULL;
+
+ while (1) {
+ mutex_lock(&wcd9xxx->xfer_lock);
+ ret = slim_request_val_element(interface ?
+ wcd9xxx->slim_slave : wcd9xxx->slim,
+ &msg, dest, bytes);
+ mutex_unlock(&wcd9xxx->xfer_lock);
+ if (likely(ret == 0) || (--slim_read_tries == 0))
+ break;
+ usleep_range(5000, 5000);
+ }
+
+ if (ret)
+ pr_err("%s: Error, Codec read failed (%d)\n", __func__, ret);
+
+ return ret;
+}
+/* Interface specifies whether the write is to the interface or general
+ * registers.
+ */
+static int wcd9xxx_slim_write_device(struct wcd9xxx *wcd9xxx,
+ unsigned short reg, int bytes, void *src, bool interface)
+{
+ int ret;
+ struct slim_ele_access msg;
+ int slim_write_tries = WCD9XXX_SLIM_RW_MAX_TRIES;
+ msg.start_offset = WCD9XXX_REGISTER_START_OFFSET + reg;
+ msg.num_bytes = bytes;
+ msg.comp = NULL;
+
+ while (1) {
+ mutex_lock(&wcd9xxx->xfer_lock);
+ ret = slim_change_val_element(interface ?
+ wcd9xxx->slim_slave : wcd9xxx->slim,
+ &msg, src, bytes);
+ mutex_unlock(&wcd9xxx->xfer_lock);
+ if (likely(ret == 0) || (--slim_write_tries == 0))
+ break;
+ usleep_range(5000, 5000);
+ }
+
+ if (ret)
+ pr_err("%s: Error, Codec write failed (%d)\n", __func__, ret);
+
+ return ret;
+}
+
+static struct mfd_cell tabla1x_devs[] = {
+ {
+ .name = "tabla1x_codec",
+ },
+};
+
+static struct mfd_cell tabla_devs[] = {
+ {
+ .name = "tabla_codec",
+ },
+};
+
+static struct mfd_cell sitar_devs[] = {
+ {
+ .name = "sitar_codec",
+ },
+};
+
+static void wcd9xxx_bring_up(struct wcd9xxx *wcd9xxx)
+{
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0x4);
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_CDC_CTL, 0);
+ usleep_range(5000, 5000);
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_CDC_CTL, 3);
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 3);
+}
+
+static void wcd9xxx_bring_down(struct wcd9xxx *wcd9xxx)
+{
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0x7);
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0x6);
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0xe);
+ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0x8);
+}
+
+static int wcd9xxx_reset(struct wcd9xxx *wcd9xxx)
+{
+ int ret;
+
+ if (wcd9xxx->reset_gpio) {
+ ret = gpio_request(wcd9xxx->reset_gpio, "CDC_RESET");
+ if (ret) {
+ pr_err("%s: Failed to request gpio %d\n", __func__,
+ wcd9xxx->reset_gpio);
+ wcd9xxx->reset_gpio = 0;
+ return ret;
+ }
+
+ gpio_direction_output(wcd9xxx->reset_gpio, 1);
+ msleep(20);
+ gpio_direction_output(wcd9xxx->reset_gpio, 0);
+ msleep(20);
+ gpio_direction_output(wcd9xxx->reset_gpio, 1);
+ msleep(20);
+ }
+ return 0;
+}
+
+static void wcd9xxx_free_reset(struct wcd9xxx *wcd9xxx)
+{
+ if (wcd9xxx->reset_gpio) {
+ gpio_free(wcd9xxx->reset_gpio);
+ wcd9xxx->reset_gpio = 0;
+ }
+}
+
+static int wcd9xxx_device_init(struct wcd9xxx *wcd9xxx, int irq)
+{
+ int ret;
+ u8 idbyte_0, idbyte_1, idbyte_2, idbyte_3;
+ struct mfd_cell *wcd9xxx_dev = NULL;
+ int wcd9xxx_dev_size = 0;
+
+ mutex_init(&wcd9xxx->io_lock);
+ mutex_init(&wcd9xxx->xfer_lock);
+
+ mutex_init(&wcd9xxx->pm_lock);
+ wcd9xxx->wlock_holders = 0;
+ wcd9xxx->pm_state = WCD9XXX_PM_SLEEPABLE;
+ init_waitqueue_head(&wcd9xxx->pm_wq);
+ wake_lock_init(&wcd9xxx->wlock, WAKE_LOCK_IDLE, "wcd9310-irq");
+
+ dev_set_drvdata(wcd9xxx->dev, wcd9xxx);
+
+ wcd9xxx_bring_up(wcd9xxx);
+
+ ret = wcd9xxx_irq_init(wcd9xxx);
+ if (ret) {
+ pr_err("IRQ initialization failed\n");
+ goto err;
+ }
+
+ idbyte_0 = wcd9xxx_reg_read(wcd9xxx, WCD9XXX_A_CHIP_ID_BYTE_0);
+ idbyte_1 = wcd9xxx_reg_read(wcd9xxx, WCD9XXX_A_CHIP_ID_BYTE_1);
+ idbyte_2 = wcd9xxx_reg_read(wcd9xxx, WCD9XXX_A_CHIP_ID_BYTE_2);
+ idbyte_3 = wcd9xxx_reg_read(wcd9xxx, WCD9XXX_A_CHIP_ID_BYTE_3);
+
+ wcd9xxx->version = wcd9xxx_reg_read(wcd9xxx,
+ WCD9XXX_A_CHIP_VERSION) & 0x1F;
+ pr_info("%s : Codec version %u initialized\n",
+ __func__, wcd9xxx->version);
+ pr_info("idbyte_0[%08x] idbyte_1[%08x] idbyte_2[%08x] idbyte_3[%08x]\n",
+ idbyte_0, idbyte_1, idbyte_2, idbyte_3);
+
+ if (wcd9xxx->slim != NULL) {
+ if (!strncmp(wcd9xxx->slim->name, "tabla", 5)) {
+ if (TABLA_IS_1_X(wcd9xxx->version)) {
+ wcd9xxx_dev = tabla1x_devs;
+ wcd9xxx_dev_size = ARRAY_SIZE(tabla1x_devs);
+ } else {
+ wcd9xxx_dev = tabla_devs;
+ wcd9xxx_dev_size = ARRAY_SIZE(tabla_devs);
+ }
+ } else {
+ wcd9xxx_dev = sitar_devs;
+ wcd9xxx_dev_size = ARRAY_SIZE(sitar_devs);
+ }
+ } else {
+ /* Need to add here check for Tabla.
+ * For now the read of version takes
+ * care of now only tabla.
+ */
+ pr_debug("%s : Read codec version using I2C\n", __func__);
+ if (TABLA_IS_1_X(wcd9xxx->version)) {
+ wcd9xxx_dev = tabla1x_devs;
+ wcd9xxx_dev_size = ARRAY_SIZE(tabla1x_devs);
+ } else if (TABLA_IS_2_0(wcd9xxx->version)) {
+ wcd9xxx_dev = tabla_devs;
+ wcd9xxx_dev_size = ARRAY_SIZE(tabla_devs);
+ } else {
+ wcd9xxx_dev = sitar_devs;
+ wcd9xxx_dev_size = ARRAY_SIZE(sitar_devs);
+ }
+ }
+
+ ret = mfd_add_devices(wcd9xxx->dev, -1,
+ wcd9xxx_dev, wcd9xxx_dev_size,
+ NULL, 0);
+ if (ret != 0) {
+ dev_err(wcd9xxx->dev, "Failed to add children: %d\n", ret);
+ goto err_irq;
+ }
+ return ret;
+err_irq:
+ wcd9xxx_irq_exit(wcd9xxx);
+err:
+ wcd9xxx_bring_down(wcd9xxx);
+ wake_lock_destroy(&wcd9xxx->wlock);
+ mutex_destroy(&wcd9xxx->pm_lock);
+ mutex_destroy(&wcd9xxx->io_lock);
+ mutex_destroy(&wcd9xxx->xfer_lock);
+ return ret;
+}
+
+static void wcd9xxx_device_exit(struct wcd9xxx *wcd9xxx)
+{
+ wcd9xxx_irq_exit(wcd9xxx);
+ wcd9xxx_bring_down(wcd9xxx);
+ wcd9xxx_free_reset(wcd9xxx);
+ mutex_destroy(&wcd9xxx->pm_lock);
+ wake_lock_destroy(&wcd9xxx->wlock);
+ mutex_destroy(&wcd9xxx->io_lock);
+ mutex_destroy(&wcd9xxx->xfer_lock);
+ if (wcd9xxx_intf == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+ slim_remove_device(wcd9xxx->slim_slave);
+ kfree(wcd9xxx);
+}
+
+
+#ifdef CONFIG_DEBUG_FS
+struct wcd9xxx *debugCodec;
+
+static struct dentry *debugfs_wcd9xxx_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))
+ wcd9xxx_interface_reg_write(debugCodec, 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 = wcd9xxx_interface_reg_read(debugCodec,
+ 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 wcd9xxx_enable_supplies(struct wcd9xxx *wcd9xxx,
+ struct wcd9xxx_pdata *pdata)
+{
+ int ret;
+ int i;
+ wcd9xxx->supplies = kzalloc(sizeof(struct regulator_bulk_data) *
+ ARRAY_SIZE(pdata->regulator),
+ GFP_KERNEL);
+ if (!wcd9xxx->supplies) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++)
+ wcd9xxx->supplies[i].supply = pdata->regulator[i].name;
+
+ ret = regulator_bulk_get(wcd9xxx->dev, ARRAY_SIZE(pdata->regulator),
+ wcd9xxx->supplies);
+ if (ret != 0) {
+ dev_err(wcd9xxx->dev, "Failed to get supplies: err = %d\n",
+ ret);
+ goto err_supplies;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) {
+ ret = regulator_set_voltage(wcd9xxx->supplies[i].consumer,
+ pdata->regulator[i].min_uV, pdata->regulator[i].max_uV);
+ if (ret) {
+ pr_err("%s: Setting regulator voltage failed for "
+ "regulator %s err = %d\n", __func__,
+ wcd9xxx->supplies[i].supply, ret);
+ goto err_get;
+ }
+
+ ret = regulator_set_optimum_mode(wcd9xxx->supplies[i].consumer,
+ pdata->regulator[i].optimum_uA);
+ if (ret < 0) {
+ pr_err("%s: Setting regulator optimum mode failed for "
+ "regulator %s err = %d\n", __func__,
+ wcd9xxx->supplies[i].supply, ret);
+ goto err_get;
+ }
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(pdata->regulator),
+ wcd9xxx->supplies);
+ if (ret != 0) {
+ dev_err(wcd9xxx->dev, "Failed to enable supplies: err = %d\n",
+ ret);
+ goto err_configure;
+ }
+ return ret;
+
+err_configure:
+ for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) {
+ regulator_set_voltage(wcd9xxx->supplies[i].consumer, 0,
+ pdata->regulator[i].max_uV);
+ regulator_set_optimum_mode(wcd9xxx->supplies[i].consumer, 0);
+ }
+err_get:
+ regulator_bulk_free(ARRAY_SIZE(pdata->regulator), wcd9xxx->supplies);
+err_supplies:
+ kfree(wcd9xxx->supplies);
+err:
+ return ret;
+}
+
+static void wcd9xxx_disable_supplies(struct wcd9xxx *wcd9xxx,
+ struct wcd9xxx_pdata *pdata)
+{
+ int i;
+
+ regulator_bulk_disable(ARRAY_SIZE(pdata->regulator),
+ wcd9xxx->supplies);
+ for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) {
+ regulator_set_voltage(wcd9xxx->supplies[i].consumer, 0,
+ pdata->regulator[i].max_uV);
+ regulator_set_optimum_mode(wcd9xxx->supplies[i].consumer, 0);
+ }
+ regulator_bulk_free(ARRAY_SIZE(pdata->regulator), wcd9xxx->supplies);
+ kfree(wcd9xxx->supplies);
+}
+
+int wcd9xxx_get_intf_type(void)
+{
+ return wcd9xxx_intf;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_get_intf_type);
+
+struct wcd9xxx_i2c *get_i2c_wcd9xxx_device_info(u16 reg)
+{
+ u16 mask = 0x0f00;
+ int value = 0;
+ struct wcd9xxx_i2c *wcd9xxx = NULL;
+ value = ((reg & mask) >> 8) & 0x000f;
+ switch (value) {
+ case 0:
+ wcd9xxx = &wcd9xxx_modules[0];
+ break;
+ case 1:
+ wcd9xxx = &wcd9xxx_modules[1];
+ break;
+ case 2:
+ wcd9xxx = &wcd9xxx_modules[2];
+ break;
+ case 3:
+ wcd9xxx = &wcd9xxx_modules[3];
+ break;
+ default:
+ break;
+ }
+ return wcd9xxx;
+}
+
+int wcd9xxx_i2c_write_device(u16 reg, u8 *value,
+ u32 bytes)
+{
+
+ struct i2c_msg *msg;
+ int ret = 0;
+ u8 reg_addr = 0;
+ u8 data[bytes + 1];
+ struct wcd9xxx_i2c *wcd9xxx;
+
+ wcd9xxx = get_i2c_wcd9xxx_device_info(reg);
+ if (wcd9xxx == NULL || wcd9xxx->client == NULL) {
+ pr_err("failed to get device info\n");
+ return -ENODEV;
+ }
+ reg_addr = (u8)reg;
+ msg = &wcd9xxx->xfer_msg[0];
+ msg->addr = wcd9xxx->client->addr;
+ msg->len = bytes + 1;
+ msg->flags = 0;
+ data[0] = reg;
+ data[1] = *value;
+ msg->buf = data;
+ ret = i2c_transfer(wcd9xxx->client->adapter, wcd9xxx->xfer_msg, 1);
+ /* Try again if the write fails */
+ if (ret != 1) {
+ ret = i2c_transfer(wcd9xxx->client->adapter,
+ wcd9xxx->xfer_msg, 1);
+ if (ret != 1) {
+ pr_err("failed to write the device\n");
+ return ret;
+ }
+ }
+ pr_debug("write sucess register = %x val = %x\n", reg, data[1]);
+ return 0;
+}
+
+
+int wcd9xxx_i2c_read_device(unsigned short reg,
+ int bytes, unsigned char *dest)
+{
+ struct i2c_msg *msg;
+ int ret = 0;
+ u8 reg_addr = 0;
+ struct wcd9xxx_i2c *wcd9xxx;
+ u8 i = 0;
+
+ wcd9xxx = get_i2c_wcd9xxx_device_info(reg);
+ if (wcd9xxx == NULL || wcd9xxx->client == NULL) {
+ pr_err("failed to get device info\n");
+ return -ENODEV;
+ }
+ for (i = 0; i < bytes; i++) {
+ reg_addr = (u8)reg++;
+ msg = &wcd9xxx->xfer_msg[0];
+ msg->addr = wcd9xxx->client->addr;
+ msg->len = 1;
+ msg->flags = 0;
+ msg->buf = ®_addr;
+
+ msg = &wcd9xxx->xfer_msg[1];
+ msg->addr = wcd9xxx->client->addr;
+ msg->len = 1;
+ msg->flags = I2C_M_RD;
+ msg->buf = dest++;
+ ret = i2c_transfer(wcd9xxx->client->adapter,
+ wcd9xxx->xfer_msg, 2);
+
+ /* Try again if read fails first time */
+ if (ret != 2) {
+ ret = i2c_transfer(wcd9xxx->client->adapter,
+ wcd9xxx->xfer_msg, 2);
+ if (ret != 2) {
+ pr_err("failed to read wcd9xxx register\n");
+ return ret;
+ }
+ }
+ }
+ return 0;
+}
+
+int wcd9xxx_i2c_read(struct wcd9xxx *wcd9xxx, unsigned short reg,
+ int bytes, void *dest, bool interface_reg)
+{
+ return wcd9xxx_i2c_read_device(reg, bytes, dest);
+}
+
+int wcd9xxx_i2c_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
+ int bytes, void *src, bool interface_reg)
+{
+ return wcd9xxx_i2c_write_device(reg, src, bytes);
+}
+
+static int __devinit wcd9xxx_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct wcd9xxx *wcd9xxx;
+ struct wcd9xxx_pdata *pdata = client->dev.platform_data;
+ int val = 0;
+ int ret = 0;
+ static int device_id;
+
+ if (wcd9xxx_intf == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ pr_info("tabla card is already detected in slimbus mode\n");
+ return -ENODEV;
+ }
+ if (device_id > 0) {
+ wcd9xxx_modules[device_id++].client = client;
+ pr_info("probe for other slaves devices of tabla\n");
+ return ret;
+ }
+
+ wcd9xxx = kzalloc(sizeof(struct wcd9xxx), GFP_KERNEL);
+ if (wcd9xxx == NULL) {
+ pr_err("%s: error, allocation failed\n", __func__);
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ if (!pdata) {
+ dev_dbg(&client->dev, "no platform data?\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
+ dev_dbg(&client->dev, "can't talk I2C?\n");
+ ret = -EIO;
+ goto fail;
+ }
+ dev_set_drvdata(&client->dev, wcd9xxx);
+ wcd9xxx->dev = &client->dev;
+ wcd9xxx->reset_gpio = pdata->reset_gpio;
+ ret = wcd9xxx_enable_supplies(wcd9xxx, pdata);
+ if (ret) {
+ pr_err("%s: Fail to enable Codec supplies\n", __func__);
+ goto err_codec;
+ }
+
+ usleep_range(5, 5);
+ ret = wcd9xxx_reset(wcd9xxx);
+ if (ret) {
+ pr_err("%s: Resetting Codec failed\n", __func__);
+ goto err_supplies;
+ }
+ wcd9xxx_modules[device_id++].client = client;
+
+ wcd9xxx->read_dev = wcd9xxx_i2c_read;
+ wcd9xxx->write_dev = wcd9xxx_i2c_write;
+ wcd9xxx->irq = pdata->irq;
+ wcd9xxx->irq_base = pdata->irq_base;
+
+ /*read the tabla status before initializing the device type*/
+ ret = wcd9xxx_read(wcd9xxx, WCD9XXX_A_CHIP_STATUS, 1, &val, 0);
+ if ((ret < 0) || (val != WCD9XXX_I2C_MODE)) {
+ pr_err("failed to read the wcd9xxx status\n");
+ goto err_device_init;
+ }
+
+ ret = wcd9xxx_device_init(wcd9xxx, wcd9xxx->irq);
+ if (ret) {
+ pr_err("%s: error, initializing device failed\n", __func__);
+ goto err_device_init;
+ }
+ wcd9xxx_intf = WCD9XXX_INTERFACE_TYPE_I2C;
+
+ return ret;
+err_device_init:
+ wcd9xxx_free_reset(wcd9xxx);
+err_supplies:
+ wcd9xxx_disable_supplies(wcd9xxx, pdata);
+err_codec:
+ kfree(wcd9xxx);
+fail:
+ return ret;
+}
+
+static int __devexit wcd9xxx_i2c_remove(struct i2c_client *client)
+{
+ struct wcd9xxx *wcd9xxx;
+ struct wcd9xxx_pdata *pdata = client->dev.platform_data;
+ pr_debug("exit\n");
+ wcd9xxx = dev_get_drvdata(&client->dev);
+ wcd9xxx_disable_supplies(wcd9xxx, pdata);
+ wcd9xxx_device_exit(wcd9xxx);
+ return 0;
+}
+
+static int wcd9xxx_slim_probe(struct slim_device *slim)
+{
+ struct wcd9xxx *wcd9xxx;
+ struct wcd9xxx_pdata *pdata;
+ int ret = 0;
+ int sgla_retry_cnt;
+
+ dev_info(&slim->dev, "Initialized slim device %s\n", slim->name);
+ pdata = slim->dev.platform_data;
+
+ if (!pdata) {
+ dev_err(&slim->dev, "Error, no platform data\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ wcd9xxx = kzalloc(sizeof(struct wcd9xxx), GFP_KERNEL);
+ if (wcd9xxx == 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_codec;
+ }
+ wcd9xxx->slim = slim;
+ slim_set_clientdata(slim, wcd9xxx);
+ wcd9xxx->reset_gpio = pdata->reset_gpio;
+ wcd9xxx->dev = &slim->dev;
+
+ ret = wcd9xxx_enable_supplies(wcd9xxx, pdata);
+ if (ret)
+ goto err_codec;
+ usleep_range(5, 5);
+
+ ret = wcd9xxx_reset(wcd9xxx);
+ if (ret) {
+ pr_err("%s: Resetting Codec failed\n", __func__);
+ goto err_supplies;
+ }
+
+ ret = slim_get_logical_addr(wcd9xxx->slim, wcd9xxx->slim->e_addr,
+ ARRAY_SIZE(wcd9xxx->slim->e_addr), &wcd9xxx->slim->laddr);
+ if (ret) {
+ pr_err("fail to get slimbus logical address %d\n", ret);
+ goto err_reset;
+ }
+ wcd9xxx->read_dev = wcd9xxx_slim_read_device;
+ wcd9xxx->write_dev = wcd9xxx_slim_write_device;
+ wcd9xxx->irq = pdata->irq;
+ wcd9xxx->irq_base = pdata->irq_base;
+ wcd9xxx_pgd_la = wcd9xxx->slim->laddr;
+
+ if (pdata->num_irqs < TABLA_NUM_IRQS) {
+ pr_err("%s: Error, not enough interrupt lines allocated\n",
+ __func__);
+ goto err_reset;
+ }
+
+ wcd9xxx->slim_slave = &pdata->slimbus_slave_device;
+
+ ret = slim_add_device(slim->ctrl, wcd9xxx->slim_slave);
+ if (ret) {
+ pr_err("%s: error, adding SLIMBUS device failed\n", __func__);
+ goto err_reset;
+ }
+
+ sgla_retry_cnt = 0;
+
+ while (1) {
+ ret = slim_get_logical_addr(wcd9xxx->slim_slave,
+ wcd9xxx->slim_slave->e_addr,
+ ARRAY_SIZE(wcd9xxx->slim_slave->e_addr),
+ &wcd9xxx->slim_slave->laddr);
+ if (ret) {
+ if (sgla_retry_cnt++ < WCD9XXX_SLIM_GLA_MAX_RETRIES) {
+ /* Give SLIMBUS slave time to report present
+ and be ready.
+ */
+ usleep_range(1000, 1000);
+ pr_debug("%s: retry slim_get_logical_addr()\n",
+ __func__);
+ continue;
+ }
+ pr_err("fail to get slimbus slave logical address"
+ " %d\n", ret);
+ goto err_slim_add;
+ }
+ break;
+ }
+ wcd9xxx_inf_la = wcd9xxx->slim_slave->laddr;
+ wcd9xxx_intf = WCD9XXX_INTERFACE_TYPE_SLIMBUS;
+
+ ret = wcd9xxx_device_init(wcd9xxx, wcd9xxx->irq);
+ if (ret) {
+ pr_err("%s: error, initializing device failed\n", __func__);
+ goto err_slim_add;
+ }
+ wcd9xxx_init_slimslave(wcd9xxx, wcd9xxx_pgd_la);
+#ifdef CONFIG_DEBUG_FS
+ debugCodec = wcd9xxx;
+
+ debugfs_wcd9xxx_dent = debugfs_create_dir
+ ("wcd9310_slimbus_interface_device", 0);
+ if (!IS_ERR(debugfs_wcd9xxx_dent)) {
+ debugfs_peek = debugfs_create_file("peek",
+ S_IFREG | S_IRUGO, debugfs_wcd9xxx_dent,
+ (void *) "peek", &codec_debug_ops);
+
+ debugfs_poke = debugfs_create_file("poke",
+ S_IFREG | S_IRUGO, debugfs_wcd9xxx_dent,
+ (void *) "poke", &codec_debug_ops);
+ }
+#endif
+
+ return ret;
+
+err_slim_add:
+ slim_remove_device(wcd9xxx->slim_slave);
+err_reset:
+ wcd9xxx_free_reset(wcd9xxx);
+err_supplies:
+ wcd9xxx_disable_supplies(wcd9xxx, pdata);
+err_codec:
+ kfree(wcd9xxx);
+err:
+ return ret;
+}
+static int wcd9xxx_slim_remove(struct slim_device *pdev)
+{
+ struct wcd9xxx *wcd9xxx;
+ struct wcd9xxx_pdata *pdata = pdev->dev.platform_data;
+
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove(debugfs_peek);
+ debugfs_remove(debugfs_poke);
+ debugfs_remove(debugfs_wcd9xxx_dent);
+#endif
+ wcd9xxx = slim_get_devicedata(pdev);
+ wcd9xxx_deinit_slimslave(wcd9xxx);
+ slim_remove_device(wcd9xxx->slim_slave);
+ wcd9xxx_disable_supplies(wcd9xxx, pdata);
+ wcd9xxx_device_exit(wcd9xxx);
+ return 0;
+}
+
+static int wcd9xxx_resume(struct wcd9xxx *wcd9xxx)
+{
+ int ret = 0;
+
+ pr_debug("%s: enter\n", __func__);
+ mutex_lock(&wcd9xxx->pm_lock);
+ if (wcd9xxx->pm_state == WCD9XXX_PM_ASLEEP) {
+ pr_debug("%s: resuming system, state %d, wlock %d\n", __func__,
+ wcd9xxx->pm_state, wcd9xxx->wlock_holders);
+ wcd9xxx->pm_state = WCD9XXX_PM_SLEEPABLE;
+ } else {
+ pr_warn("%s: system is already awake, state %d wlock %d\n",
+ __func__, wcd9xxx->pm_state, wcd9xxx->wlock_holders);
+ }
+ mutex_unlock(&wcd9xxx->pm_lock);
+ wake_up_all(&wcd9xxx->pm_wq);
+
+ return ret;
+}
+
+static int wcd9xxx_slim_resume(struct slim_device *sldev)
+{
+ struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev);
+ return wcd9xxx_resume(wcd9xxx);
+}
+
+static int wcd9xxx_i2c_resume(struct i2c_client *i2cdev)
+{
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(&i2cdev->dev);
+ if (wcd9xxx)
+ return wcd9xxx_resume(wcd9xxx);
+ else
+ return 0;
+}
+
+static int wcd9xxx_suspend(struct wcd9xxx *wcd9xxx, pm_message_t pmesg)
+{
+ int ret = 0;
+
+ pr_debug("%s: enter\n", __func__);
+ /* wake_lock() can be called after this suspend chain call started.
+ * thus suspend can be called while wlock is being held */
+ mutex_lock(&wcd9xxx->pm_lock);
+ if (wcd9xxx->pm_state == WCD9XXX_PM_SLEEPABLE) {
+ pr_debug("%s: suspending system, state %d, wlock %d\n",
+ __func__, wcd9xxx->pm_state, wcd9xxx->wlock_holders);
+ wcd9xxx->pm_state = WCD9XXX_PM_ASLEEP;
+ } else if (wcd9xxx->pm_state == WCD9XXX_PM_AWAKE) {
+ /* unlock to wait for pm_state == WCD9XXX_PM_SLEEPABLE
+ * then set to WCD9XXX_PM_ASLEEP */
+ pr_debug("%s: waiting to suspend system, state %d, wlock %d\n",
+ __func__, wcd9xxx->pm_state, wcd9xxx->wlock_holders);
+ mutex_unlock(&wcd9xxx->pm_lock);
+ if (!(wait_event_timeout(wcd9xxx->pm_wq,
+ wcd9xxx_pm_cmpxchg(wcd9xxx,
+ WCD9XXX_PM_SLEEPABLE,
+ WCD9XXX_PM_ASLEEP) ==
+ WCD9XXX_PM_SLEEPABLE,
+ HZ))) {
+ pr_debug("%s: suspend failed state %d, wlock %d\n",
+ __func__, wcd9xxx->pm_state,
+ wcd9xxx->wlock_holders);
+ ret = -EBUSY;
+ } else {
+ pr_debug("%s: done, state %d, wlock %d\n", __func__,
+ wcd9xxx->pm_state, wcd9xxx->wlock_holders);
+ }
+ mutex_lock(&wcd9xxx->pm_lock);
+ } else if (wcd9xxx->pm_state == WCD9XXX_PM_ASLEEP) {
+ pr_warn("%s: system is already suspended, state %d, wlock %dn",
+ __func__, wcd9xxx->pm_state, wcd9xxx->wlock_holders);
+ }
+ mutex_unlock(&wcd9xxx->pm_lock);
+
+ return ret;
+}
+
+static int wcd9xxx_slim_suspend(struct slim_device *sldev, pm_message_t pmesg)
+{
+ struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev);
+ return wcd9xxx_suspend(wcd9xxx, pmesg);
+}
+
+static int wcd9xxx_i2c_suspend(struct i2c_client *i2cdev, pm_message_t pmesg)
+{
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(&i2cdev->dev);
+ if (wcd9xxx)
+ return wcd9xxx_suspend(wcd9xxx, pmesg);
+ else
+ return 0;
+}
+
+static const struct slim_device_id sitar_slimtest_id[] = {
+ {"sitar-slim", 0},
+ {}
+};
+static struct slim_driver sitar_slim_driver = {
+ .driver = {
+ .name = "sitar-slim",
+ .owner = THIS_MODULE,
+ },
+ .probe = wcd9xxx_slim_probe,
+ .remove = wcd9xxx_slim_remove,
+ .id_table = sitar_slimtest_id,
+ .resume = wcd9xxx_slim_resume,
+ .suspend = wcd9xxx_slim_suspend,
+};
+
+static const struct slim_device_id sitar1p1_slimtest_id[] = {
+ {"sitar1p1-slim", 0},
+ {}
+};
+static struct slim_driver sitar1p1_slim_driver = {
+ .driver = {
+ .name = "sitar1p1-slim",
+ .owner = THIS_MODULE,
+ },
+ .probe = wcd9xxx_slim_probe,
+ .remove = wcd9xxx_slim_remove,
+ .id_table = sitar1p1_slimtest_id,
+ .resume = wcd9xxx_slim_resume,
+ .suspend = wcd9xxx_slim_suspend,
+};
+
+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 = wcd9xxx_slim_probe,
+ .remove = wcd9xxx_slim_remove,
+ .id_table = slimtest_id,
+ .resume = wcd9xxx_slim_resume,
+ .suspend = wcd9xxx_slim_suspend,
+};
+
+static const struct slim_device_id slimtest2x_id[] = {
+ {"tabla2x-slim", 0},
+ {}
+};
+
+static struct slim_driver tabla2x_slim_driver = {
+ .driver = {
+ .name = "tabla2x-slim",
+ .owner = THIS_MODULE,
+ },
+ .probe = wcd9xxx_slim_probe,
+ .remove = wcd9xxx_slim_remove,
+ .id_table = slimtest2x_id,
+ .resume = wcd9xxx_slim_resume,
+ .suspend = wcd9xxx_slim_suspend,
+};
+
+#define TABLA_I2C_TOP_LEVEL 0
+#define TABLA_I2C_ANALOG 1
+#define TABLA_I2C_DIGITAL_1 2
+#define TABLA_I2C_DIGITAL_2 3
+
+static struct i2c_device_id tabla_id_table[] = {
+ {"tabla top level", TABLA_I2C_TOP_LEVEL},
+ {"tabla analog", TABLA_I2C_TOP_LEVEL},
+ {"tabla digital1", TABLA_I2C_TOP_LEVEL},
+ {"tabla digital2", TABLA_I2C_TOP_LEVEL},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, tabla_id_table);
+
+static struct i2c_driver tabla_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "tabla-i2c-core",
+ },
+ .id_table = tabla_id_table,
+ .probe = wcd9xxx_i2c_probe,
+ .remove = __devexit_p(wcd9xxx_i2c_remove),
+ .resume = wcd9xxx_i2c_resume,
+ .suspend = wcd9xxx_i2c_suspend,
+};
+
+static int __init wcd9xxx_init(void)
+{
+ int ret1, ret2, ret3, ret4, ret5;
+
+ ret1 = slim_driver_register(&tabla_slim_driver);
+ if (ret1 != 0)
+ pr_err("Failed to register tabla SB driver: %d\n", ret1);
+
+ ret2 = slim_driver_register(&tabla2x_slim_driver);
+ if (ret2 != 0)
+ pr_err("Failed to register tabla2x SB driver: %d\n", ret2);
+
+ ret3 = i2c_add_driver(&tabla_i2c_driver);
+ if (ret3 != 0)
+ pr_err("failed to add the I2C driver\n");
+
+ ret4 = slim_driver_register(&sitar_slim_driver);
+ if (ret4 != 0)
+ pr_err("Failed to register sitar SB driver: %d\n", ret4);
+
+ ret5 = slim_driver_register(&sitar1p1_slim_driver);
+ if (ret5 != 0)
+ pr_err("Failed to register sitar SB driver: %d\n", ret5);
+
+ return (ret1 && ret2 && ret3 && ret4 && ret5) ? -1 : 0;
+}
+module_init(wcd9xxx_init);
+
+static void __exit wcd9xxx_exit(void)
+{
+}
+module_exit(wcd9xxx_exit);
+
+MODULE_DESCRIPTION("Codec core driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c
new file mode 100644
index 0000000..ba3c1e8
--- /dev/null
+++ b/drivers/mfd/wcd9xxx-irq.c
@@ -0,0 +1,307 @@
+/* Copyright (c) 2011-2012, 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/sched.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd9xxx/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 wcd9xxx_irq {
+ bool level;
+};
+
+static struct wcd9xxx_irq wcd9xxx_irqs[TABLA_NUM_IRQS] = {
+ [0] = { .level = 1},
+/* All other wcd9xxx interrupts are edge triggered */
+};
+
+static inline int irq_to_wcd9xxx_irq(struct wcd9xxx *wcd9xxx, int irq)
+{
+ return irq - wcd9xxx->irq_base;
+}
+
+static void wcd9xxx_irq_lock(struct irq_data *data)
+{
+ struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
+ mutex_lock(&wcd9xxx->irq_lock);
+}
+
+static void wcd9xxx_irq_sync_unlock(struct irq_data *data)
+{
+ struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wcd9xxx->irq_masks_cur); i++) {
+ /* If there's been a change in the mask write it back
+ * to the hardware.
+ */
+ if (wcd9xxx->irq_masks_cur[i] != wcd9xxx->irq_masks_cache[i]) {
+ wcd9xxx->irq_masks_cache[i] = wcd9xxx->irq_masks_cur[i];
+ wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MASK0+i,
+ wcd9xxx->irq_masks_cur[i]);
+ }
+ }
+
+ mutex_unlock(&wcd9xxx->irq_lock);
+}
+
+static void wcd9xxx_irq_enable(struct irq_data *data)
+{
+ struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
+ int wcd9xxx_irq = irq_to_wcd9xxx_irq(wcd9xxx, data->irq);
+ wcd9xxx->irq_masks_cur[BIT_BYTE(wcd9xxx_irq)] &=
+ ~(BYTE_BIT_MASK(wcd9xxx_irq));
+}
+
+static void wcd9xxx_irq_disable(struct irq_data *data)
+{
+ struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
+ int wcd9xxx_irq = irq_to_wcd9xxx_irq(wcd9xxx, data->irq);
+ wcd9xxx->irq_masks_cur[BIT_BYTE(wcd9xxx_irq)]
+ |= BYTE_BIT_MASK(wcd9xxx_irq);
+}
+
+static struct irq_chip wcd9xxx_irq_chip = {
+ .name = "wcd9xxx",
+ .irq_bus_lock = wcd9xxx_irq_lock,
+ .irq_bus_sync_unlock = wcd9xxx_irq_sync_unlock,
+ .irq_disable = wcd9xxx_irq_disable,
+ .irq_enable = wcd9xxx_irq_enable,
+};
+
+enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg(struct wcd9xxx *wcd9xxx,
+ enum wcd9xxx_pm_state o,
+ enum wcd9xxx_pm_state n)
+{
+ enum wcd9xxx_pm_state old;
+ mutex_lock(&wcd9xxx->pm_lock);
+ old = wcd9xxx->pm_state;
+ if (old == o)
+ wcd9xxx->pm_state = n;
+ mutex_unlock(&wcd9xxx->pm_lock);
+ return old;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_pm_cmpxchg);
+
+bool wcd9xxx_lock_sleep(struct wcd9xxx *wcd9xxx)
+{
+ enum wcd9xxx_pm_state os;
+
+ /* wcd9xxx_{lock/unlock}_sleep will be called by wcd9xxx_irq_thread
+ * and its subroutines only motly.
+ * but btn0_lpress_fn is not wcd9xxx_irq_thread's subroutine and
+ * it can race with wcd9xxx_irq_thread.
+ * so need to embrace wlock_holders with mutex.
+ */
+ mutex_lock(&wcd9xxx->pm_lock);
+ if (wcd9xxx->wlock_holders++ == 0) {
+ pr_debug("%s: holding wake lock\n", __func__);
+ wake_lock(&wcd9xxx->wlock);
+ }
+ mutex_unlock(&wcd9xxx->pm_lock);
+ if (!wait_event_timeout(wcd9xxx->pm_wq,
+ ((os = wcd9xxx_pm_cmpxchg(wcd9xxx, WCD9XXX_PM_SLEEPABLE,
+ WCD9XXX_PM_AWAKE)) ==
+ WCD9XXX_PM_SLEEPABLE ||
+ (os == WCD9XXX_PM_AWAKE)),
+ 5 * HZ)) {
+ pr_err("%s: system didn't resume within 5000ms, state %d, "
+ "wlock %d\n", __func__, wcd9xxx->pm_state,
+ wcd9xxx->wlock_holders);
+ WARN_ON(1);
+ wcd9xxx_unlock_sleep(wcd9xxx);
+ return false;
+ }
+ wake_up_all(&wcd9xxx->pm_wq);
+ return true;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_lock_sleep);
+
+void wcd9xxx_unlock_sleep(struct wcd9xxx *wcd9xxx)
+{
+ mutex_lock(&wcd9xxx->pm_lock);
+ if (--wcd9xxx->wlock_holders == 0) {
+ wcd9xxx->pm_state = WCD9XXX_PM_SLEEPABLE;
+ pr_debug("%s: releasing wake lock\n", __func__);
+ wake_unlock(&wcd9xxx->wlock);
+ }
+ mutex_unlock(&wcd9xxx->pm_lock);
+ wake_up_all(&wcd9xxx->pm_wq);
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_unlock_sleep);
+
+static void wcd9xxx_irq_dispatch(struct wcd9xxx *wcd9xxx, int irqbit)
+{
+ if ((irqbit <= TABLA_IRQ_MBHC_INSERTION) &&
+ (irqbit >= TABLA_IRQ_MBHC_REMOVAL)) {
+ wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_CLEAR0 +
+ BIT_BYTE(irqbit), BYTE_BIT_MASK(irqbit));
+ if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
+ wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MODE, 0x02);
+ handle_nested_irq(wcd9xxx->irq_base + irqbit);
+ } else {
+ handle_nested_irq(wcd9xxx->irq_base + irqbit);
+ wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_CLEAR0 +
+ BIT_BYTE(irqbit), BYTE_BIT_MASK(irqbit));
+ if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
+ wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MODE, 0x02);
+ }
+}
+
+static irqreturn_t wcd9xxx_irq_thread(int irq, void *data)
+{
+ int ret;
+ struct wcd9xxx *wcd9xxx = data;
+ u8 status[WCD9XXX_NUM_IRQ_REGS];
+ int i;
+
+ if (unlikely(wcd9xxx_lock_sleep(wcd9xxx) == false)) {
+ dev_err(wcd9xxx->dev, "Failed to hold suspend\n");
+ return IRQ_NONE;
+ }
+ ret = wcd9xxx_bulk_read(wcd9xxx, TABLA_A_INTR_STATUS0,
+ WCD9XXX_NUM_IRQ_REGS, status);
+ if (ret < 0) {
+ dev_err(wcd9xxx->dev, "Failed to read interrupt status: %d\n",
+ ret);
+ wcd9xxx_unlock_sleep(wcd9xxx);
+ return IRQ_NONE;
+ }
+ /* Apply masking */
+ for (i = 0; i < WCD9XXX_NUM_IRQ_REGS; i++)
+ status[i] &= ~wcd9xxx->irq_masks_cur[i];
+
+ /* Find out which interrupt was triggered and call that interrupt's
+ * handler function
+ */
+ if (status[BIT_BYTE(TABLA_IRQ_SLIMBUS)] &
+ BYTE_BIT_MASK(TABLA_IRQ_SLIMBUS))
+ wcd9xxx_irq_dispatch(wcd9xxx, TABLA_IRQ_SLIMBUS);
+
+ /* Since codec has only one hardware irq line which is shared by
+ * codec's different internal interrupts, so it's possible master irq
+ * handler dispatches multiple nested irq handlers after breaking
+ * order. Dispatch MBHC interrupts order to follow MBHC state
+ * machine's order */
+ for (i = TABLA_IRQ_MBHC_INSERTION; i >= TABLA_IRQ_MBHC_REMOVAL; i--) {
+ if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i))
+ wcd9xxx_irq_dispatch(wcd9xxx, i);
+ }
+ for (i = TABLA_IRQ_BG_PRECHARGE; i < TABLA_NUM_IRQS; i++) {
+ if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i))
+ wcd9xxx_irq_dispatch(wcd9xxx, i);
+ }
+ wcd9xxx_unlock_sleep(wcd9xxx);
+
+ return IRQ_HANDLED;
+}
+
+int wcd9xxx_irq_init(struct wcd9xxx *wcd9xxx)
+{
+ int ret;
+ unsigned int i, cur_irq;
+
+ mutex_init(&wcd9xxx->irq_lock);
+
+ if (!wcd9xxx->irq) {
+ dev_warn(wcd9xxx->dev,
+ "No interrupt specified, no interrupts\n");
+ wcd9xxx->irq_base = 0;
+ return 0;
+ }
+
+ if (!wcd9xxx->irq_base) {
+ dev_err(wcd9xxx->dev,
+ "No interrupt base specified, no interrupts\n");
+ return 0;
+ }
+ /* Mask the individual interrupt sources */
+ for (i = 0, cur_irq = wcd9xxx->irq_base; i < TABLA_NUM_IRQS; i++,
+ cur_irq++) {
+
+ irq_set_chip_data(cur_irq, wcd9xxx);
+
+ if (wcd9xxx_irqs[i].level)
+ irq_set_chip_and_handler(cur_irq, &wcd9xxx_irq_chip,
+ handle_level_irq);
+ else
+ irq_set_chip_and_handler(cur_irq, &wcd9xxx_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
+
+ wcd9xxx->irq_masks_cur[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
+ wcd9xxx->irq_masks_cache[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
+ wcd9xxx->irq_level[BIT_BYTE(i)] |= wcd9xxx_irqs[i].level <<
+ (i % BITS_PER_BYTE);
+ }
+ for (i = 0; i < WCD9XXX_NUM_IRQ_REGS; i++) {
+ /* Initialize interrupt mask and level registers */
+ wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_LEVEL0 + i,
+ wcd9xxx->irq_level[i]);
+ wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MASK0 + i,
+ wcd9xxx->irq_masks_cur[i]);
+ }
+
+ ret = request_threaded_irq(wcd9xxx->irq, NULL, wcd9xxx_irq_thread,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "wcd9xxx", wcd9xxx);
+ if (ret != 0)
+ dev_err(wcd9xxx->dev, "Failed to request IRQ %d: %d\n",
+ wcd9xxx->irq, ret);
+ else {
+ ret = enable_irq_wake(wcd9xxx->irq);
+ if (ret == 0) {
+ ret = device_init_wakeup(wcd9xxx->dev, 1);
+ if (ret) {
+ dev_err(wcd9xxx->dev, "Failed to init device"
+ "wakeup : %d\n", ret);
+ disable_irq_wake(wcd9xxx->irq);
+ }
+ } else
+ dev_err(wcd9xxx->dev, "Failed to set wake interrupt on"
+ " IRQ %d: %d\n", wcd9xxx->irq, ret);
+ if (ret)
+ free_irq(wcd9xxx->irq, wcd9xxx);
+ }
+
+ if (ret)
+ mutex_destroy(&wcd9xxx->irq_lock);
+
+ return ret;
+}
+
+void wcd9xxx_irq_exit(struct wcd9xxx *wcd9xxx)
+{
+ if (wcd9xxx->irq) {
+ disable_irq_wake(wcd9xxx->irq);
+ free_irq(wcd9xxx->irq, wcd9xxx);
+ device_init_wakeup(wcd9xxx->dev, 0);
+ }
+ mutex_destroy(&wcd9xxx->irq_lock);
+}
diff --git a/drivers/mfd/wcd9xxx-slimslave.c b/drivers/mfd/wcd9xxx-slimslave.c
new file mode 100644
index 0000000..538ca7c
--- /dev/null
+++ b/drivers/mfd/wcd9xxx-slimslave.c
@@ -0,0 +1,528 @@
+/* Copyright (c) 2012, 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/slab.h>
+#include <linux/mutex.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
+
+struct wcd9xxx_slim_sch_rx {
+ u32 sph;
+ u32 ch_num;
+ u16 ch_h;
+ u16 grph;
+};
+
+struct wcd9xxx_slim_sch_tx {
+ u32 sph;
+ u32 ch_num;
+ u16 ch_h;
+ u16 grph;
+};
+
+struct wcd9xxx_slim_sch {
+ struct wcd9xxx_slim_sch_rx rx[SLIM_MAX_RX_PORTS];
+ struct wcd9xxx_slim_sch_tx tx[SLIM_MAX_TX_PORTS];
+};
+
+static struct wcd9xxx_slim_sch sh_ch;
+
+static int wcd9xxx_alloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx,
+ u8 wcd9xxx_pgd_la);
+static int wcd9xxx_alloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx,
+ u8 wcd9xxx_pgd_la);
+static int wcd9xxx_dealloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx);
+static int wcd9xxx_dealloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx);
+
+int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la)
+{
+ int ret = 0;
+
+ ret = wcd9xxx_alloc_slim_sh_ch_rx(wcd9xxx, wcd9xxx_pgd_la);
+ if (ret) {
+ pr_err("%s: Failed to alloc rx slimbus shared channels\n",
+ __func__);
+ goto rx_err;
+ }
+ ret = wcd9xxx_alloc_slim_sh_ch_tx(wcd9xxx, wcd9xxx_pgd_la);
+ if (ret) {
+ pr_err("%s: Failed to alloc tx slimbus shared channels\n",
+ __func__);
+ goto tx_err;
+ }
+ return 0;
+tx_err:
+ wcd9xxx_dealloc_slim_sh_ch_rx(wcd9xxx);
+rx_err:
+ return ret;
+}
+
+
+int wcd9xxx_deinit_slimslave(struct wcd9xxx *wcd9xxx)
+{
+ int ret = 0;
+ ret = wcd9xxx_dealloc_slim_sh_ch_rx(wcd9xxx);
+ if (ret < 0) {
+ pr_err("%s: fail to dealloc rx slim ports\n", __func__);
+ goto err;
+ }
+ ret = wcd9xxx_dealloc_slim_sh_ch_tx(wcd9xxx);
+ if (ret < 0) {
+ pr_err("%s: fail to dealloc tx slim ports\n", __func__);
+ goto err;
+ }
+err:
+ return ret;
+}
+
+int wcd9xxx_get_channel(struct wcd9xxx *wcd9xxx,
+ unsigned int *rx_ch,
+ unsigned int *tx_ch)
+{
+ int ch_idx = 0;
+ struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+ struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+
+ for (ch_idx = 0; ch_idx < SLIM_MAX_RX_PORTS; ch_idx++)
+ rx_ch[ch_idx] = rx[ch_idx].ch_num;
+ for (ch_idx = 0; ch_idx < SLIM_MAX_TX_PORTS; ch_idx++)
+ tx_ch[ch_idx] = tx[ch_idx].ch_num;
+ return 0;
+}
+
+static int wcd9xxx_alloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx,
+ u8 wcd9xxx_pgd_la)
+{
+ int ret = 0;
+ u8 ch_idx ;
+ u16 slave_port_id = 0;
+ struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+
+ /*
+ * DSP requires channel number to be between 128 and 255.
+ */
+ pr_debug("%s: pgd_la[%d]\n", __func__, wcd9xxx_pgd_la);
+ for (ch_idx = 0; ch_idx < SLIM_MAX_RX_PORTS; ch_idx++) {
+ slave_port_id = (ch_idx + 1 +
+ SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS);
+ rx[ch_idx].ch_num = slave_port_id + BASE_CH_NUM;
+ ret = slim_get_slaveport(wcd9xxx_pgd_la, slave_port_id,
+ &rx[ch_idx].sph, SLIM_SINK);
+ if (ret < 0) {
+ pr_err("%s: slave port failure id[%d] ret[%d]\n",
+ __func__, slave_port_id, ret);
+ goto err;
+ }
+
+ ret = slim_query_ch(wcd9xxx->slim, rx[ch_idx].ch_num,
+ &rx[ch_idx].ch_h);
+ if (ret < 0) {
+ pr_err("%s: slim_query_ch failed ch-num[%d] ret[%d]\n",
+ __func__, rx[ch_idx].ch_num, ret);
+ goto err;
+ }
+ }
+err:
+ return ret;
+}
+
+static int wcd9xxx_alloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx,
+ u8 wcd9xxx_pgd_la)
+{
+ int ret = 0;
+ u8 ch_idx ;
+ struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+ u16 slave_port_id = 0;
+
+ pr_debug("%s: pgd_la[%d]\n", __func__, wcd9xxx_pgd_la);
+ /* DSP requires channel number to be between 128 and 255. For RX port
+ * use channel numbers from 138 to 144, for TX port
+ * use channel numbers from 128 to 137
+ */
+ for (ch_idx = 0; ch_idx < SLIM_MAX_TX_PORTS; ch_idx++) {
+ slave_port_id = ch_idx;
+ tx[ch_idx].ch_num = slave_port_id + BASE_CH_NUM;
+ ret = slim_get_slaveport(wcd9xxx_pgd_la, slave_port_id,
+ &tx[ch_idx].sph, SLIM_SRC);
+ if (ret < 0) {
+ pr_err("%s: slave port failure id[%d] ret[%d]\n",
+ __func__, slave_port_id, ret);
+ goto err;
+ }
+ ret = slim_query_ch(wcd9xxx->slim, tx[ch_idx].ch_num,
+ &tx[ch_idx].ch_h);
+ if (ret < 0) {
+ pr_err("%s: slim_query_ch failed ch-num[%d] ret[%d]\n",
+ __func__, tx[ch_idx].ch_num, ret);
+ goto err;
+ }
+ }
+err:
+ return ret;
+}
+
+static int wcd9xxx_dealloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx)
+{
+ int idx = 0;
+ int ret = 0;
+ struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+ /* slim_dealloc_ch */
+ for (idx = 0; idx < SLIM_MAX_RX_PORTS; idx++) {
+ ret = slim_dealloc_ch(wcd9xxx->slim, rx[idx].ch_h);
+ if (ret < 0) {
+ pr_err("%s: slim_dealloc_ch fail ret[%d] ch_h[%d]\n",
+ __func__, ret, rx[idx].ch_h);
+ }
+ }
+ memset(sh_ch.rx, 0, sizeof(sh_ch.rx));
+ return ret;
+}
+
+static int wcd9xxx_dealloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx)
+{
+ int idx = 0;
+ int ret = 0;
+ struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+ /* slim_dealloc_ch */
+ for (idx = 0; idx < SLIM_MAX_TX_PORTS; idx++) {
+ ret = slim_dealloc_ch(wcd9xxx->slim, tx[idx].ch_h);
+ if (ret < 0) {
+ pr_err("%s: slim_dealloc_ch fail ret[%d] ch_h[%d]\n",
+ __func__, ret, tx[idx].ch_h);
+ }
+ }
+ memset(sh_ch.tx, 0, sizeof(sh_ch.tx));
+ return ret;
+}
+
+/* Enable slimbus slave device for RX path */
+int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
+ unsigned int ch_cnt, unsigned int rate)
+{
+ u8 i = 0;
+ u16 grph;
+ u32 sph[SLIM_MAX_RX_PORTS] = {0};
+ u16 ch_h[SLIM_MAX_RX_PORTS] = {0};
+ u16 slave_port_id;
+ u8 payload_rx = 0, wm_payload = 0;
+ int ret, idx = 0;
+ unsigned short multi_chan_cfg_reg_addr;
+ struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+ struct slim_ch prop;
+
+ /* Configure slave interface device */
+ pr_debug("%s: ch_cnt[%d] rate=%d\n", __func__, ch_cnt, rate);
+
+ for (i = 0; i < ch_cnt; i++) {
+ idx = (ch_num[i] - BASE_CH_NUM -
+ SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS - 1);
+ ch_h[i] = rx[idx].ch_h;
+ sph[i] = rx[idx].sph;
+ slave_port_id = idx + 1;
+ if ((slave_port_id > SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS) ||
+ (slave_port_id == 0)) {
+ pr_err("Slimbus: invalid slave port id: %d",
+ slave_port_id);
+ ret = -EINVAL;
+ goto err;
+ }
+ slave_port_id += SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS;
+ /* look for the valid port range and chose the
+ * payload accordingly
+ */
+ if ((slave_port_id >
+ SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID) &&
+ (slave_port_id <=
+ SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID)) {
+ payload_rx = payload_rx |
+ (1 <<
+ (slave_port_id -
+ SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID));
+ } else {
+ ret = -EINVAL;
+ goto err;
+ }
+ multi_chan_cfg_reg_addr =
+ SB_PGD_RX_PORT_MULTI_CHANNEL_0(slave_port_id);
+ /* write to interface device */
+ ret = wcd9xxx_interface_reg_write(wcd9xxx,
+ multi_chan_cfg_reg_addr,
+ payload_rx);
+ if (ret < 0) {
+ pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+ __func__,
+ multi_chan_cfg_reg_addr,
+ payload_rx, ret);
+ goto err;
+ }
+ /* configure the slave port for water mark and enable*/
+ wm_payload = (SLAVE_PORT_WATER_MARK_VALUE <<
+ SLAVE_PORT_WATER_MARK_SHIFT) +
+ SLAVE_PORT_ENABLE;
+ ret = wcd9xxx_interface_reg_write(wcd9xxx,
+ SB_PGD_PORT_CFG_BYTE_ADDR(slave_port_id),
+ wm_payload);
+ if (ret < 0) {
+ pr_err("%s:watermark set failure for port[%d] ret[%d]",
+ __func__, slave_port_id, ret);
+ }
+ }
+
+ /* slim_define_ch api */
+ prop.prot = SLIM_AUTO_ISO;
+ prop.baser = SLIM_RATE_4000HZ;
+ prop.dataf = SLIM_CH_DATAF_NOT_DEFINED;
+ prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
+ prop.ratem = (rate/4000);
+ prop.sampleszbits = 16;
+
+ ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt,
+ true, &grph);
+ if (ret < 0) {
+ pr_err("%s: slim_define_ch failed ret[%d]\n",
+ __func__, ret);
+ goto err;
+ }
+ for (i = 0; i < ch_cnt; i++) {
+ ret = slim_connect_sink(wcd9xxx->slim, &sph[i],
+ 1, ch_h[i]);
+ if (ret < 0) {
+ pr_err("%s: slim_connect_sink failed ret[%d]\n",
+ __func__, ret);
+ goto err_close_slim_sch;
+ }
+ }
+ /* slim_control_ch */
+ ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_ACTIVATE,
+ true);
+ if (ret < 0) {
+ pr_err("%s: slim_control_ch failed ret[%d]\n",
+ __func__, ret);
+ goto err_close_slim_sch;
+ }
+ for (i = 0; i < ch_cnt; i++) {
+ idx = (ch_num[i] - BASE_CH_NUM -
+ SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS - 1);
+ rx[idx].grph = grph;
+ }
+ return 0;
+
+err_close_slim_sch:
+ /* release all acquired handles */
+ wcd9xxx_close_slim_sch_rx(wcd9xxx, ch_num, ch_cnt);
+err:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_cfg_slim_sch_rx);
+
+/* Enable slimbus slave device for RX path */
+int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
+ unsigned int ch_cnt, unsigned int rate)
+{
+ u8 i = 0;
+ u8 payload_tx_0 = 0, payload_tx_1 = 0, wm_payload = 0;
+ u16 grph;
+ u32 sph[SLIM_MAX_TX_PORTS] = {0};
+ u16 ch_h[SLIM_MAX_TX_PORTS] = {0};
+ u16 idx = 0, slave_port_id;
+ int ret = 0;
+ unsigned short multi_chan_cfg_reg_addr;
+
+ struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+ struct slim_ch prop;
+
+ pr_debug("%s: ch_cnt[%d] rate[%d]\n", __func__, ch_cnt, rate);
+ for (i = 0; i < ch_cnt; i++) {
+ idx = (ch_num[i] - BASE_CH_NUM);
+ ch_h[i] = tx[idx].ch_h;
+ sph[i] = tx[idx].sph;
+ slave_port_id = idx ;
+ if (slave_port_id > SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS) {
+ pr_err("SLIMbus: invalid slave port id: %d",
+ slave_port_id);
+ ret = -EINVAL;
+ goto err;
+ }
+ /* look for the valid port range and chose the
+ * payload accordingly
+ */
+ if (slave_port_id <=
+ SB_PGD_TX_PORT_MULTI_CHANNEL_0_END_PORT_ID) {
+ payload_tx_0 = payload_tx_0 | (1 << slave_port_id);
+ } else if (slave_port_id <=
+ SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID) {
+ payload_tx_1 = payload_tx_1 |
+ (1 <<
+ (slave_port_id -
+ SB_PGD_TX_PORT_MULTI_CHANNEL_1_START_PORT_ID));
+ } else {
+ ret = -EINVAL;
+ goto err;
+ }
+ multi_chan_cfg_reg_addr =
+ SB_PGD_TX_PORT_MULTI_CHANNEL_0(slave_port_id);
+ /* write to interface device */
+ ret = wcd9xxx_interface_reg_write(wcd9xxx,
+ multi_chan_cfg_reg_addr,
+ payload_tx_0);
+ if (ret < 0) {
+ pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+ __func__,
+ multi_chan_cfg_reg_addr,
+ payload_tx_0, ret);
+ goto err;
+ }
+ multi_chan_cfg_reg_addr =
+ SB_PGD_TX_PORT_MULTI_CHANNEL_1(slave_port_id);
+ /* ports 8,9 */
+ ret = wcd9xxx_interface_reg_write(wcd9xxx,
+ multi_chan_cfg_reg_addr,
+ payload_tx_1);
+ if (ret < 0) {
+ pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+ __func__,
+ multi_chan_cfg_reg_addr,
+ payload_tx_1, ret);
+ goto err;
+ }
+ /* configure the slave port for water mark and enable*/
+ wm_payload = (SLAVE_PORT_WATER_MARK_VALUE <<
+ SLAVE_PORT_WATER_MARK_SHIFT) +
+ SLAVE_PORT_ENABLE;
+ ret = wcd9xxx_interface_reg_write(wcd9xxx,
+ SB_PGD_PORT_CFG_BYTE_ADDR(slave_port_id),
+ wm_payload);
+ if (ret < 0) {
+ pr_err("%s:watermark set failure for port[%d] ret[%d]",
+ __func__,
+ slave_port_id, ret);
+ }
+ }
+
+ /* slim_define_ch api */
+ prop.prot = SLIM_AUTO_ISO;
+ prop.baser = SLIM_RATE_4000HZ;
+ prop.dataf = SLIM_CH_DATAF_NOT_DEFINED;
+ prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
+ prop.ratem = (rate/4000);
+ prop.sampleszbits = 16;
+ ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt,
+ true, &grph);
+ if (ret < 0) {
+ pr_err("%s: slim_define_ch failed ret[%d]\n",
+ __func__, ret);
+ goto err;
+ }
+ for (i = 0; i < ch_cnt; i++) {
+ ret = slim_connect_src(wcd9xxx->slim, sph[i],
+ ch_h[i]);
+ if (ret < 0) {
+ pr_err("%s: slim_connect_src failed ret[%d]\n",
+ __func__, ret);
+ goto err;
+ }
+ }
+ /* slim_control_ch */
+ ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_ACTIVATE,
+ true);
+ if (ret < 0) {
+ pr_err("%s: slim_control_ch failed ret[%d]\n",
+ __func__, ret);
+ goto err;
+ }
+ for (i = 0; i < ch_cnt; i++) {
+ idx = (ch_num[i] - BASE_CH_NUM);
+ tx[idx].grph = grph;
+ }
+ return 0;
+err:
+ /* release all acquired handles */
+ wcd9xxx_close_slim_sch_tx(wcd9xxx, ch_num, ch_cnt);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_cfg_slim_sch_tx);
+
+int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
+ unsigned int ch_cnt)
+{
+ u16 grph = 0;
+ u32 sph[SLIM_MAX_RX_PORTS] = {0};
+ int i = 0 , idx = 0;
+ int ret = 0;
+ struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+
+ pr_debug("%s: ch_cnt[%d]\n", __func__, ch_cnt);
+ for (i = 0; i < ch_cnt; i++) {
+ idx = (ch_num[i] - BASE_CH_NUM -
+ SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS - 1);
+ if (idx < 0) {
+ pr_err("%s: Error:-Invalid index found = %d\n",
+ __func__, idx);
+ ret = -EINVAL;
+ goto err;
+ }
+ sph[i] = rx[idx].sph;
+ grph = rx[idx].grph;
+ }
+
+ /* slim_control_ch (REMOVE) */
+ ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true);
+ if (ret < 0) {
+ pr_err("%s: slim_control_ch failed ret[%d]\n",
+ __func__, ret);
+ goto err;
+ }
+ for (i = 0; i < ch_cnt; i++) {
+ idx = (ch_num[i] - BASE_CH_NUM -
+ SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS - 1);
+ rx[idx].grph = 0;
+ }
+err:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_close_slim_sch_rx);
+
+int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
+ unsigned int ch_cnt)
+{
+ u16 grph = 0;
+ u32 sph[SLIM_MAX_TX_PORTS] = {0};
+ int ret = 0;
+ int i = 0 , idx = 0;
+ struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+
+ pr_debug("%s: ch_cnt[%d]\n", __func__, ch_cnt);
+ for (i = 0; i < ch_cnt; i++) {
+ idx = (ch_num[i] - BASE_CH_NUM);
+ if (idx < 0) {
+ pr_err("%s: Error:- Invalid index found = %d\n",
+ __func__, idx);
+ ret = -EINVAL;
+ goto err;
+ }
+ sph[i] = tx[idx].sph;
+ grph = tx[idx].grph;
+ }
+
+ /* slim_control_ch (REMOVE) */
+ ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true);
+ if (ret < 0) {
+ pr_err("%s: slim_control_ch failed ret[%d]\n",
+ __func__, ret);
+ goto err;
+ }
+ for (i = 0; i < ch_cnt; i++) {
+ idx = (ch_num[i] - BASE_CH_NUM);
+ tx[idx].grph = 0;
+ }
+err:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_close_slim_sch_tx);