2019-05-27 09:55:05 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2008-10-15 14:15:39 +04:00
/*
2009-12-13 23:23:33 +03:00
* twl_core . c - driver for TWL4030 / TWL5030 / TWL60X0 / TPS659x0 PM
* and audio CODEC devices
2008-10-15 14:15:39 +04:00
*
* Copyright ( C ) 2005 - 2006 Texas Instruments , Inc .
*
* Modifications to defer interrupt handling to a kernel thread :
* Copyright ( C ) 2006 MontaVista Software , Inc .
*
* Based on tlv320aic23 . c :
* Copyright ( c ) by Kai Svahn < kai . svahn @ nokia . com >
*
* Code cleanup and modifications to IRQ handler .
* by syed khasim < x0khasim @ ti . com >
*/
# include <linux/init.h>
# include <linux/mutex.h>
# include <linux/platform_device.h>
2012-11-13 12:28:44 +04:00
# include <linux/regmap.h>
2008-10-15 14:15:39 +04:00
# include <linux/clk.h>
2008-10-21 01:46:28 +04:00
# include <linux/err.h>
2011-08-29 18:20:23 +04:00
# include <linux/device.h>
# include <linux/of.h>
# include <linux/of_irq.h>
# include <linux/of_platform.h>
2012-03-03 08:05:31 +04:00
# include <linux/irq.h>
2011-08-29 18:20:23 +04:00
# include <linux/irqdomain.h>
2008-10-15 14:15:39 +04:00
2008-12-01 02:43:58 +03:00
# include <linux/regulator/machine.h>
2008-10-15 14:15:39 +04:00
# include <linux/i2c.h>
2017-08-14 19:34:24 +03:00
# include <linux/mfd/twl.h>
2008-10-15 14:15:39 +04:00
2014-01-03 17:27:48 +04:00
/* Register descriptions for audio */
# include <linux/mfd/twl4030-audio.h>
2012-02-29 22:28:14 +04:00
# include "twl-core.h"
2008-10-15 14:15:39 +04:00
/*
* The TWL4030 " Triton 2 " is one of a family of a multi - function " Power
* Management and System Companion Device " chips originally designed for
* use in OMAP2 and OMAP 3 based systems . Its control interfaces use I2C ,
* often at around 3 Mbit / sec , including for interrupt handling .
*
* This driver core provides genirq support for the interrupts emitted ,
* by the various modules , and exports register access primitives .
*
* FIXME this driver currently requires use of the first interrupt line
* ( and associated registers ) .
*/
2009-12-13 23:23:33 +03:00
# define DRIVER_NAME "twl"
2008-10-15 14:15:39 +04:00
/* Triton Core internal information (BEGIN) */
/* Base Address defns for twl4030_map[] */
/* subchip/slave 0 - USB ID */
# define TWL4030_BASEADD_USB 0x0000
/* subchip/slave 1 - AUD ID */
# define TWL4030_BASEADD_AUDIO_VOICE 0x0000
# define TWL4030_BASEADD_GPIO 0x0098
# define TWL4030_BASEADD_INTBR 0x0085
# define TWL4030_BASEADD_PIH 0x0080
# define TWL4030_BASEADD_TEST 0x004C
/* subchip/slave 2 - AUX ID */
# define TWL4030_BASEADD_INTERRUPTS 0x00B9
# define TWL4030_BASEADD_LED 0x00EE
# define TWL4030_BASEADD_MADC 0x0000
# define TWL4030_BASEADD_MAIN_CHARGE 0x0074
# define TWL4030_BASEADD_PRECHARGE 0x00AA
2013-01-16 17:53:50 +04:00
# define TWL4030_BASEADD_PWM 0x00F8
2008-10-15 14:15:39 +04:00
# define TWL4030_BASEADD_KEYPAD 0x00D2
2009-11-10 18:26:15 +03:00
# define TWL5031_BASEADD_ACCESSORY 0x0074 /* Replaces Main Charge */
# define TWL5031_BASEADD_INTERRUPTS 0x00B9 / * Different than TWL4030's
one */
2008-10-15 14:15:39 +04:00
/* subchip/slave 3 - POWER ID */
# define TWL4030_BASEADD_BACKUP 0x0014
# define TWL4030_BASEADD_INT 0x002E
# define TWL4030_BASEADD_PM_MASTER 0x0036
2014-04-11 03:47:18 +04:00
2008-10-15 14:15:39 +04:00
# define TWL4030_BASEADD_PM_RECEIVER 0x005B
2014-04-11 03:47:18 +04:00
# define TWL4030_DCDC_GLOBAL_CFG 0x06
# define SMARTREFLEX_ENABLE BIT(3)
2008-10-15 14:15:39 +04:00
# define TWL4030_BASEADD_RTC 0x001C
# define TWL4030_BASEADD_SECURED_REG 0x0000
/* Triton Core internal information (END) */
2009-12-14 02:25:31 +03:00
/* subchip/slave 0 0x48 - POWER */
# define TWL6030_BASEADD_RTC 0x0000
2013-01-16 17:53:50 +04:00
# define TWL6030_BASEADD_SECURED_REG 0x0017
2009-12-14 02:25:31 +03:00
# define TWL6030_BASEADD_PM_MASTER 0x001F
# define TWL6030_BASEADD_PM_SLAVE_MISC 0x0030 /* PM_RECEIVER */
# define TWL6030_BASEADD_PM_MISC 0x00E2
# define TWL6030_BASEADD_PM_PUPD 0x00F0
/* subchip/slave 1 0x49 - FEATURE */
# define TWL6030_BASEADD_USB 0x0000
# define TWL6030_BASEADD_GPADC_CTRL 0x002E
# define TWL6030_BASEADD_AUX 0x0090
# define TWL6030_BASEADD_PWM 0x00BA
# define TWL6030_BASEADD_GASGAUGE 0x00C0
# define TWL6030_BASEADD_PIH 0x00D0
# define TWL6030_BASEADD_CHARGER 0x00E0
2013-06-19 16:24:02 +04:00
# define TWL6032_BASEADD_CHARGER 0x00DA
2013-01-16 17:53:50 +04:00
# define TWL6030_BASEADD_LED 0x00F4
2009-12-14 02:25:31 +03:00
/* subchip/slave 2 0x4A - DFT */
# define TWL6030_BASEADD_DIEID 0x00C0
/* subchip/slave 3 0x4B - AUDIO */
# define TWL6030_BASEADD_AUDIO 0x0000
# define TWL6030_BASEADD_RSV 0x0000
2010-02-19 14:39:38 +03:00
# define TWL6030_BASEADD_ZERO 0x0000
2009-12-14 02:25:31 +03:00
2008-10-15 14:15:39 +04:00
/* Few power values */
# define R_CFG_BOOT 0x05
/* some fields in R_CFG_BOOT */
# define HFCLK_FREQ_19p2_MHZ (1 << 0)
# define HFCLK_FREQ_26_MHZ (2 << 0)
# define HFCLK_FREQ_38p4_MHZ (3 << 0)
# define HIGH_PERF_SQ (1 << 3)
2009-10-22 15:14:09 +04:00
# define CK32K_LOWPWR_EN (1 << 7)
2008-10-15 14:15:39 +04:00
/*----------------------------------------------------------------------*/
2009-12-14 02:25:31 +03:00
/* Structure for each TWL4030/TWL6030 Slave */
2009-12-13 23:23:33 +03:00
struct twl_client {
2008-10-15 14:15:39 +04:00
struct i2c_client * client ;
2012-11-13 12:28:44 +04:00
struct regmap * regmap ;
2008-10-15 14:15:39 +04:00
} ;
/* mapping the module id to slave id and base address */
2009-12-13 23:23:33 +03:00
struct twl_mapping {
2008-10-15 14:15:39 +04:00
unsigned char sid ; /* Slave ID */
unsigned char base ; /* base address */
} ;
2013-01-16 17:53:57 +04:00
struct twl_private {
bool ready ; /* The core driver is ready to be used */
u32 twl_idcode ; /* TWL IDCODE Register value */
unsigned int twl_id ;
struct twl_mapping * twl_map ;
struct twl_client * twl_modules ;
} ;
static struct twl_private * twl_priv ;
2008-10-15 14:15:39 +04:00
2012-11-13 12:28:48 +04:00
static struct twl_mapping twl4030_map [ ] = {
2008-10-15 14:15:39 +04:00
/*
* NOTE : don ' t change this table without updating the
2017-08-14 19:34:24 +03:00
* < linux / mfd / twl . h > defines for TWL4030_MODULE_ *
2008-10-15 14:15:39 +04:00
* so they continue to match the order in this table .
*/
2013-01-16 17:53:50 +04:00
/* Common IPs */
2008-10-15 14:15:39 +04:00
{ 0 , TWL4030_BASEADD_USB } ,
2013-01-16 17:53:50 +04:00
{ 1 , TWL4030_BASEADD_PIH } ,
{ 2 , TWL4030_BASEADD_MAIN_CHARGE } ,
{ 3 , TWL4030_BASEADD_PM_MASTER } ,
{ 3 , TWL4030_BASEADD_PM_RECEIVER } ,
{ 3 , TWL4030_BASEADD_RTC } ,
{ 2 , TWL4030_BASEADD_PWM } ,
{ 2 , TWL4030_BASEADD_LED } ,
{ 3 , TWL4030_BASEADD_SECURED_REG } ,
/* TWL4030 specific IPs */
2008-10-15 14:15:39 +04:00
{ 1 , TWL4030_BASEADD_AUDIO_VOICE } ,
{ 1 , TWL4030_BASEADD_GPIO } ,
{ 1 , TWL4030_BASEADD_INTBR } ,
2012-11-13 12:28:50 +04:00
{ 1 , TWL4030_BASEADD_TEST } ,
2008-10-15 14:15:39 +04:00
{ 2 , TWL4030_BASEADD_KEYPAD } ,
2013-01-16 17:53:50 +04:00
2008-10-15 14:15:39 +04:00
{ 2 , TWL4030_BASEADD_MADC } ,
{ 2 , TWL4030_BASEADD_INTERRUPTS } ,
{ 2 , TWL4030_BASEADD_PRECHARGE } ,
{ 3 , TWL4030_BASEADD_BACKUP } ,
{ 3 , TWL4030_BASEADD_INT } ,
2012-11-13 12:28:50 +04:00
2013-01-16 17:53:50 +04:00
{ 2 , TWL5031_BASEADD_ACCESSORY } ,
{ 2 , TWL5031_BASEADD_INTERRUPTS } ,
2008-10-15 14:15:39 +04:00
} ;
2015-01-05 12:01:32 +03:00
static const struct reg_default twl4030_49_defaults [ ] = {
2014-01-03 17:27:48 +04:00
/* Audio Registers */
{ 0x01 , 0x00 } , /* CODEC_MODE */
{ 0x02 , 0x00 } , /* OPTION */
/* 0x03 Unused */
{ 0x04 , 0x00 } , /* MICBIAS_CTL */
{ 0x05 , 0x00 } , /* ANAMICL */
{ 0x06 , 0x00 } , /* ANAMICR */
{ 0x07 , 0x00 } , /* AVADC_CTL */
{ 0x08 , 0x00 } , /* ADCMICSEL */
{ 0x09 , 0x00 } , /* DIGMIXING */
{ 0x0a , 0x0f } , /* ATXL1PGA */
{ 0x0b , 0x0f } , /* ATXR1PGA */
{ 0x0c , 0x0f } , /* AVTXL2PGA */
{ 0x0d , 0x0f } , /* AVTXR2PGA */
{ 0x0e , 0x00 } , /* AUDIO_IF */
{ 0x0f , 0x00 } , /* VOICE_IF */
{ 0x10 , 0x3f } , /* ARXR1PGA */
{ 0x11 , 0x3f } , /* ARXL1PGA */
{ 0x12 , 0x3f } , /* ARXR2PGA */
{ 0x13 , 0x3f } , /* ARXL2PGA */
{ 0x14 , 0x25 } , /* VRXPGA */
{ 0x15 , 0x00 } , /* VSTPGA */
{ 0x16 , 0x00 } , /* VRX2ARXPGA */
{ 0x17 , 0x00 } , /* AVDAC_CTL */
{ 0x18 , 0x00 } , /* ARX2VTXPGA */
{ 0x19 , 0x32 } , /* ARXL1_APGA_CTL*/
{ 0x1a , 0x32 } , /* ARXR1_APGA_CTL*/
{ 0x1b , 0x32 } , /* ARXL2_APGA_CTL*/
{ 0x1c , 0x32 } , /* ARXR2_APGA_CTL*/
{ 0x1d , 0x00 } , /* ATX2ARXPGA */
{ 0x1e , 0x00 } , /* BT_IF */
{ 0x1f , 0x55 } , /* BTPGA */
{ 0x20 , 0x00 } , /* BTSTPGA */
{ 0x21 , 0x00 } , /* EAR_CTL */
{ 0x22 , 0x00 } , /* HS_SEL */
{ 0x23 , 0x00 } , /* HS_GAIN_SET */
{ 0x24 , 0x00 } , /* HS_POPN_SET */
{ 0x25 , 0x00 } , /* PREDL_CTL */
{ 0x26 , 0x00 } , /* PREDR_CTL */
{ 0x27 , 0x00 } , /* PRECKL_CTL */
{ 0x28 , 0x00 } , /* PRECKR_CTL */
{ 0x29 , 0x00 } , /* HFL_CTL */
{ 0x2a , 0x00 } , /* HFR_CTL */
{ 0x2b , 0x05 } , /* ALC_CTL */
{ 0x2c , 0x00 } , /* ALC_SET1 */
{ 0x2d , 0x00 } , /* ALC_SET2 */
{ 0x2e , 0x00 } , /* BOOST_CTL */
{ 0x2f , 0x00 } , /* SOFTVOL_CTL */
{ 0x30 , 0x13 } , /* DTMF_FREQSEL */
{ 0x31 , 0x00 } , /* DTMF_TONEXT1H */
{ 0x32 , 0x00 } , /* DTMF_TONEXT1L */
{ 0x33 , 0x00 } , /* DTMF_TONEXT2H */
{ 0x34 , 0x00 } , /* DTMF_TONEXT2L */
{ 0x35 , 0x79 } , /* DTMF_TONOFF */
{ 0x36 , 0x11 } , /* DTMF_WANONOFF */
{ 0x37 , 0x00 } , /* I2S_RX_SCRAMBLE_H */
{ 0x38 , 0x00 } , /* I2S_RX_SCRAMBLE_M */
{ 0x39 , 0x00 } , /* I2S_RX_SCRAMBLE_L */
{ 0x3a , 0x06 } , /* APLL_CTL */
{ 0x3b , 0x00 } , /* DTMF_CTL */
{ 0x3c , 0x44 } , /* DTMF_PGA_CTL2 (0x3C) */
{ 0x3d , 0x69 } , /* DTMF_PGA_CTL1 (0x3D) */
{ 0x3e , 0x00 } , /* MISC_SET_1 */
{ 0x3f , 0x00 } , /* PCMBTMUX */
/* 0x40 - 0x42 Unused */
{ 0x43 , 0x00 } , /* RX_PATH_SEL */
{ 0x44 , 0x32 } , /* VDL_APGA_CTL */
{ 0x45 , 0x00 } , /* VIBRA_CTL */
{ 0x46 , 0x00 } , /* VIBRA_SET */
{ 0x47 , 0x00 } , /* VIBRA_PWM_SET */
{ 0x48 , 0x00 } , /* ANAMIC_GAIN */
{ 0x49 , 0x00 } , /* MISC_SET_2 */
/* End of Audio Registers */
} ;
static bool twl4030_49_nop_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
2014-03-10 22:12:50 +04:00
case 0x00 :
case 0x03 :
case 0x40 :
case 0x41 :
case 0x42 :
2014-01-03 17:27:48 +04:00
return false ;
default :
return true ;
}
}
static const struct regmap_range twl4030_49_volatile_ranges [ ] = {
regmap_reg_range ( TWL4030_BASEADD_TEST , 0xff ) ,
} ;
static const struct regmap_access_table twl4030_49_volatile_table = {
. yes_ranges = twl4030_49_volatile_ranges ,
. n_yes_ranges = ARRAY_SIZE ( twl4030_49_volatile_ranges ) ,
} ;
2015-01-05 12:01:32 +03:00
static const struct regmap_config twl4030_regmap_config [ 4 ] = {
2012-11-13 12:28:44 +04:00
{
/* Address 0x48 */
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0xff ,
} ,
{
/* Address 0x49 */
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0xff ,
2014-01-03 17:27:48 +04:00
. readable_reg = twl4030_49_nop_reg ,
. writeable_reg = twl4030_49_nop_reg ,
. volatile_table = & twl4030_49_volatile_table ,
. reg_defaults = twl4030_49_defaults ,
. num_reg_defaults = ARRAY_SIZE ( twl4030_49_defaults ) ,
. cache_type = REGCACHE_RBTREE ,
2012-11-13 12:28:44 +04:00
} ,
{
/* Address 0x4a */
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0xff ,
} ,
{
/* Address 0x4b */
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0xff ,
} ,
} ;
2009-12-14 02:25:31 +03:00
static struct twl_mapping twl6030_map [ ] = {
/*
* NOTE : don ' t change this table without updating the
2017-08-14 19:34:24 +03:00
* < linux / mfd / twl . h > defines for TWL4030_MODULE_ *
2009-12-14 02:25:31 +03:00
* so they continue to match the order in this table .
*/
2013-01-16 17:53:50 +04:00
/* Common IPs */
{ 1 , TWL6030_BASEADD_USB } ,
{ 1 , TWL6030_BASEADD_PIH } ,
{ 1 , TWL6030_BASEADD_CHARGER } ,
{ 0 , TWL6030_BASEADD_PM_MASTER } ,
{ 0 , TWL6030_BASEADD_PM_SLAVE_MISC } ,
{ 0 , TWL6030_BASEADD_RTC } ,
{ 1 , TWL6030_BASEADD_PWM } ,
{ 1 , TWL6030_BASEADD_LED } ,
{ 0 , TWL6030_BASEADD_SECURED_REG } ,
/* TWL6030 specific IPs */
{ 0 , TWL6030_BASEADD_ZERO } ,
{ 1 , TWL6030_BASEADD_ZERO } ,
{ 2 , TWL6030_BASEADD_ZERO } ,
{ 1 , TWL6030_BASEADD_GPADC_CTRL } ,
{ 1 , TWL6030_BASEADD_GASGAUGE } ,
2009-12-14 02:25:31 +03:00
} ;
2015-01-05 12:01:32 +03:00
static const struct regmap_config twl6030_regmap_config [ 3 ] = {
2012-11-13 12:28:44 +04:00
{
/* Address 0x48 */
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0xff ,
} ,
{
/* Address 0x49 */
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0xff ,
} ,
{
/* Address 0x4a */
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0xff ,
} ,
} ;
2008-10-15 14:15:39 +04:00
/*----------------------------------------------------------------------*/
2013-01-16 17:53:53 +04:00
static inline int twl_get_num_slaves ( void )
{
if ( twl_class_is_4030 ( ) )
return 4 ; /* TWL4030 class have four slave address */
else
return 3 ; /* TWL6030 class have three slave address */
}
2013-01-16 17:53:50 +04:00
static inline int twl_get_last_module ( void )
{
if ( twl_class_is_4030 ( ) )
return TWL4030_MODULE_LAST ;
else
return TWL6030_MODULE_LAST ;
}
2008-10-15 14:15:39 +04:00
/* Exported Functions */
2013-01-16 17:53:57 +04:00
unsigned int twl_rev ( void )
{
return twl_priv ? twl_priv - > twl_id : 0 ;
}
EXPORT_SYMBOL ( twl_rev ) ;
2008-10-15 14:15:39 +04:00
/**
2014-01-03 17:27:46 +04:00
* twl_get_regmap - Get the regmap associated with the given module
2008-10-15 14:15:39 +04:00
* @ mod_no : module number
*
2014-01-03 17:27:46 +04:00
* Returns the regmap pointer or NULL in case of failure .
2008-10-15 14:15:39 +04:00
*/
2014-01-03 17:27:46 +04:00
static struct regmap * twl_get_regmap ( u8 mod_no )
2008-10-15 14:15:39 +04:00
{
int sid ;
2009-12-13 23:23:33 +03:00
struct twl_client * twl ;
2008-10-15 14:15:39 +04:00
2013-02-12 00:26:19 +04:00
if ( unlikely ( ! twl_priv | | ! twl_priv - > ready ) ) {
pr_err ( " %s: not initialized \n " , DRIVER_NAME ) ;
2014-01-03 17:27:46 +04:00
return NULL ;
2008-10-15 14:15:39 +04:00
}
2013-02-12 00:26:19 +04:00
if ( unlikely ( mod_no > = twl_get_last_module ( ) ) ) {
pr_err ( " %s: invalid module number %d \n " , DRIVER_NAME , mod_no ) ;
2014-01-03 17:27:46 +04:00
return NULL ;
2008-10-15 14:15:39 +04:00
}
2013-01-16 17:53:51 +04:00
2013-01-16 17:53:57 +04:00
sid = twl_priv - > twl_map [ mod_no ] . sid ;
twl = & twl_priv - > twl_modules [ sid ] ;
2011-08-02 01:00:28 +04:00
2014-01-03 17:27:46 +04:00
return twl - > regmap ;
}
/**
* twl_i2c_write - Writes a n bit register in TWL4030 / TWL5030 / TWL60X0
* @ mod_no : module number
* @ value : an array of num_bytes + 1 containing data to write
* @ reg : register address ( just offset will do )
* @ num_bytes : number of bytes to transfer
*
2017-06-30 11:02:21 +03:00
* Returns 0 on success or else a negative error code .
2014-01-03 17:27:46 +04:00
*/
int twl_i2c_write ( u8 mod_no , u8 * value , u8 reg , unsigned num_bytes )
{
struct regmap * regmap = twl_get_regmap ( mod_no ) ;
int ret ;
if ( ! regmap )
return - EPERM ;
ret = regmap_bulk_write ( regmap , twl_priv - > twl_map [ mod_no ] . base + reg ,
value , num_bytes ) ;
2012-11-13 12:28:44 +04:00
if ( ret )
pr_err ( " %s: Write failed (mod %d, reg 0x%02x count %d) \n " ,
DRIVER_NAME , mod_no , reg , num_bytes ) ;
return ret ;
2008-10-15 14:15:39 +04:00
}
2009-12-13 23:23:33 +03:00
EXPORT_SYMBOL ( twl_i2c_write ) ;
2008-10-15 14:15:39 +04:00
/**
2009-12-13 23:23:33 +03:00
* twl_i2c_read - Reads a n bit register in TWL4030 / TWL5030 / TWL60X0
2008-10-15 14:15:39 +04:00
* @ mod_no : module number
* @ value : an array of num_bytes containing data to be read
* @ reg : register address ( just offset will do )
* @ num_bytes : number of bytes to transfer
*
2017-06-30 11:02:21 +03:00
* Returns 0 on success or else a negative error code .
2008-10-15 14:15:39 +04:00
*/
2009-12-13 23:23:33 +03:00
int twl_i2c_read ( u8 mod_no , u8 * value , u8 reg , unsigned num_bytes )
2008-10-15 14:15:39 +04:00
{
2014-01-03 17:27:46 +04:00
struct regmap * regmap = twl_get_regmap ( mod_no ) ;
2008-10-15 14:15:39 +04:00
int ret ;
2014-01-03 17:27:46 +04:00
if ( ! regmap )
2008-10-15 14:15:39 +04:00
return - EPERM ;
2011-08-02 01:00:28 +04:00
2014-01-03 17:27:46 +04:00
ret = regmap_bulk_read ( regmap , twl_priv - > twl_map [ mod_no ] . base + reg ,
value , num_bytes ) ;
2012-11-13 12:28:44 +04:00
if ( ret )
pr_err ( " %s: Read failed (mod %d, reg 0x%02x count %d) \n " ,
DRIVER_NAME , mod_no , reg , num_bytes ) ;
return ret ;
2008-10-15 14:15:39 +04:00
}
2009-12-13 23:23:33 +03:00
EXPORT_SYMBOL ( twl_i2c_read ) ;
2008-10-15 14:15:39 +04:00
2014-01-03 17:27:47 +04:00
/**
* twl_regcache_bypass - Configure the regcache bypass for the regmap associated
* with the module
* @ mod_no : module number
* @ enable : Regcache bypass state
*
* Returns 0 else failure .
*/
int twl_set_regcache_bypass ( u8 mod_no , bool enable )
{
struct regmap * regmap = twl_get_regmap ( mod_no ) ;
if ( ! regmap )
return - EPERM ;
regcache_cache_bypass ( regmap , enable ) ;
return 0 ;
}
EXPORT_SYMBOL ( twl_set_regcache_bypass ) ;
2008-10-15 14:15:39 +04:00
/*----------------------------------------------------------------------*/
2011-04-14 16:27:53 +04:00
/**
* twl_read_idcode_register - API to read the IDCODE register .
*
* Unlocks the IDCODE register and read the 32 bit value .
*/
static int twl_read_idcode_register ( void )
{
int err ;
err = twl_i2c_write_u8 ( TWL4030_MODULE_INTBR , TWL_EEPROM_R_UNLOCK ,
REG_UNLOCK_TEST_REG ) ;
if ( err ) {
pr_err ( " TWL4030 Unable to unlock IDCODE registers -%d \n " , err ) ;
goto fail ;
}
2013-01-16 17:53:57 +04:00
err = twl_i2c_read ( TWL4030_MODULE_INTBR , ( u8 * ) ( & twl_priv - > twl_idcode ) ,
2011-04-14 16:27:53 +04:00
REG_IDCODE_7_0 , 4 ) ;
if ( err ) {
pr_err ( " TWL4030: unable to read IDCODE -%d \n " , err ) ;
goto fail ;
}
err = twl_i2c_write_u8 ( TWL4030_MODULE_INTBR , 0x0 , REG_UNLOCK_TEST_REG ) ;
if ( err )
pr_err ( " TWL4030 Unable to relock IDCODE registers -%d \n " , err ) ;
fail :
return err ;
}
/**
* twl_get_type - API to get TWL Si type .
*
* Api to get the TWL Si type from IDCODE value .
*/
int twl_get_type ( void )
{
2013-01-16 17:53:57 +04:00
return TWL_SIL_TYPE ( twl_priv - > twl_idcode ) ;
2011-04-14 16:27:53 +04:00
}
EXPORT_SYMBOL_GPL ( twl_get_type ) ;
/**
* twl_get_version - API to get TWL Si version .
*
* Api to get the TWL Si version from IDCODE value .
*/
int twl_get_version ( void )
{
2013-01-16 17:53:57 +04:00
return TWL_SIL_REV ( twl_priv - > twl_idcode ) ;
2011-04-14 16:27:53 +04:00
}
EXPORT_SYMBOL_GPL ( twl_get_version ) ;
2012-09-10 14:46:22 +04:00
/**
* twl_get_hfclk_rate - API to get TWL external HFCLK clock rate .
*
* Api to get the TWL HFCLK rate based on BOOT_CFG register .
*/
int twl_get_hfclk_rate ( void )
{
u8 ctrl ;
int rate ;
twl_i2c_read_u8 ( TWL_MODULE_PM_MASTER , & ctrl , R_CFG_BOOT ) ;
switch ( ctrl & 0x3 ) {
case HFCLK_FREQ_19p2_MHZ :
rate = 19200000 ;
break ;
case HFCLK_FREQ_26_MHZ :
rate = 26000000 ;
break ;
case HFCLK_FREQ_38p4_MHZ :
rate = 38400000 ;
break ;
default :
pr_err ( " TWL4030: HFCLK is not configured \n " ) ;
rate = - EINVAL ;
break ;
}
return rate ;
}
EXPORT_SYMBOL_GPL ( twl_get_hfclk_rate ) ;
2008-12-01 02:43:58 +03:00
static struct device *
2013-01-16 17:53:52 +04:00
add_numbered_child ( unsigned mod_no , const char * name , int num ,
2008-12-01 02:31:04 +03:00
void * pdata , unsigned pdata_len ,
bool can_wakeup , int irq0 , int irq1 )
2008-10-15 14:15:39 +04:00
{
2008-12-01 02:31:04 +03:00
struct platform_device * pdev ;
2013-01-16 17:53:52 +04:00
struct twl_client * twl ;
int status , sid ;
if ( unlikely ( mod_no > = twl_get_last_module ( ) ) ) {
pr_err ( " %s: invalid module number %d \n " , DRIVER_NAME , mod_no ) ;
return ERR_PTR ( - EPERM ) ;
}
2013-01-16 17:53:57 +04:00
sid = twl_priv - > twl_map [ mod_no ] . sid ;
twl = & twl_priv - > twl_modules [ sid ] ;
2008-12-01 02:31:04 +03:00
2008-12-01 02:43:58 +03:00
pdev = platform_device_alloc ( name , num ) ;
2016-06-26 13:25:36 +03:00
if ( ! pdev )
return ERR_PTR ( - ENOMEM ) ;
2008-10-15 14:15:39 +04:00
2008-12-01 02:31:04 +03:00
pdev - > dev . parent = & twl - > client - > dev ;
2008-10-15 14:15:39 +04:00
2008-12-01 02:31:04 +03:00
if ( pdata ) {
status = platform_device_add_data ( pdev , pdata , pdata_len ) ;
if ( status < 0 ) {
dev_dbg ( & pdev - > dev , " can't add platform_data \n " ) ;
2016-06-26 14:03:59 +03:00
goto put_device ;
2008-10-15 14:15:39 +04:00
}
2008-12-01 02:31:04 +03:00
}
2008-10-15 14:15:39 +04:00
2008-12-01 02:31:04 +03:00
if ( irq0 ) {
struct resource r [ 2 ] = {
{ . start = irq0 , . flags = IORESOURCE_IRQ , } ,
{ . start = irq1 , . flags = IORESOURCE_IRQ , } ,
} ;
2008-10-15 14:15:39 +04:00
2008-12-01 02:31:04 +03:00
status = platform_device_add_resources ( pdev , r , irq1 ? 2 : 1 ) ;
2008-10-15 14:15:39 +04:00
if ( status < 0 ) {
2008-12-01 02:31:04 +03:00
dev_dbg ( & pdev - > dev , " can't add irqs \n " ) ;
2016-06-26 14:03:59 +03:00
goto put_device ;
2008-10-15 14:15:39 +04:00
}
}
2008-12-01 02:31:04 +03:00
status = platform_device_add ( pdev ) ;
2016-06-26 14:03:59 +03:00
if ( status )
goto put_device ;
device_init_wakeup ( & pdev - > dev , can_wakeup ) ;
2008-12-01 02:31:04 +03:00
return & pdev - > dev ;
2016-06-26 14:03:59 +03:00
put_device :
platform_device_put ( pdev ) ;
dev_err ( & twl - > client - > dev , " failed to add device %s \n " , name ) ;
return ERR_PTR ( status ) ;
2008-12-01 02:31:04 +03:00
}
2008-10-15 14:15:39 +04:00
2013-01-16 17:53:52 +04:00
static inline struct device * add_child ( unsigned mod_no , const char * name ,
2008-12-01 02:43:58 +03:00
void * pdata , unsigned pdata_len ,
bool can_wakeup , int irq0 , int irq1 )
{
2013-01-16 17:53:52 +04:00
return add_numbered_child ( mod_no , name , - 1 , pdata , pdata_len ,
2008-12-01 02:43:58 +03:00
can_wakeup , irq0 , irq1 ) ;
}
static struct device *
add_regulator_linked ( int num , struct regulator_init_data * pdata ,
struct regulator_consumer_supply * consumers ,
2011-05-12 17:27:55 +04:00
unsigned num_consumers , unsigned long features )
2008-12-01 02:43:58 +03:00
{
2012-02-16 14:27:52 +04:00
struct twl_regulator_driver_data drv_data ;
2008-12-01 02:43:58 +03:00
/* regulator framework demands init_data ... */
if ( ! pdata )
return NULL ;
2008-12-07 21:10:58 +03:00
if ( consumers ) {
2008-12-01 02:43:58 +03:00
pdata - > consumer_supplies = consumers ;
pdata - > num_consumer_supplies = num_consumers ;
}
2012-02-16 14:27:52 +04:00
if ( pdata - > driver_data ) {
/* If we have existing drv_data, just add the flags */
struct twl_regulator_driver_data * tmp ;
tmp = pdata - > driver_data ;
tmp - > features | = features ;
} else {
/* add new driver data struct, used only during init */
drv_data . features = features ;
drv_data . set_voltage = NULL ;
drv_data . get_voltage = NULL ;
drv_data . data = NULL ;
pdata - > driver_data = & drv_data ;
}
2011-05-12 17:27:55 +04:00
2008-12-01 02:43:58 +03:00
/* NOTE: we currently ignore regulator IRQs, e.g. for short circuits */
2013-01-16 17:53:52 +04:00
return add_numbered_child ( TWL_MODULE_PM_MASTER , " twl_reg " , num ,
2008-12-01 02:43:58 +03:00
pdata , sizeof ( * pdata ) , false , 0 , 0 ) ;
}
static struct device *
2011-05-12 17:27:55 +04:00
add_regulator ( int num , struct regulator_init_data * pdata ,
unsigned long features )
2008-12-01 02:43:58 +03:00
{
2011-05-12 17:27:55 +04:00
return add_regulator_linked ( num , pdata , NULL , 0 , features ) ;
2008-12-01 02:43:58 +03:00
}
2008-12-01 02:31:04 +03:00
/*
* NOTE : We know the first 8 IRQs after pdata - > base_irq are
* for the PIH , and the next are for the PWR_INT SIH , since
* that ' s how twl_init_irq ( ) sets things up .
*/
2008-10-15 14:15:39 +04:00
2008-12-01 02:43:58 +03:00
static int
2012-02-22 16:32:16 +04:00
add_children ( struct twl4030_platform_data * pdata , unsigned irq_base ,
unsigned long features )
2008-12-01 02:31:04 +03:00
{
struct device * child ;
2008-10-15 14:15:39 +04:00
2012-09-18 12:29:50 +04:00
if ( IS_ENABLED ( CONFIG_GPIO_TWL4030 ) & & pdata - > gpio ) {
2013-01-16 17:53:52 +04:00
child = add_child ( TWL4030_MODULE_GPIO , " twl4030_gpio " ,
2008-12-01 02:31:04 +03:00
pdata - > gpio , sizeof ( * pdata - > gpio ) ,
2012-02-22 16:32:16 +04:00
false , irq_base + GPIO_INTR_OFFSET , 0 ) ;
2008-12-01 02:31:04 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2008-10-15 14:15:39 +04:00
}
2012-09-18 12:29:50 +04:00
if ( IS_ENABLED ( CONFIG_KEYBOARD_TWL4030 ) & & pdata - > keypad ) {
2013-01-16 17:53:52 +04:00
child = add_child ( TWL4030_MODULE_KEYPAD , " twl4030_keypad " ,
2008-12-01 02:31:04 +03:00
pdata - > keypad , sizeof ( * pdata - > keypad ) ,
2012-02-22 16:32:16 +04:00
true , irq_base + KEYPAD_INTR_OFFSET , 0 ) ;
2008-12-01 02:31:04 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2008-10-15 14:15:39 +04:00
}
2012-11-13 12:28:42 +04:00
if ( IS_ENABLED ( CONFIG_TWL4030_MADC ) & & pdata - > madc & &
twl_class_is_4030 ( ) ) {
2013-01-16 17:53:52 +04:00
child = add_child ( TWL4030_MODULE_MADC , " twl4030_madc " ,
2008-12-01 02:31:04 +03:00
pdata - > madc , sizeof ( * pdata - > madc ) ,
2012-02-22 16:32:16 +04:00
true , irq_base + MADC_INTR_OFFSET , 0 ) ;
2008-12-01 02:31:04 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2008-10-15 14:15:39 +04:00
}
2012-09-18 12:29:50 +04:00
if ( IS_ENABLED ( CONFIG_RTC_DRV_TWL4030 ) ) {
2008-10-15 14:15:39 +04:00
/*
2008-12-01 02:31:04 +03:00
* REVISIT platform_data here currently might expose the
2008-10-15 14:15:39 +04:00
* " msecure " line . . . but for now we just expect board
2008-12-01 02:31:04 +03:00
* setup to tell the chip " it's always ok to SET_TIME " .
2008-10-15 14:15:39 +04:00
* Eventually , Linux might become more aware of such
* HW security concerns , and " least privilege " .
*/
2013-01-16 17:53:52 +04:00
child = add_child ( TWL_MODULE_RTC , " twl_rtc " , NULL , 0 ,
2012-02-22 16:32:16 +04:00
true , irq_base + RTC_INTR_OFFSET , 0 ) ;
2008-12-01 02:31:04 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2008-10-15 14:15:39 +04:00
}
2012-11-13 12:28:43 +04:00
if ( IS_ENABLED ( CONFIG_PWM_TWL ) ) {
2013-01-16 17:53:52 +04:00
child = add_child ( TWL_MODULE_PWM , " twl-pwm " , NULL , 0 ,
2012-11-13 12:28:43 +04:00
false , 0 , 0 ) ;
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
}
if ( IS_ENABLED ( CONFIG_PWM_TWL_LED ) ) {
2013-01-16 17:53:52 +04:00
child = add_child ( TWL_MODULE_LED , " twl-pwmled " , NULL , 0 ,
2012-09-18 12:29:51 +04:00
false , 0 , 0 ) ;
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
}
2012-09-18 12:29:50 +04:00
if ( IS_ENABLED ( CONFIG_TWL4030_USB ) & & pdata - > usb & &
twl_class_is_4030 ( ) ) {
2009-09-30 19:26:37 +04:00
static struct regulator_consumer_supply usb1v5 = {
. supply = " usb1v5 " ,
} ;
static struct regulator_consumer_supply usb1v8 = {
. supply = " usb1v8 " ,
} ;
2015-07-30 03:11:24 +03:00
static struct regulator_consumer_supply usb3v1 = {
. supply = " usb3v1 " ,
2009-09-30 19:26:37 +04:00
} ;
/* First add the regulators so that they can be used by transceiver */
2012-09-18 12:29:50 +04:00
if ( IS_ENABLED ( CONFIG_REGULATOR_TWL4030 ) ) {
2009-09-30 19:26:37 +04:00
/* this is a template that gets copied */
struct regulator_init_data usb_fixed = {
. constraints . valid_modes_mask =
REGULATOR_MODE_NORMAL
| REGULATOR_MODE_STANDBY ,
. constraints . valid_ops_mask =
REGULATOR_CHANGE_MODE
| REGULATOR_CHANGE_STATUS ,
} ;
child = add_regulator_linked ( TWL4030_REG_VUSB1V5 ,
2011-05-12 17:27:55 +04:00
& usb_fixed , & usb1v5 , 1 ,
features ) ;
2009-09-30 19:26:37 +04:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
child = add_regulator_linked ( TWL4030_REG_VUSB1V8 ,
2011-05-12 17:27:55 +04:00
& usb_fixed , & usb1v8 , 1 ,
features ) ;
2009-09-30 19:26:37 +04:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
child = add_regulator_linked ( TWL4030_REG_VUSB3V1 ,
2015-07-30 03:11:24 +03:00
& usb_fixed , & usb3v1 , 1 ,
2011-05-12 17:27:55 +04:00
features ) ;
2009-09-30 19:26:37 +04:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
}
2013-01-16 17:53:52 +04:00
child = add_child ( TWL_MODULE_USB , " twl4030_usb " ,
2012-11-13 12:28:46 +04:00
pdata - > usb , sizeof ( * pdata - > usb ) , true ,
2008-12-01 02:31:04 +03:00
/* irq0 = USB_PRES, irq1 = USB */
2012-02-22 16:32:16 +04:00
irq_base + USB_PRES_INTR_OFFSET ,
irq_base + USB_INTR_OFFSET ) ;
2009-09-30 19:26:37 +04:00
2008-12-01 02:31:04 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2008-12-01 02:43:58 +03:00
/* we need to connect regulators to this transceiver */
2012-09-18 12:29:50 +04:00
if ( IS_ENABLED ( CONFIG_REGULATOR_TWL4030 ) & & child ) {
2012-02-03 15:02:58 +04:00
usb1v5 . dev_name = dev_name ( child ) ;
usb1v8 . dev_name = dev_name ( child ) ;
2015-07-30 03:11:24 +03:00
usb3v1 . dev_name = dev_name ( child ) ;
2009-09-30 19:26:37 +04:00
}
2008-12-01 02:43:58 +03:00
}
2012-09-18 12:29:50 +04:00
if ( IS_ENABLED ( CONFIG_TWL4030_WATCHDOG ) & & twl_class_is_4030 ( ) ) {
2013-01-16 17:53:52 +04:00
child = add_child ( TWL_MODULE_PM_RECEIVER , " twl4030_wdt " , NULL ,
0 , false , 0 , 0 ) ;
2009-03-27 17:42:17 +03:00
if ( IS_ERR ( child ) )
2009-08-03 20:16:38 +04:00
return PTR_ERR ( child ) ;
}
2012-09-18 12:29:50 +04:00
if ( IS_ENABLED ( CONFIG_INPUT_TWL4030_PWRBUTTON ) & & twl_class_is_4030 ( ) ) {
2013-01-16 17:53:52 +04:00
child = add_child ( TWL_MODULE_PM_MASTER , " twl4030_pwrbutton " ,
NULL , 0 , true , irq_base + 8 + 0 , 0 ) ;
2009-08-03 20:16:38 +04:00
if ( IS_ERR ( child ) )
2009-03-27 17:42:17 +03:00
return PTR_ERR ( child ) ;
}
2012-09-18 12:29:50 +04:00
if ( IS_ENABLED ( CONFIG_MFD_TWL4030_AUDIO ) & & pdata - > audio & &
twl_class_is_4030 ( ) ) {
2013-01-16 17:53:52 +04:00
child = add_child ( TWL4030_MODULE_AUDIO_VOICE , " twl4030-audio " ,
2011-05-31 16:21:13 +04:00
pdata - > audio , sizeof ( * pdata - > audio ) ,
2010-02-24 03:10:19 +03:00
false , 0 , 0 ) ;
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
}
2009-12-14 00:29:47 +03:00
/* twl4030 regulators */
2012-09-18 12:29:50 +04:00
if ( IS_ENABLED ( CONFIG_REGULATOR_TWL4030 ) & & twl_class_is_4030 ( ) ) {
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL4030_REG_VPLL1 , pdata - > vpll1 ,
features ) ;
2008-12-01 02:43:58 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2009-12-11 13:12:15 +03:00
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL4030_REG_VIO , pdata - > vio ,
features ) ;
2009-12-11 13:12:15 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL4030_REG_VDD1 , pdata - > vdd1 ,
features ) ;
2009-12-11 13:12:15 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL4030_REG_VDD2 , pdata - > vdd2 ,
features ) ;
2009-12-11 13:12:15 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2008-12-01 02:43:58 +03:00
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL4030_REG_VMMC1 , pdata - > vmmc1 ,
features ) ;
2008-12-01 02:43:58 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL4030_REG_VDAC , pdata - > vdac ,
features ) ;
2008-12-01 02:43:58 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
child = add_regulator ( ( features & TWL4030_VAUX2 )
? TWL4030_REG_VAUX2_4030
: TWL4030_REG_VAUX2 ,
2011-05-12 17:27:55 +04:00
pdata - > vaux2 , features ) ;
2008-12-01 02:43:58 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2009-12-11 13:12:15 +03:00
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL4030_REG_VINTANA1 , pdata - > vintana1 ,
features ) ;
2009-12-11 13:12:15 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL4030_REG_VINTANA2 , pdata - > vintana2 ,
features ) ;
2009-12-11 13:12:15 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL4030_REG_VINTDIG , pdata - > vintdig ,
features ) ;
2009-12-11 13:12:15 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2008-12-01 02:43:58 +03:00
}
/* maybe add LDOs that are omitted on cost-reduced parts */
2012-09-18 12:29:50 +04:00
if ( IS_ENABLED ( CONFIG_REGULATOR_TWL4030 ) & & ! ( features & TPS_SUBSET )
2009-12-14 00:29:47 +03:00
& & twl_class_is_4030 ( ) ) {
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL4030_REG_VPLL2 , pdata - > vpll2 ,
features ) ;
2008-12-01 02:43:58 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL4030_REG_VMMC2 , pdata - > vmmc2 ,
features ) ;
2008-12-01 02:43:58 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL4030_REG_VSIM , pdata - > vsim ,
features ) ;
2008-12-01 02:43:58 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL4030_REG_VAUX1 , pdata - > vaux1 ,
features ) ;
2008-12-01 02:43:58 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL4030_REG_VAUX3 , pdata - > vaux3 ,
features ) ;
2008-12-01 02:43:58 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL4030_REG_VAUX4 , pdata - > vaux4 ,
features ) ;
2008-12-01 02:43:58 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2008-10-15 14:15:39 +04:00
}
2012-09-18 12:29:50 +04:00
if ( IS_ENABLED ( CONFIG_CHARGER_TWL4030 ) & & pdata - > bci & &
2010-10-01 14:17:50 +04:00
! ( features & ( TPS_SUBSET | TWL5031 ) ) ) {
2013-01-16 17:53:52 +04:00
child = add_child ( TWL_MODULE_MAIN_CHARGE , " twl4030_bci " ,
2010-10-01 14:17:50 +04:00
pdata - > bci , sizeof ( * pdata - > bci ) , false ,
/* irq0 = CHG_PRES, irq1 = BCI */
2012-02-22 16:32:16 +04:00
irq_base + BCI_PRES_INTR_OFFSET ,
irq_base + BCI_INTR_OFFSET ) ;
2010-10-01 14:17:50 +04:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
}
2013-06-18 17:17:56 +04:00
if ( IS_ENABLED ( CONFIG_TWL4030_POWER ) & & pdata - > power ) {
child = add_child ( TWL_MODULE_PM_MASTER , " twl4030_power " ,
pdata - > power , sizeof ( * pdata - > power ) , false ,
0 , 0 ) ;
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
}
2008-12-01 02:31:04 +03:00
return 0 ;
2008-10-15 14:15:39 +04:00
}
/*----------------------------------------------------------------------*/
/*
* These three functions initialize the on - chip clock framework ,
* letting it generate the right frequencies for USB , MADC , and
* other purposes .
*/
2018-10-17 20:13:23 +03:00
static inline int protect_pm_master ( void )
2008-10-15 14:15:39 +04:00
{
int e = 0 ;
2012-11-13 12:28:54 +04:00
e = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , 0 ,
TWL4030_PM_MASTER_PROTECT_KEY ) ;
2008-10-15 14:15:39 +04:00
return e ;
}
2018-10-17 20:13:23 +03:00
static inline int unprotect_pm_master ( void )
2008-10-15 14:15:39 +04:00
{
int e = 0 ;
2012-11-13 12:28:54 +04:00
e | = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , TWL4030_PM_MASTER_KEY_CFG1 ,
TWL4030_PM_MASTER_PROTECT_KEY ) ;
e | = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , TWL4030_PM_MASTER_KEY_CFG2 ,
TWL4030_PM_MASTER_PROTECT_KEY ) ;
2010-08-18 10:19:35 +04:00
2008-10-15 14:15:39 +04:00
return e ;
}
2009-10-22 15:14:09 +04:00
static void clocks_init ( struct device * dev ,
struct twl4030_clock_init_data * clock )
2008-10-15 14:15:39 +04:00
{
int e = 0 ;
struct clk * osc ;
u32 rate ;
u8 ctrl = HFCLK_FREQ_26_MHZ ;
2012-09-18 03:26:10 +04:00
osc = clk_get ( dev , " fck " ) ;
2008-10-15 14:15:39 +04:00
if ( IS_ERR ( osc ) ) {
2009-12-13 23:23:33 +03:00
printk ( KERN_WARNING " Skipping twl internal clock init and "
2008-10-15 14:15:39 +04:00
" using bootloader value (unknown osc rate) \n " ) ;
return ;
}
rate = clk_get_rate ( osc ) ;
clk_put ( osc ) ;
switch ( rate ) {
case 19200000 :
ctrl = HFCLK_FREQ_19p2_MHZ ;
break ;
case 26000000 :
ctrl = HFCLK_FREQ_26_MHZ ;
break ;
case 38400000 :
ctrl = HFCLK_FREQ_38p4_MHZ ;
break ;
}
ctrl | = HIGH_PERF_SQ ;
2009-10-22 15:14:09 +04:00
if ( clock & & clock - > ck32k_lowpwr_enable )
ctrl | = CK32K_LOWPWR_EN ;
2008-10-15 14:15:39 +04:00
e | = unprotect_pm_master ( ) ;
/* effect->MADC+USB ck en */
2009-12-13 23:23:33 +03:00
e | = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , ctrl , R_CFG_BOOT ) ;
2008-10-15 14:15:39 +04:00
e | = protect_pm_master ( ) ;
if ( e < 0 )
pr_err ( " %s: clock init err [%d] \n " , DRIVER_NAME , e ) ;
}
/*----------------------------------------------------------------------*/
2009-12-13 23:23:33 +03:00
static int twl_remove ( struct i2c_client * client )
2008-10-15 14:15:39 +04:00
{
2012-01-04 16:58:33 +04:00
unsigned i , num_slaves ;
2008-10-21 01:46:28 +04:00
int status ;
2008-10-15 14:15:39 +04:00
2013-01-16 17:53:53 +04:00
if ( twl_class_is_4030 ( ) )
2009-12-14 02:25:31 +03:00
status = twl4030_exit_irq ( ) ;
2013-01-16 17:53:53 +04:00
else
2009-12-14 02:25:31 +03:00
status = twl6030_exit_irq ( ) ;
2008-10-21 01:46:28 +04:00
if ( status < 0 )
return status ;
2008-10-15 14:15:39 +04:00
2013-01-16 17:53:53 +04:00
num_slaves = twl_get_num_slaves ( ) ;
2012-01-04 16:58:33 +04:00
for ( i = 0 ; i < num_slaves ; i + + ) {
2013-01-16 17:53:57 +04:00
struct twl_client * twl = & twl_priv - > twl_modules [ i ] ;
2008-10-15 14:15:39 +04:00
if ( twl - > client & & twl - > client ! = client )
i2c_unregister_device ( twl - > client ) ;
2013-01-16 17:53:57 +04:00
twl - > client = NULL ;
2008-10-15 14:15:39 +04:00
}
2013-01-16 17:53:57 +04:00
twl_priv - > ready = false ;
2008-10-15 14:15:39 +04:00
return 0 ;
}
2013-11-16 04:36:03 +04:00
static struct of_dev_auxdata twl_auxdata_lookup [ ] = {
OF_DEV_AUXDATA ( " ti,twl4030-gpio " , 0 , " twl4030-gpio " , NULL ) ,
{ /* sentinel */ } ,
} ;
2012-03-02 14:11:26 +04:00
/* NOTE: This driver only handles a single twl4030/tps659x0 chip */
2012-11-19 22:23:04 +04:00
static int
2009-12-13 23:23:33 +03:00
twl_probe ( struct i2c_client * client , const struct i2c_device_id * id )
2008-10-15 14:15:39 +04:00
{
2013-07-30 12:10:05 +04:00
struct twl4030_platform_data * pdata = dev_get_platdata ( & client - > dev ) ;
2011-08-29 18:20:23 +04:00
struct device_node * node = client - > dev . of_node ;
2012-09-18 03:26:10 +04:00
struct platform_device * pdev ;
2015-01-05 12:01:32 +03:00
const struct regmap_config * twl_regmap_config ;
2012-03-02 14:11:26 +04:00
int irq_base = 0 ;
int status ;
2012-01-04 16:58:33 +04:00
unsigned i , num_slaves ;
2011-08-29 18:20:23 +04:00
2013-01-16 17:53:55 +04:00
if ( ! node & & ! pdata ) {
dev_err ( & client - > dev , " no platform data \n " ) ;
return - EINVAL ;
}
2013-01-16 17:53:57 +04:00
if ( twl_priv ) {
2013-01-16 17:53:56 +04:00
dev_dbg ( & client - > dev , " only one instance of %s allowed \n " ,
DRIVER_NAME ) ;
return - EBUSY ;
}
2012-09-18 03:26:10 +04:00
pdev = platform_device_alloc ( DRIVER_NAME , - 1 ) ;
if ( ! pdev ) {
dev_err ( & client - > dev , " can't alloc pdev \n " ) ;
return - ENOMEM ;
}
status = platform_device_add ( pdev ) ;
if ( status ) {
platform_device_put ( pdev ) ;
return status ;
}
2008-10-15 14:15:39 +04:00
if ( i2c_check_functionality ( client - > adapter , I2C_FUNC_I2C ) = = 0 ) {
dev_dbg ( & client - > dev , " can't talk I2C? \n " ) ;
2012-09-18 03:26:10 +04:00
status = - EIO ;
goto free ;
2008-10-15 14:15:39 +04:00
}
2013-01-16 17:53:57 +04:00
twl_priv = devm_kzalloc ( & client - > dev , sizeof ( struct twl_private ) ,
GFP_KERNEL ) ;
if ( ! twl_priv ) {
status = - ENOMEM ;
goto free ;
}
2012-01-04 16:58:33 +04:00
if ( ( id - > driver_data ) & TWL6030_CLASS ) {
2013-01-16 17:53:57 +04:00
twl_priv - > twl_id = TWL6030_CLASS_ID ;
twl_priv - > twl_map = & twl6030_map [ 0 ] ;
2013-06-19 16:24:02 +04:00
/* The charger base address is different in twl6032 */
if ( ( id - > driver_data ) & TWL6032_SUBCLASS )
2013-01-16 17:53:57 +04:00
twl_priv - > twl_map [ TWL_MODULE_MAIN_CHARGE ] . base =
2013-06-19 16:24:02 +04:00
TWL6032_BASEADD_CHARGER ;
2012-11-13 12:28:44 +04:00
twl_regmap_config = twl6030_regmap_config ;
2012-01-04 16:58:33 +04:00
} else {
2013-01-16 17:53:57 +04:00
twl_priv - > twl_id = TWL4030_CLASS_ID ;
twl_priv - > twl_map = & twl4030_map [ 0 ] ;
2012-11-13 12:28:44 +04:00
twl_regmap_config = twl4030_regmap_config ;
2013-01-16 17:53:53 +04:00
}
num_slaves = twl_get_num_slaves ( ) ;
treewide: devm_kzalloc() -> devm_kcalloc()
The devm_kzalloc() function has a 2-factor argument form, devm_kcalloc().
This patch replaces cases of:
devm_kzalloc(handle, a * b, gfp)
with:
devm_kcalloc(handle, a * b, gfp)
as well as handling cases of:
devm_kzalloc(handle, a * b * c, gfp)
with:
devm_kzalloc(handle, array3_size(a, b, c), gfp)
as it's slightly less ugly than:
devm_kcalloc(handle, array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
devm_kzalloc(handle, 4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
Some manual whitespace fixes were needed in this patch, as Coccinelle
really liked to write "=devm_kcalloc..." instead of "= devm_kcalloc...".
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
expression HANDLE;
type TYPE;
expression THING, E;
@@
(
devm_kzalloc(HANDLE,
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
devm_kzalloc(HANDLE,
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression HANDLE;
expression COUNT;
typedef u8;
typedef __u8;
@@
(
devm_kzalloc(HANDLE,
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
expression HANDLE;
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
expression HANDLE;
identifier SIZE, COUNT;
@@
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression HANDLE;
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression HANDLE;
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
expression HANDLE;
identifier STRIDE, SIZE, COUNT;
@@
(
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression HANDLE;
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE,
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression HANDLE;
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, sizeof(THING) * C2, ...)
|
devm_kzalloc(HANDLE, sizeof(TYPE) * C2, ...)
|
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE, C1 * C2, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * E2
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * (E2)
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 00:07:58 +03:00
twl_priv - > twl_modules = devm_kcalloc ( & client - > dev ,
num_slaves ,
sizeof ( struct twl_client ) ,
2013-01-16 17:53:57 +04:00
GFP_KERNEL ) ;
if ( ! twl_priv - > twl_modules ) {
2013-01-16 17:53:53 +04:00
status = - ENOMEM ;
goto free ;
2012-01-04 16:58:33 +04:00
}
for ( i = 0 ; i < num_slaves ; i + + ) {
2013-01-16 17:53:57 +04:00
struct twl_client * twl = & twl_priv - > twl_modules [ i ] ;
2008-10-15 14:15:39 +04:00
2012-03-02 14:11:26 +04:00
if ( i = = 0 ) {
2008-10-15 14:15:39 +04:00
twl - > client = client ;
2012-03-02 14:11:26 +04:00
} else {
2019-07-22 20:26:21 +03:00
twl - > client = i2c_new_dummy_device ( client - > adapter ,
2012-11-13 12:28:44 +04:00
client - > addr + i ) ;
2019-07-22 20:26:21 +03:00
if ( IS_ERR ( twl - > client ) ) {
2009-10-20 17:22:52 +04:00
dev_err ( & client - > dev ,
2008-10-15 14:15:39 +04:00
" can't attach client %d \n " , i ) ;
2019-07-22 20:26:21 +03:00
status = PTR_ERR ( twl - > client ) ;
2008-10-15 14:15:39 +04:00
goto fail ;
}
}
2012-11-13 12:28:44 +04:00
twl - > regmap = devm_regmap_init_i2c ( twl - > client ,
& twl_regmap_config [ i ] ) ;
if ( IS_ERR ( twl - > regmap ) ) {
status = PTR_ERR ( twl - > regmap ) ;
dev_err ( & client - > dev ,
" Failed to allocate regmap %d, err: %d \n " , i ,
status ) ;
goto fail ;
}
2008-10-15 14:15:39 +04:00
}
2012-03-02 14:11:26 +04:00
2013-01-16 17:53:57 +04:00
twl_priv - > ready = true ;
2008-10-15 14:15:39 +04:00
/* setup clock framework */
2018-04-09 11:45:39 +03:00
clocks_init ( & client - > dev , pdata ? pdata - > clock : NULL ) ;
2008-10-15 14:15:39 +04:00
2011-04-14 16:27:53 +04:00
/* read TWL IDCODE Register */
2013-01-16 17:53:57 +04:00
if ( twl_class_is_4030 ( ) ) {
2012-03-02 14:11:26 +04:00
status = twl_read_idcode_register ( ) ;
WARN ( status < 0 , " Error: reading twl_idcode register value \n " ) ;
2011-04-14 16:27:53 +04:00
}
2008-10-15 14:15:39 +04:00
/* Maybe init the T2 Interrupt subsystem */
2012-02-22 16:32:16 +04:00
if ( client - > irq ) {
2009-12-14 02:25:31 +03:00
if ( twl_class_is_4030 ( ) ) {
twl4030_init_chip_irq ( id - > name ) ;
2012-02-29 22:40:31 +04:00
irq_base = twl4030_init_irq ( & client - > dev , client - > irq ) ;
2009-12-14 02:25:31 +03:00
} else {
2012-02-29 22:40:31 +04:00
irq_base = twl6030_init_irq ( & client - > dev , client - > irq ) ;
2009-12-14 02:25:31 +03:00
}
2012-02-29 22:40:31 +04:00
if ( irq_base < 0 ) {
status = irq_base ;
2008-10-21 01:46:28 +04:00
goto fail ;
2012-02-29 22:40:31 +04:00
}
2008-10-15 14:15:39 +04:00
}
2012-03-02 14:11:26 +04:00
/*
* Disable TWL4030 / TWL5030 I2C Pull - up on I2C1 and I2C4 ( SR ) interface .
2010-02-17 03:57:21 +03:00
* Program I2C_SCL_CTRL_PU ( bit 0 ) = 0 , I2C_SDA_CTRL_PU ( bit 2 ) = 0 ,
* SR_I2C_SCL_CTRL_PU ( bit 4 ) = 0 and SR_I2C_SDA_CTRL_PU ( bit 6 ) = 0.
2014-04-11 03:47:18 +04:00
*
* Also , always enable SmartReflex bit as that ' s needed for omaps to
* to do anything over I2C4 for voltage scaling even if SmartReflex
* is disabled . Without the SmartReflex bit omap sys_clkreq idle
* signal will never trigger for retention idle .
2010-02-17 03:57:21 +03:00
*/
if ( twl_class_is_4030 ( ) ) {
2012-03-02 14:11:26 +04:00
u8 temp ;
2010-02-17 03:57:21 +03:00
twl_i2c_read_u8 ( TWL4030_MODULE_INTBR , & temp , REG_GPPUPDCTR1 ) ;
temp & = ~ ( SR_I2C_SDA_CTRL_PU | SR_I2C_SCL_CTRL_PU | \
2012-03-02 14:11:26 +04:00
I2C_SDA_CTRL_PU | I2C_SCL_CTRL_PU ) ;
2010-02-17 03:57:21 +03:00
twl_i2c_write_u8 ( TWL4030_MODULE_INTBR , temp , REG_GPPUPDCTR1 ) ;
2014-04-11 03:47:18 +04:00
twl_i2c_read_u8 ( TWL_MODULE_PM_RECEIVER , & temp ,
TWL4030_DCDC_GLOBAL_CFG ) ;
temp | = SMARTREFLEX_ENABLE ;
twl_i2c_write_u8 ( TWL_MODULE_PM_RECEIVER , temp ,
TWL4030_DCDC_GLOBAL_CFG ) ;
2010-02-17 03:57:21 +03:00
}
2013-11-16 04:36:03 +04:00
if ( node ) {
if ( pdata )
twl_auxdata_lookup [ 0 ] . platform_data = pdata - > gpio ;
status = of_platform_populate ( node , NULL , twl_auxdata_lookup ,
& client - > dev ) ;
} else {
2012-02-22 16:32:16 +04:00
status = add_children ( pdata , irq_base , id - > driver_data ) ;
2013-11-16 04:36:03 +04:00
}
2011-08-29 18:20:23 +04:00
2008-10-15 14:15:39 +04:00
fail :
if ( status < 0 )
2009-12-13 23:23:33 +03:00
twl_remove ( client ) ;
2012-09-18 03:26:10 +04:00
free :
if ( status < 0 )
platform_device_unregister ( pdev ) ;
2012-03-02 14:11:26 +04:00
2008-10-15 14:15:39 +04:00
return status ;
}
2019-02-23 14:47:54 +03:00
static int __maybe_unused twl_suspend ( struct device * dev )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
if ( client - > irq )
disable_irq ( client - > irq ) ;
return 0 ;
}
static int __maybe_unused twl_resume ( struct device * dev )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
if ( client - > irq )
enable_irq ( client - > irq ) ;
return 0 ;
}
static SIMPLE_DEV_PM_OPS ( twl_dev_pm_ops , twl_suspend , twl_resume ) ;
2009-12-13 23:23:33 +03:00
static const struct i2c_device_id twl_ids [ ] = {
2008-12-01 02:43:58 +03:00
{ " twl4030 " , TWL4030_VAUX2 } , /* "Triton 2" */
{ " twl5030 " , 0 } , /* T2 updated */
2009-11-10 18:26:15 +03:00
{ " twl5031 " , TWL5031 } , /* TWL5030 updated */
2008-12-01 02:43:58 +03:00
{ " tps65950 " , 0 } , /* catalog version of twl5030 */
{ " tps65930 " , TPS_SUBSET } , /* fewer LDOs and DACs; no charger */
{ " tps65920 " , TPS_SUBSET } , /* fewer LDOs; no codec or charger */
2011-07-01 15:34:13 +04:00
{ " tps65921 " , TPS_SUBSET } , /* fewer LDOs; no codec, no LED
and vibrator . Charger in USB module */
2009-12-14 02:25:31 +03:00
{ " twl6030 " , TWL6030_CLASS } , /* "Phoenix power chip" */
2013-06-19 16:24:02 +04:00
{ " twl6032 " , TWL6030_CLASS | TWL6032_SUBCLASS } , /* "Phoenix lite" */
2008-10-15 14:15:39 +04:00
{ /* end of list */ } ,
} ;
/* One Client Driver , 4 Clients */
2009-12-13 23:23:33 +03:00
static struct i2c_driver twl_driver = {
2008-10-15 14:15:39 +04:00
. driver . name = DRIVER_NAME ,
2019-02-23 14:47:54 +03:00
. driver . pm = & twl_dev_pm_ops ,
2009-12-13 23:23:33 +03:00
. id_table = twl_ids ,
. probe = twl_probe ,
. remove = twl_remove ,
2008-10-15 14:15:39 +04:00
} ;
2016-09-12 17:40:54 +03:00
builtin_i2c_driver ( twl_driver ) ;