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 >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/init.h>
# include <linux/mutex.h>
2011-07-03 23:13:27 +04:00
# include <linux/module.h>
2008-10-15 14:15:39 +04:00
# 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>
2009-12-13 22:05:51 +03:00
# include <linux/i2c/twl.h>
2008-10-15 14:15:39 +04:00
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) */
2009-12-13 23:23:33 +03:00
# define TWL_NUM_SLAVES 4
2008-10-15 14:15:39 +04:00
2009-12-13 23:23:33 +03:00
# define SUB_CHIP_ID0 0
# define SUB_CHIP_ID1 1
# define SUB_CHIP_ID2 2
# define SUB_CHIP_ID3 3
2008-10-15 14:15:39 +04:00
/* 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
# define TWL4030_BASEADD_PM_RECEIVER 0x005B
# 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
2011-05-12 17:27:55 +04:00
# define TWL6025_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
/*----------------------------------------------------------------------*/
/* is driver active, bound to a chip? */
static bool inuse ;
2011-04-14 16:27:53 +04:00
/* TWL IDCODE Register value */
static u32 twl_idcode ;
2009-12-14 02:25:31 +03:00
static unsigned int twl_id ;
unsigned int twl_rev ( void )
{
return twl_id ;
}
EXPORT_SYMBOL ( twl_rev ) ;
/* 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
} ;
2009-12-13 23:23:33 +03:00
static struct twl_client twl_modules [ TWL_NUM_SLAVES ] ;
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 */
} ;
2010-09-21 14:01:17 +04:00
static struct twl_mapping * twl_map ;
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
2009-12-14 02:25:31 +03:00
* < linux / i2c / 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
} ;
2012-11-13 12:28:44 +04:00
static struct regmap_config twl4030_regmap_config [ 4 ] = {
{
/* 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 ,
} ,
{
/* 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
* < linux / i2c / twl . h > defines for TWL4030_MODULE_ *
* 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
} ;
2012-11-13 12:28:44 +04:00
static struct regmap_config twl6030_regmap_config [ 3 ] = {
{
/* 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: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 */
/**
2009-12-13 23:23:33 +03:00
* twl_i2c_write - Writes 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 + 1 containing data to write
* @ reg : register address ( just offset will do )
* @ num_bytes : number of bytes to transfer
*
* IMPORTANT : for ' value ' parameter : Allocate value num_bytes + 1 and
* valid data starts at Offset 1.
*
* Returns the result of operation - 0 is success
*/
2009-12-13 23:23:33 +03:00
int twl_i2c_write ( u8 mod_no , u8 * value , u8 reg , unsigned num_bytes )
2008-10-15 14:15:39 +04:00
{
int ret ;
int sid ;
2009-12-13 23:23:33 +03:00
struct twl_client * twl ;
2008-10-15 14:15:39 +04:00
2013-01-16 17:53:50 +04:00
if ( unlikely ( mod_no > = twl_get_last_module ( ) ) ) {
2008-10-15 14:15:39 +04:00
pr_err ( " %s: invalid module number %d \n " , DRIVER_NAME , mod_no ) ;
return - EPERM ;
}
if ( unlikely ( ! inuse ) ) {
2011-08-02 01:00:28 +04:00
pr_err ( " %s: not initialized \n " , DRIVER_NAME ) ;
2008-10-15 14:15:39 +04:00
return - EPERM ;
}
2013-01-16 17:53:51 +04:00
2011-08-02 01:00:28 +04:00
sid = twl_map [ mod_no ] . sid ;
twl = & twl_modules [ sid ] ;
2012-11-13 12:28:44 +04:00
ret = regmap_bulk_write ( twl - > regmap , twl_map [ mod_no ] . base + reg ,
2012-11-13 12:28:45 +04:00
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
*
* Returns result of operation - num_bytes is success else failure .
*/
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
{
int ret ;
int sid ;
2009-12-13 23:23:33 +03:00
struct twl_client * twl ;
2008-10-15 14:15:39 +04:00
2013-01-16 17:53:50 +04:00
if ( unlikely ( mod_no > = twl_get_last_module ( ) ) ) {
2008-10-15 14:15:39 +04:00
pr_err ( " %s: invalid module number %d \n " , DRIVER_NAME , mod_no ) ;
return - EPERM ;
}
if ( unlikely ( ! inuse ) ) {
2011-08-02 01:00:28 +04:00
pr_err ( " %s: not initialized \n " , DRIVER_NAME ) ;
2008-10-15 14:15:39 +04:00
return - EPERM ;
}
2013-01-16 17:53:51 +04:00
2011-08-02 01:00:28 +04:00
sid = twl_map [ mod_no ] . sid ;
twl = & twl_modules [ sid ] ;
2012-11-13 12:28:44 +04:00
ret = regmap_bulk_read ( twl - > regmap , twl_map [ mod_no ] . base + reg ,
value , num_bytes ) ;
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
/**
2009-12-13 23:23:33 +03:00
* twl_i2c_write_u8 - Writes a 8 bit register in TWL4030 / TWL5030 / TWL60X0
2008-10-15 14:15:39 +04:00
* @ mod_no : module number
* @ value : the value to be written 8 bit
* @ reg : register address ( just offset will do )
*
* Returns result of operation - 0 is success
*/
2009-12-13 23:23:33 +03:00
int twl_i2c_write_u8 ( u8 mod_no , u8 value , u8 reg )
2008-10-15 14:15:39 +04:00
{
2012-11-13 12:28:45 +04:00
return twl_i2c_write ( mod_no , & value , reg , 1 ) ;
2008-10-15 14:15:39 +04:00
}
2009-12-13 23:23:33 +03:00
EXPORT_SYMBOL ( twl_i2c_write_u8 ) ;
2008-10-15 14:15:39 +04:00
/**
2009-12-13 23:23:33 +03:00
* twl_i2c_read_u8 - Reads a 8 bit register from TWL4030 / TWL5030 / TWL60X0
2008-10-15 14:15:39 +04:00
* @ mod_no : module number
* @ value : the value read 8 bit
* @ reg : register address ( just offset will do )
*
* Returns result of operation - 0 is success
*/
2009-12-13 23:23:33 +03:00
int twl_i2c_read_u8 ( u8 mod_no , u8 * value , u8 reg )
2008-10-15 14:15:39 +04:00
{
2009-12-13 23:23:33 +03:00
return twl_i2c_read ( mod_no , value , reg , 1 ) ;
2008-10-15 14:15:39 +04:00
}
2009-12-13 23:23:33 +03:00
EXPORT_SYMBOL ( twl_i2c_read_u8 ) ;
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 ;
}
err = twl_i2c_read ( TWL4030_MODULE_INTBR , ( u8 * ) ( & twl_idcode ) ,
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 )
{
return TWL_SIL_TYPE ( twl_idcode ) ;
}
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 )
{
return TWL_SIL_REV ( twl_idcode ) ;
}
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 *
add_numbered_child ( unsigned chip , 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 ;
2009-12-13 23:23:33 +03:00
struct twl_client * twl = & twl_modules [ chip ] ;
2008-12-01 02:31:04 +03:00
int status ;
2008-12-01 02:43:58 +03:00
pdev = platform_device_alloc ( name , num ) ;
2008-12-01 02:31:04 +03:00
if ( ! pdev ) {
dev_dbg ( & twl - > client - > dev , " can't alloc dev \n " ) ;
status = - ENOMEM ;
goto err ;
}
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 " ) ;
2008-10-15 14:15:39 +04:00
goto err ;
}
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 " ) ;
2008-10-15 14:15:39 +04:00
goto err ;
}
}
2008-12-01 02:31:04 +03:00
status = platform_device_add ( pdev ) ;
2012-07-07 02:51:03 +04:00
if ( status = = 0 )
device_init_wakeup ( & pdev - > dev , can_wakeup ) ;
2008-10-15 14:15:39 +04:00
2008-12-01 02:31:04 +03:00
err :
if ( status < 0 ) {
platform_device_put ( pdev ) ;
dev_err ( & twl - > client - > dev , " can't add %s dev \n " , name ) ;
return ERR_PTR ( status ) ;
}
return & pdev - > dev ;
}
2008-10-15 14:15:39 +04:00
2008-12-01 02:43:58 +03:00
static inline struct device * add_child ( unsigned chip , const char * name ,
void * pdata , unsigned pdata_len ,
bool can_wakeup , int irq0 , int irq1 )
{
return add_numbered_child ( chip , name , - 1 , pdata , pdata_len ,
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
{
2009-12-14 02:25:31 +03:00
unsigned sub_chip_id ;
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 */
2009-12-14 02:25:31 +03:00
sub_chip_id = twl_map [ TWL_MODULE_PM_MASTER ] . sid ;
return add_numbered_child ( sub_chip_id , " 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 ;
2009-12-14 02:25:31 +03:00
unsigned sub_chip_id ;
2008-10-15 14:15:39 +04:00
2012-09-18 12:29:50 +04:00
if ( IS_ENABLED ( CONFIG_GPIO_TWL4030 ) & & pdata - > gpio ) {
2009-12-13 23:23:33 +03:00
child = add_child ( SUB_CHIP_ID1 , " 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 ) {
2009-12-13 23:23:33 +03:00
child = add_child ( SUB_CHIP_ID2 , " 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 ( ) ) {
2012-11-13 12:28:46 +04:00
child = add_child ( SUB_CHIP_ID2 , " 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 " .
*/
2009-12-14 02:25:31 +03:00
sub_chip_id = twl_map [ TWL_MODULE_RTC ] . sid ;
2012-11-13 12:28:46 +04:00
child = add_child ( sub_chip_id , " 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 ) ) {
child = add_child ( SUB_CHIP_ID1 , " twl-pwm " , NULL , 0 ,
false , 0 , 0 ) ;
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
}
if ( IS_ENABLED ( CONFIG_PWM_TWL_LED ) ) {
child = add_child ( SUB_CHIP_ID1 , " 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 " ,
} ;
2012-05-09 01:40:40 +04:00
static struct regulator_consumer_supply usb3v1 [ ] = {
{ . supply = " usb3v1 " } ,
{ . supply = " bci3v1 " } ,
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 ,
2012-05-09 01:40:40 +04:00
& usb_fixed , usb3v1 , 2 ,
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 ) ;
}
2012-11-13 12:28:46 +04:00
child = add_child ( SUB_CHIP_ID0 , " twl4030_usb " ,
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 ) ;
2012-05-09 01:40:40 +04:00
usb3v1 [ 0 ] . 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_TWL6030_USB ) & & pdata - > usb & &
twl_class_is_6030 ( ) ) {
2010-12-10 15:39:52 +03:00
2011-05-12 17:27:55 +04:00
static struct regulator_consumer_supply usb3v3 ;
int regulator ;
2010-12-10 15:39:52 +03:00
2012-09-18 12:29:50 +04:00
if ( IS_ENABLED ( CONFIG_REGULATOR_TWL4030 ) ) {
2010-12-10 15:39:52 +03: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 ,
} ;
2011-05-12 17:27:55 +04:00
if ( features & TWL6025_SUBCLASS ) {
usb3v3 . supply = " ldousb " ;
regulator = TWL6025_REG_LDOUSB ;
} else {
usb3v3 . supply = " vusb " ;
regulator = TWL6030_REG_VUSB ;
}
child = add_regulator_linked ( regulator , & usb_fixed ,
& usb3v3 , 1 ,
features ) ;
2010-12-10 15:39:52 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
}
2011-05-12 17:27:55 +04:00
pdata - > usb - > features = features ;
2012-11-13 12:28:46 +04:00
child = add_child ( SUB_CHIP_ID0 , " twl6030_usb " ,
pdata - > usb , sizeof ( * pdata - > usb ) , true ,
2010-12-10 15:39:52 +03:00
/* irq1 = VBUS_PRES, irq0 = USB ID */
2012-02-22 16:32:16 +04:00
irq_base + USBOTG_INTR_OFFSET ,
irq_base + USB_PRES_INTR_OFFSET ) ;
2010-12-10 15:39:52 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
/* 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
usb3v3 . dev_name = dev_name ( child ) ;
2012-09-18 12:29:50 +04:00
} else if ( IS_ENABLED ( CONFIG_REGULATOR_TWL4030 ) & &
twl_class_is_6030 ( ) ) {
2011-05-12 17:27:55 +04:00
if ( features & TWL6025_SUBCLASS )
child = add_regulator ( TWL6025_REG_LDOUSB ,
pdata - > ldousb , features ) ;
else
child = add_regulator ( TWL6030_REG_VUSB ,
pdata - > vusb , features ) ;
2010-12-10 15:39:52 +03:00
2011-05-12 17:27:55 +04:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2010-12-10 15:39:52 +03: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 ( ) ) {
2012-11-13 12:28:46 +04:00
child = add_child ( SUB_CHIP_ID3 , " 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 ( ) ) {
2012-11-13 12:28:46 +04:00
child = add_child ( SUB_CHIP_ID3 , " 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 ( ) ) {
2012-11-13 12:28:46 +04:00
child = add_child ( SUB_CHIP_ID1 , " 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
}
2009-12-14 00:29:47 +03:00
/* twl6030 regulators */
2012-09-18 12:29:50 +04:00
if ( IS_ENABLED ( CONFIG_REGULATOR_TWL4030 ) & & twl_class_is_6030 ( ) & &
2011-05-12 17:27:55 +04:00
! ( features & TWL6025_SUBCLASS ) ) {
2012-02-28 13:39:10 +04:00
child = add_regulator ( TWL6030_REG_VDD1 , pdata - > vdd1 ,
features ) ;
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
child = add_regulator ( TWL6030_REG_VDD2 , pdata - > vdd2 ,
features ) ;
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
child = add_regulator ( TWL6030_REG_VDD3 , pdata - > vdd3 ,
features ) ;
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2012-02-28 13:39:13 +04:00
child = add_regulator ( TWL6030_REG_V1V8 , pdata - > v1v8 ,
features ) ;
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
child = add_regulator ( TWL6030_REG_V2V1 , pdata - > v2v1 ,
features ) ;
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL6030_REG_VMMC , pdata - > vmmc ,
features ) ;
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
child = add_regulator ( TWL6030_REG_VPP , pdata - > vpp ,
features ) ;
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
child = add_regulator ( TWL6030_REG_VUSIM , pdata - > vusim ,
features ) ;
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
child = add_regulator ( TWL6030_REG_VCXIO , pdata - > vcxio ,
features ) ;
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
child = add_regulator ( TWL6030_REG_VDAC , pdata - > vdac ,
features ) ;
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
child = add_regulator ( TWL6030_REG_VAUX1_6030 , pdata - > vaux1 ,
features ) ;
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
child = add_regulator ( TWL6030_REG_VAUX2_6030 , pdata - > vaux2 ,
features ) ;
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
child = add_regulator ( TWL6030_REG_VAUX3_6030 , pdata - > vaux3 ,
features ) ;
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
child = add_regulator ( TWL6030_REG_CLK32KG , pdata - > clk32kg ,
features ) ;
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
}
/* 6030 and 6025 share this regulator */
2012-09-18 12:29:50 +04:00
if ( IS_ENABLED ( CONFIG_REGULATOR_TWL4030 ) & & twl_class_is_6030 ( ) ) {
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL6030_REG_VANA , pdata - > vana ,
features ) ;
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
}
/* twl6025 regulators */
2012-09-18 12:29:50 +04:00
if ( IS_ENABLED ( CONFIG_REGULATOR_TWL4030 ) & & twl_class_is_6030 ( ) & &
2011-05-12 17:27:55 +04:00
( features & TWL6025_SUBCLASS ) ) {
child = add_regulator ( TWL6025_REG_LDO5 , pdata - > ldo5 ,
features ) ;
2009-12-14 00:29:47 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL6025_REG_LDO1 , pdata - > ldo1 ,
features ) ;
2009-12-14 00:29:47 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL6025_REG_LDO7 , pdata - > ldo7 ,
features ) ;
2009-12-14 00:29:47 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL6025_REG_LDO6 , pdata - > ldo6 ,
features ) ;
2009-12-14 00:29:47 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL6025_REG_LDOLN , pdata - > ldoln ,
features ) ;
2009-12-14 00:29:47 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL6025_REG_LDO2 , pdata - > ldo2 ,
features ) ;
2009-12-14 00:29:47 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL6025_REG_LDO4 , pdata - > ldo4 ,
features ) ;
2009-12-14 00:29:47 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL6025_REG_LDO3 , pdata - > ldo3 ,
features ) ;
2009-12-14 00:29:47 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL6025_REG_SMPS3 , pdata - > smps3 ,
features ) ;
2009-12-14 00:29:47 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2011-02-10 16:14:50 +03:00
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL6025_REG_SMPS4 , pdata - > smps4 ,
features ) ;
2011-02-10 16:14:50 +03:00
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2011-05-12 17:27:55 +04:00
child = add_regulator ( TWL6025_REG_VIO , pdata - > vio6025 ,
features ) ;
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
2009-12-14 00:29:47 +03: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 ) ) ) {
2012-11-13 12:28:46 +04:00
child = add_child ( SUB_CHIP_ID3 , " 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 ) ;
}
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 .
*/
static inline int __init protect_pm_master ( void )
{
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 ;
}
static inline int __init unprotect_pm_master ( void )
{
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
2012-01-04 16:58:33 +04:00
if ( twl_class_is_4030 ( ) ) {
2009-12-14 02:25:31 +03:00
status = twl4030_exit_irq ( ) ;
2012-01-04 16:58:33 +04:00
num_slaves = TWL_NUM_SLAVES ;
} else {
2009-12-14 02:25:31 +03:00
status = twl6030_exit_irq ( ) ;
2012-01-04 16:58:33 +04:00
num_slaves = TWL_NUM_SLAVES - 1 ;
}
2009-12-14 02:25:31 +03:00
2008-10-21 01:46:28 +04:00
if ( status < 0 )
return status ;
2008-10-15 14:15:39 +04:00
2012-01-04 16:58:33 +04:00
for ( i = 0 ; i < num_slaves ; i + + ) {
2009-12-13 23:23:33 +03:00
struct twl_client * twl = & twl_modules [ i ] ;
2008-10-15 14:15:39 +04:00
if ( twl - > client & & twl - > client ! = client )
i2c_unregister_device ( twl - > client ) ;
2009-12-13 23:23:33 +03:00
twl_modules [ i ] . client = NULL ;
2008-10-15 14:15:39 +04:00
}
inuse = false ;
return 0 ;
}
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
{
struct twl4030_platform_data * pdata = client - > dev . platform_data ;
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 ;
2012-11-13 12:28:44 +04:00
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
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 ;
}
2011-08-29 18:20:23 +04:00
if ( node & & ! pdata ) {
/*
* XXX : Temporary pdata until the information is correctly
* retrieved by every TWL modules from DT .
*/
pdata = devm_kzalloc ( & client - > dev ,
sizeof ( struct twl4030_platform_data ) ,
GFP_KERNEL ) ;
2012-09-18 03:26:10 +04:00
if ( ! pdata ) {
status = - ENOMEM ;
goto free ;
}
2011-08-29 18:20:23 +04:00
}
2008-10-15 14:15:39 +04:00
if ( ! pdata ) {
dev_dbg ( & client - > dev , " no platform data? \n " ) ;
2012-09-18 03:26:10 +04:00
status = - EINVAL ;
goto free ;
2008-10-15 14:15:39 +04:00
}
2012-09-18 03:26:10 +04:00
platform_set_drvdata ( pdev , pdata ) ;
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
}
2008-10-21 01:46:28 +04:00
if ( inuse ) {
2008-10-15 14:15:39 +04:00
dev_dbg ( & client - > dev , " driver is already in use \n " ) ;
2012-09-18 03:26:10 +04:00
status = - EBUSY ;
goto free ;
2008-10-15 14:15:39 +04:00
}
2012-01-04 16:58:33 +04:00
if ( ( id - > driver_data ) & TWL6030_CLASS ) {
twl_id = TWL6030_CLASS_ID ;
twl_map = & twl6030_map [ 0 ] ;
2013-01-16 17:53:50 +04:00
/* The charger base address is different in twl6025 */
if ( ( id - > driver_data ) & TWL6025_SUBCLASS )
twl_map [ TWL_MODULE_MAIN_CHARGE ] . base =
TWL6025_BASEADD_CHARGER ;
2012-11-13 12:28:44 +04:00
twl_regmap_config = twl6030_regmap_config ;
2012-01-04 16:58:33 +04:00
num_slaves = TWL_NUM_SLAVES - 1 ;
} else {
twl_id = TWL4030_CLASS_ID ;
twl_map = & twl4030_map [ 0 ] ;
2012-11-13 12:28:44 +04:00
twl_regmap_config = twl4030_regmap_config ;
2012-01-04 16:58:33 +04:00
num_slaves = TWL_NUM_SLAVES ;
}
for ( i = 0 ; i < num_slaves ; i + + ) {
2012-03-02 14:11:26 +04:00
struct twl_client * twl = & 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 {
2008-10-15 14:15:39 +04:00
twl - > client = i2c_new_dummy ( client - > adapter ,
2012-11-13 12:28:44 +04:00
client - > addr + i ) ;
2008-10-15 14:15:39 +04:00
if ( ! 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 ) ;
status = - ENOMEM ;
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
2008-10-15 14:15:39 +04:00
inuse = true ;
/* setup clock framework */
2012-09-18 03:26:10 +04:00
clocks_init ( & pdev - > dev , pdata - > clock ) ;
2008-10-15 14:15:39 +04:00
2011-04-14 16:27:53 +04:00
/* read TWL IDCODE Register */
if ( twl_id = = TWL4030_CLASS_ID ) {
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
}
2009-08-31 20:32:18 +04:00
/* load power event scripts */
2012-09-18 12:29:50 +04:00
if ( IS_ENABLED ( CONFIG_TWL4030_POWER ) & & pdata - > power )
2009-08-31 20:32:18 +04:00
twl4030_power_init ( pdata - > power ) ;
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.
*/
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 ) ;
}
2012-02-25 01:58:34 +04:00
status = - ENODEV ;
2011-08-29 18:20:23 +04:00
if ( node )
status = of_platform_populate ( node , NULL , NULL , & client - > dev ) ;
2012-02-25 01:58:34 +04:00
if ( status )
2012-02-22 16:32:16 +04:00
status = add_children ( pdata , irq_base , id - > driver_data ) ;
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 ;
}
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" */
2011-05-12 17:27:55 +04:00
{ " twl6025 " , TWL6030_CLASS | TWL6025_SUBCLASS } , /* "Phoenix lite" */
2008-10-15 14:15:39 +04:00
{ /* end of list */ } ,
} ;
2009-12-13 23:23:33 +03:00
MODULE_DEVICE_TABLE ( i2c , twl_ids ) ;
2008-10-15 14:15:39 +04:00
/* 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 ,
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
} ;
2009-12-13 23:23:33 +03:00
static int __init twl_init ( void )
2008-10-15 14:15:39 +04:00
{
2009-12-13 23:23:33 +03:00
return i2c_add_driver ( & twl_driver ) ;
2008-10-15 14:15:39 +04:00
}
2009-12-13 23:23:33 +03:00
subsys_initcall ( twl_init ) ;
2008-10-15 14:15:39 +04:00
2009-12-13 23:23:33 +03:00
static void __exit twl_exit ( void )
2008-10-15 14:15:39 +04:00
{
2009-12-13 23:23:33 +03:00
i2c_del_driver ( & twl_driver ) ;
2008-10-15 14:15:39 +04:00
}
2009-12-13 23:23:33 +03:00
module_exit ( twl_exit ) ;
2008-10-15 14:15:39 +04:00
MODULE_AUTHOR ( " Texas Instruments, Inc. " ) ;
2009-12-13 23:23:33 +03:00
MODULE_DESCRIPTION ( " I2C Core interface for TWL " ) ;
2008-10-15 14:15:39 +04:00
MODULE_LICENSE ( " GPL " ) ;