2012-05-15 10:48:56 +04:00
/*
* TI Palmas MFD Driver
*
* Copyright 2011 - 2012 Texas Instruments Inc .
*
* Author : Graeme Gregory < gg @ slimlogic . co . uk >
*
* 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 .
*
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/i2c.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/regmap.h>
# include <linux/err.h>
# include <linux/mfd/core.h>
# include <linux/mfd/palmas.h>
2013-06-19 09:57:48 +04:00
# include <linux/of_device.h>
2012-05-15 10:48:56 +04:00
2013-08-13 11:53:11 +04:00
# define PALMAS_EXT_REQ (PALMAS_EXT_CONTROL_ENABLE1 | \
PALMAS_EXT_CONTROL_ENABLE2 | \
PALMAS_EXT_CONTROL_NSLEEP )
struct palmas_sleep_requestor_info {
int id ;
int reg_offset ;
int bit_pos ;
} ;
# define EXTERNAL_REQUESTOR(_id, _offset, _pos) \
[ PALMAS_EXTERNAL_REQSTR_ID_ # # _id ] = { \
. id = PALMAS_EXTERNAL_REQSTR_ID_ # # _id , \
. reg_offset = _offset , \
. bit_pos = _pos , \
}
static struct palmas_sleep_requestor_info sleep_req_info [ ] = {
EXTERNAL_REQUESTOR ( REGEN1 , 0 , 0 ) ,
EXTERNAL_REQUESTOR ( REGEN2 , 0 , 1 ) ,
EXTERNAL_REQUESTOR ( SYSEN1 , 0 , 2 ) ,
EXTERNAL_REQUESTOR ( SYSEN2 , 0 , 3 ) ,
EXTERNAL_REQUESTOR ( CLK32KG , 0 , 4 ) ,
EXTERNAL_REQUESTOR ( CLK32KGAUDIO , 0 , 5 ) ,
EXTERNAL_REQUESTOR ( REGEN3 , 0 , 6 ) ,
EXTERNAL_REQUESTOR ( SMPS12 , 1 , 0 ) ,
EXTERNAL_REQUESTOR ( SMPS3 , 1 , 1 ) ,
EXTERNAL_REQUESTOR ( SMPS45 , 1 , 2 ) ,
EXTERNAL_REQUESTOR ( SMPS6 , 1 , 3 ) ,
EXTERNAL_REQUESTOR ( SMPS7 , 1 , 4 ) ,
EXTERNAL_REQUESTOR ( SMPS8 , 1 , 5 ) ,
EXTERNAL_REQUESTOR ( SMPS9 , 1 , 6 ) ,
EXTERNAL_REQUESTOR ( SMPS10 , 1 , 7 ) ,
EXTERNAL_REQUESTOR ( LDO1 , 2 , 0 ) ,
EXTERNAL_REQUESTOR ( LDO2 , 2 , 1 ) ,
EXTERNAL_REQUESTOR ( LDO3 , 2 , 2 ) ,
EXTERNAL_REQUESTOR ( LDO4 , 2 , 3 ) ,
EXTERNAL_REQUESTOR ( LDO5 , 2 , 4 ) ,
EXTERNAL_REQUESTOR ( LDO6 , 2 , 5 ) ,
EXTERNAL_REQUESTOR ( LDO7 , 2 , 6 ) ,
EXTERNAL_REQUESTOR ( LDO8 , 2 , 7 ) ,
EXTERNAL_REQUESTOR ( LDO9 , 3 , 0 ) ,
EXTERNAL_REQUESTOR ( LDOLN , 3 , 1 ) ,
EXTERNAL_REQUESTOR ( LDOUSB , 3 , 2 ) ,
} ;
2012-05-15 10:48:56 +04:00
static const struct regmap_config palmas_regmap_config [ PALMAS_NUM_CLIENTS ] = {
{
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = PALMAS_BASE_TO_REG ( PALMAS_PU_PD_OD_BASE ,
PALMAS_PRIMARY_SECONDARY_PAD3 ) ,
} ,
{
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = PALMAS_BASE_TO_REG ( PALMAS_GPADC_BASE ,
PALMAS_GPADC_SMPS_VSEL_MONITORING ) ,
} ,
{
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = PALMAS_BASE_TO_REG ( PALMAS_TRIM_GPADC_BASE ,
PALMAS_GPADC_TRIM16 ) ,
} ,
} ;
static const struct regmap_irq palmas_irqs [ ] = {
/* INT1 IRQs */
[ PALMAS_CHARG_DET_N_VBUS_OVV_IRQ ] = {
. mask = PALMAS_INT1_STATUS_CHARG_DET_N_VBUS_OVV ,
} ,
[ PALMAS_PWRON_IRQ ] = {
. mask = PALMAS_INT1_STATUS_PWRON ,
} ,
[ PALMAS_LONG_PRESS_KEY_IRQ ] = {
. mask = PALMAS_INT1_STATUS_LONG_PRESS_KEY ,
} ,
[ PALMAS_RPWRON_IRQ ] = {
. mask = PALMAS_INT1_STATUS_RPWRON ,
} ,
[ PALMAS_PWRDOWN_IRQ ] = {
. mask = PALMAS_INT1_STATUS_PWRDOWN ,
} ,
[ PALMAS_HOTDIE_IRQ ] = {
. mask = PALMAS_INT1_STATUS_HOTDIE ,
} ,
[ PALMAS_VSYS_MON_IRQ ] = {
. mask = PALMAS_INT1_STATUS_VSYS_MON ,
} ,
[ PALMAS_VBAT_MON_IRQ ] = {
. mask = PALMAS_INT1_STATUS_VBAT_MON ,
} ,
/* INT2 IRQs*/
[ PALMAS_RTC_ALARM_IRQ ] = {
. mask = PALMAS_INT2_STATUS_RTC_ALARM ,
. reg_offset = 1 ,
} ,
[ PALMAS_RTC_TIMER_IRQ ] = {
. mask = PALMAS_INT2_STATUS_RTC_TIMER ,
. reg_offset = 1 ,
} ,
[ PALMAS_WDT_IRQ ] = {
. mask = PALMAS_INT2_STATUS_WDT ,
. reg_offset = 1 ,
} ,
[ PALMAS_BATREMOVAL_IRQ ] = {
. mask = PALMAS_INT2_STATUS_BATREMOVAL ,
. reg_offset = 1 ,
} ,
[ PALMAS_RESET_IN_IRQ ] = {
. mask = PALMAS_INT2_STATUS_RESET_IN ,
. reg_offset = 1 ,
} ,
[ PALMAS_FBI_BB_IRQ ] = {
. mask = PALMAS_INT2_STATUS_FBI_BB ,
. reg_offset = 1 ,
} ,
[ PALMAS_SHORT_IRQ ] = {
. mask = PALMAS_INT2_STATUS_SHORT ,
. reg_offset = 1 ,
} ,
[ PALMAS_VAC_ACOK_IRQ ] = {
. mask = PALMAS_INT2_STATUS_VAC_ACOK ,
. reg_offset = 1 ,
} ,
/* INT3 IRQs */
[ PALMAS_GPADC_AUTO_0_IRQ ] = {
. mask = PALMAS_INT3_STATUS_GPADC_AUTO_0 ,
. reg_offset = 2 ,
} ,
[ PALMAS_GPADC_AUTO_1_IRQ ] = {
. mask = PALMAS_INT3_STATUS_GPADC_AUTO_1 ,
. reg_offset = 2 ,
} ,
[ PALMAS_GPADC_EOC_SW_IRQ ] = {
. mask = PALMAS_INT3_STATUS_GPADC_EOC_SW ,
. reg_offset = 2 ,
} ,
[ PALMAS_GPADC_EOC_RT_IRQ ] = {
. mask = PALMAS_INT3_STATUS_GPADC_EOC_RT ,
. reg_offset = 2 ,
} ,
[ PALMAS_ID_OTG_IRQ ] = {
. mask = PALMAS_INT3_STATUS_ID_OTG ,
. reg_offset = 2 ,
} ,
[ PALMAS_ID_IRQ ] = {
. mask = PALMAS_INT3_STATUS_ID ,
. reg_offset = 2 ,
} ,
[ PALMAS_VBUS_OTG_IRQ ] = {
. mask = PALMAS_INT3_STATUS_VBUS_OTG ,
. reg_offset = 2 ,
} ,
[ PALMAS_VBUS_IRQ ] = {
. mask = PALMAS_INT3_STATUS_VBUS ,
. reg_offset = 2 ,
} ,
/* INT4 IRQs */
[ PALMAS_GPIO_0_IRQ ] = {
. mask = PALMAS_INT4_STATUS_GPIO_0 ,
. reg_offset = 3 ,
} ,
[ PALMAS_GPIO_1_IRQ ] = {
. mask = PALMAS_INT4_STATUS_GPIO_1 ,
. reg_offset = 3 ,
} ,
[ PALMAS_GPIO_2_IRQ ] = {
. mask = PALMAS_INT4_STATUS_GPIO_2 ,
. reg_offset = 3 ,
} ,
[ PALMAS_GPIO_3_IRQ ] = {
. mask = PALMAS_INT4_STATUS_GPIO_3 ,
. reg_offset = 3 ,
} ,
[ PALMAS_GPIO_4_IRQ ] = {
. mask = PALMAS_INT4_STATUS_GPIO_4 ,
. reg_offset = 3 ,
} ,
[ PALMAS_GPIO_5_IRQ ] = {
. mask = PALMAS_INT4_STATUS_GPIO_5 ,
. reg_offset = 3 ,
} ,
[ PALMAS_GPIO_6_IRQ ] = {
. mask = PALMAS_INT4_STATUS_GPIO_6 ,
. reg_offset = 3 ,
} ,
[ PALMAS_GPIO_7_IRQ ] = {
. mask = PALMAS_INT4_STATUS_GPIO_7 ,
. reg_offset = 3 ,
} ,
} ;
static struct regmap_irq_chip palmas_irq_chip = {
. name = " palmas " ,
. irqs = palmas_irqs ,
. num_irqs = ARRAY_SIZE ( palmas_irqs ) ,
. num_regs = 4 ,
. irq_reg_stride = 5 ,
. status_base = PALMAS_BASE_TO_REG ( PALMAS_INTERRUPT_BASE ,
PALMAS_INT1_STATUS ) ,
. mask_base = PALMAS_BASE_TO_REG ( PALMAS_INTERRUPT_BASE ,
PALMAS_INT1_MASK ) ,
} ;
2013-08-13 11:53:11 +04:00
int palmas_ext_control_req_config ( struct palmas * palmas ,
enum palmas_external_requestor_id id , int ext_ctrl , bool enable )
{
int preq_mask_bit = 0 ;
int reg_add = 0 ;
int bit_pos ;
int ret ;
if ( ! ( ext_ctrl & PALMAS_EXT_REQ ) )
return 0 ;
if ( id > = PALMAS_EXTERNAL_REQSTR_ID_MAX )
return 0 ;
if ( ext_ctrl & PALMAS_EXT_CONTROL_NSLEEP ) {
reg_add = PALMAS_NSLEEP_RES_ASSIGN ;
preq_mask_bit = 0 ;
} else if ( ext_ctrl & PALMAS_EXT_CONTROL_ENABLE1 ) {
reg_add = PALMAS_ENABLE1_RES_ASSIGN ;
preq_mask_bit = 1 ;
} else if ( ext_ctrl & PALMAS_EXT_CONTROL_ENABLE2 ) {
reg_add = PALMAS_ENABLE2_RES_ASSIGN ;
preq_mask_bit = 2 ;
}
bit_pos = sleep_req_info [ id ] . bit_pos ;
reg_add + = sleep_req_info [ id ] . reg_offset ;
if ( enable )
ret = palmas_update_bits ( palmas , PALMAS_RESOURCE_BASE ,
reg_add , BIT ( bit_pos ) , BIT ( bit_pos ) ) ;
else
ret = palmas_update_bits ( palmas , PALMAS_RESOURCE_BASE ,
reg_add , BIT ( bit_pos ) , 0 ) ;
if ( ret < 0 ) {
dev_err ( palmas - > dev , " Resource reg 0x%02x update failed %d \n " ,
reg_add , ret ) ;
return ret ;
}
/* Unmask the PREQ */
ret = palmas_update_bits ( palmas , PALMAS_PMU_CONTROL_BASE ,
PALMAS_POWER_CTRL , BIT ( preq_mask_bit ) , 0 ) ;
if ( ret < 0 ) {
dev_err ( palmas - > dev , " POWER_CTRL register update failed %d \n " ,
ret ) ;
return ret ;
}
return ret ;
}
EXPORT_SYMBOL_GPL ( palmas_ext_control_req_config ) ;
2013-03-01 18:43:46 +04:00
static int palmas_set_pdata_irq_flag ( struct i2c_client * i2c ,
2012-08-28 15:47:38 +04:00
struct palmas_platform_data * pdata )
{
2013-03-01 18:43:46 +04:00
struct irq_data * irq_data = irq_get_irq_data ( i2c - > irq ) ;
if ( ! irq_data ) {
dev_err ( & i2c - > dev , " Invalid IRQ: %d \n " , i2c - > irq ) ;
return - EINVAL ;
}
pdata - > irq_flags = irqd_get_trigger_type ( irq_data ) ;
dev_info ( & i2c - > dev , " Irq flag is 0x%08x \n " , pdata - > irq_flags ) ;
return 0 ;
}
static void palmas_dt_to_pdata ( struct i2c_client * i2c ,
struct palmas_platform_data * pdata )
{
struct device_node * node = i2c - > dev . of_node ;
2012-08-28 15:47:38 +04:00
int ret ;
u32 prop ;
2013-02-18 09:12:44 +04:00
ret = of_property_read_u32 ( node , " ti,mux-pad1 " , & prop ) ;
2012-08-28 15:47:38 +04:00
if ( ! ret ) {
pdata - > mux_from_pdata = 1 ;
pdata - > pad1 = prop ;
}
2013-02-18 09:12:44 +04:00
ret = of_property_read_u32 ( node , " ti,mux-pad2 " , & prop ) ;
2012-08-28 15:47:38 +04:00
if ( ! ret ) {
pdata - > mux_from_pdata = 1 ;
pdata - > pad2 = prop ;
}
/* The default for this register is all masked */
2013-02-18 09:12:44 +04:00
ret = of_property_read_u32 ( node , " ti,power-ctrl " , & prop ) ;
2012-08-28 15:47:38 +04:00
if ( ! ret )
pdata - > power_ctrl = prop ;
else
pdata - > power_ctrl = PALMAS_POWER_CTRL_NSLEEP_MASK |
PALMAS_POWER_CTRL_ENABLE1_MASK |
PALMAS_POWER_CTRL_ENABLE2_MASK ;
2013-03-01 18:43:46 +04:00
if ( i2c - > irq )
palmas_set_pdata_irq_flag ( i2c , pdata ) ;
2013-08-08 15:45:05 +04:00
pdata - > pm_off = of_property_read_bool ( node ,
" ti,system-power-controller " ) ;
}
static struct palmas * palmas_dev ;
static void palmas_power_off ( void )
{
unsigned int addr ;
int ret , slave ;
if ( ! palmas_dev )
return ;
slave = PALMAS_BASE_TO_SLAVE ( PALMAS_PMU_CONTROL_BASE ) ;
addr = PALMAS_BASE_TO_REG ( PALMAS_PMU_CONTROL_BASE , PALMAS_DEV_CTRL ) ;
ret = regmap_update_bits (
palmas_dev - > regmap [ slave ] ,
addr ,
PALMAS_DEV_CTRL_DEV_ON ,
0 ) ;
if ( ret )
pr_err ( " %s: Unable to write to DEV_CTRL_DEV_ON: %d \n " ,
__func__ , ret ) ;
2012-08-28 15:47:38 +04:00
}
2013-06-19 09:57:48 +04:00
static unsigned int palmas_features = PALMAS_PMIC_FEATURE_SMPS10_BOOST ;
2013-06-20 15:05:16 +04:00
static unsigned int tps659038_features ;
2013-06-19 09:57:48 +04:00
static const struct of_device_id of_palmas_match_tbl [ ] = {
{
. compatible = " ti,palmas " ,
. data = & palmas_features ,
} ,
2013-06-20 15:05:16 +04:00
{
. compatible = " ti,tps659038 " ,
. data = & tps659038_features ,
} ,
2013-06-19 09:57:48 +04:00
{ } ,
} ;
2013-09-26 17:33:49 +04:00
MODULE_DEVICE_TABLE ( of , of_palmas_match_tbl ) ;
2013-06-19 09:57:48 +04:00
2012-11-19 22:23:04 +04:00
static int palmas_i2c_probe ( struct i2c_client * i2c ,
2012-05-15 10:48:56 +04:00
const struct i2c_device_id * id )
{
struct palmas * palmas ;
struct palmas_platform_data * pdata ;
2012-08-28 15:47:38 +04:00
struct device_node * node = i2c - > dev . of_node ;
2012-05-15 10:48:56 +04:00
int ret = 0 , i ;
2013-06-19 09:57:48 +04:00
unsigned int reg , addr , * features ;
2012-05-15 10:48:56 +04:00
int slave ;
2013-06-19 09:57:48 +04:00
const struct of_device_id * match ;
2012-05-15 10:48:56 +04:00
pdata = dev_get_platdata ( & i2c - > dev ) ;
2012-08-28 15:47:38 +04:00
if ( node & & ! pdata ) {
pdata = devm_kzalloc ( & i2c - > dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
if ( ! pdata )
return - ENOMEM ;
2013-03-01 18:43:46 +04:00
palmas_dt_to_pdata ( i2c , pdata ) ;
2012-08-28 15:47:38 +04:00
}
2012-05-15 10:48:56 +04:00
if ( ! pdata )
return - EINVAL ;
palmas = devm_kzalloc ( & i2c - > dev , sizeof ( struct palmas ) , GFP_KERNEL ) ;
if ( palmas = = NULL )
return - ENOMEM ;
i2c_set_clientdata ( i2c , palmas ) ;
palmas - > dev = & i2c - > dev ;
palmas - > irq = i2c - > irq ;
2013-10-15 07:48:51 +04:00
match = of_match_device ( of_palmas_match_tbl , & i2c - > dev ) ;
2013-06-19 09:57:48 +04:00
if ( ! match )
return - ENODATA ;
features = ( unsigned int * ) match - > data ;
palmas - > features = * features ;
2012-05-15 10:48:56 +04:00
for ( i = 0 ; i < PALMAS_NUM_CLIENTS ; i + + ) {
if ( i = = 0 )
palmas - > i2c_clients [ i ] = i2c ;
else {
palmas - > i2c_clients [ i ] =
i2c_new_dummy ( i2c - > adapter ,
i2c - > addr + i ) ;
if ( ! palmas - > i2c_clients [ i ] ) {
dev_err ( palmas - > dev ,
" can't attach client %d \n " , i ) ;
ret = - ENOMEM ;
2013-09-26 17:33:51 +04:00
goto err_i2c ;
2012-05-15 10:48:56 +04:00
}
2013-03-19 12:58:20 +04:00
palmas - > i2c_clients [ i ] - > dev . of_node = of_node_get ( node ) ;
2012-05-15 10:48:56 +04:00
}
palmas - > regmap [ i ] = devm_regmap_init_i2c ( palmas - > i2c_clients [ i ] ,
& palmas_regmap_config [ i ] ) ;
if ( IS_ERR ( palmas - > regmap [ i ] ) ) {
ret = PTR_ERR ( palmas - > regmap [ i ] ) ;
dev_err ( palmas - > dev ,
" Failed to allocate regmap %d, err: %d \n " ,
i , ret ) ;
2013-09-26 17:33:51 +04:00
goto err_i2c ;
2012-05-15 10:48:56 +04:00
}
}
2013-06-19 09:57:47 +04:00
if ( ! palmas - > irq ) {
dev_warn ( palmas - > dev , " IRQ missing: skipping irq request \n " ) ;
goto no_irq ;
}
2013-03-01 18:43:46 +04:00
/* Change interrupt line output polarity */
if ( pdata - > irq_flags & IRQ_TYPE_LEVEL_HIGH )
reg = PALMAS_POLARITY_CTRL_INT_POLARITY ;
else
reg = 0 ;
ret = palmas_update_bits ( palmas , PALMAS_PU_PD_OD_BASE ,
PALMAS_POLARITY_CTRL , PALMAS_POLARITY_CTRL_INT_POLARITY ,
reg ) ;
if ( ret < 0 ) {
dev_err ( palmas - > dev , " POLARITY_CTRL updat failed: %d \n " , ret ) ;
2013-09-26 17:33:51 +04:00
goto err_i2c ;
2013-03-01 18:43:46 +04:00
}
2012-06-22 16:36:19 +04:00
/* Change IRQ into clear on read mode for efficiency */
slave = PALMAS_BASE_TO_SLAVE ( PALMAS_INTERRUPT_BASE ) ;
addr = PALMAS_BASE_TO_REG ( PALMAS_INTERRUPT_BASE , PALMAS_INT_CTRL ) ;
reg = PALMAS_INT_CTRL_INT_CLEAR ;
regmap_write ( palmas - > regmap [ slave ] , addr , reg ) ;
ret = regmap_add_irq_chip ( palmas - > regmap [ slave ] , palmas - > irq ,
2013-03-01 18:43:46 +04:00
IRQF_ONESHOT | pdata - > irq_flags , 0 , & palmas_irq_chip ,
2012-05-15 10:48:56 +04:00
& palmas - > irq_data ) ;
if ( ret < 0 )
2013-09-26 17:33:51 +04:00
goto err_i2c ;
2012-05-15 10:48:56 +04:00
2013-06-19 09:57:47 +04:00
no_irq :
2012-05-15 10:48:56 +04:00
slave = PALMAS_BASE_TO_SLAVE ( PALMAS_PU_PD_OD_BASE ) ;
addr = PALMAS_BASE_TO_REG ( PALMAS_PU_PD_OD_BASE ,
PALMAS_PRIMARY_SECONDARY_PAD1 ) ;
if ( pdata - > mux_from_pdata ) {
reg = pdata - > pad1 ;
ret = regmap_write ( palmas - > regmap [ slave ] , addr , reg ) ;
if ( ret )
2012-08-28 15:47:35 +04:00
goto err_irq ;
2012-05-15 10:48:56 +04:00
} else {
ret = regmap_read ( palmas - > regmap [ slave ] , addr , & reg ) ;
if ( ret )
2012-08-28 15:47:35 +04:00
goto err_irq ;
2012-05-15 10:48:56 +04:00
}
if ( ! ( reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_0 ) )
palmas - > gpio_muxed | = PALMAS_GPIO_0_MUXED ;
if ( ! ( reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_MASK ) )
palmas - > gpio_muxed | = PALMAS_GPIO_1_MUXED ;
else if ( ( reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_MASK ) = =
( 2 < < PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_SHIFT ) )
palmas - > led_muxed | = PALMAS_LED1_MUXED ;
else if ( ( reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_MASK ) = =
( 3 < < PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_SHIFT ) )
palmas - > pwm_muxed | = PALMAS_PWM1_MUXED ;
if ( ! ( reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_2_MASK ) )
palmas - > gpio_muxed | = PALMAS_GPIO_2_MUXED ;
else if ( ( reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_2_MASK ) = =
( 2 < < PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_2_SHIFT ) )
palmas - > led_muxed | = PALMAS_LED2_MUXED ;
else if ( ( reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_2_MASK ) = =
( 3 < < PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_2_SHIFT ) )
palmas - > pwm_muxed | = PALMAS_PWM2_MUXED ;
if ( ! ( reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_3 ) )
palmas - > gpio_muxed | = PALMAS_GPIO_3_MUXED ;
addr = PALMAS_BASE_TO_REG ( PALMAS_PU_PD_OD_BASE ,
PALMAS_PRIMARY_SECONDARY_PAD2 ) ;
if ( pdata - > mux_from_pdata ) {
reg = pdata - > pad2 ;
ret = regmap_write ( palmas - > regmap [ slave ] , addr , reg ) ;
if ( ret )
2012-08-28 15:47:35 +04:00
goto err_irq ;
2012-05-15 10:48:56 +04:00
} else {
ret = regmap_read ( palmas - > regmap [ slave ] , addr , & reg ) ;
if ( ret )
2012-08-28 15:47:35 +04:00
goto err_irq ;
2012-05-15 10:48:56 +04:00
}
if ( ! ( reg & PALMAS_PRIMARY_SECONDARY_PAD2_GPIO_4 ) )
palmas - > gpio_muxed | = PALMAS_GPIO_4_MUXED ;
if ( ! ( reg & PALMAS_PRIMARY_SECONDARY_PAD2_GPIO_5_MASK ) )
palmas - > gpio_muxed | = PALMAS_GPIO_5_MUXED ;
if ( ! ( reg & PALMAS_PRIMARY_SECONDARY_PAD2_GPIO_6 ) )
palmas - > gpio_muxed | = PALMAS_GPIO_6_MUXED ;
if ( ! ( reg & PALMAS_PRIMARY_SECONDARY_PAD2_GPIO_7_MASK ) )
palmas - > gpio_muxed | = PALMAS_GPIO_7_MUXED ;
dev_info ( palmas - > dev , " Muxing GPIO %x, PWM %x, LED %x \n " ,
palmas - > gpio_muxed , palmas - > pwm_muxed ,
palmas - > led_muxed ) ;
reg = pdata - > power_ctrl ;
slave = PALMAS_BASE_TO_SLAVE ( PALMAS_PMU_CONTROL_BASE ) ;
addr = PALMAS_BASE_TO_REG ( PALMAS_PMU_CONTROL_BASE , PALMAS_POWER_CTRL ) ;
ret = regmap_write ( palmas - > regmap [ slave ] , addr , reg ) ;
if ( ret )
2012-08-28 15:47:35 +04:00
goto err_irq ;
2012-05-15 10:48:56 +04:00
2012-08-28 15:47:38 +04:00
/*
* If we are probing with DT do this the DT way and return here
* otherwise continue and add devices using mfd helpers .
*/
if ( node ) {
ret = of_platform_populate ( node , NULL , NULL , & i2c - > dev ) ;
2013-08-08 15:45:05 +04:00
if ( ret < 0 ) {
2012-08-28 15:47:38 +04:00
goto err_irq ;
2013-08-08 15:45:05 +04:00
} else if ( pdata - > pm_off & & ! pm_power_off ) {
palmas_dev = palmas ;
pm_power_off = palmas_power_off ;
}
2012-08-28 15:47:38 +04:00
}
2012-05-15 10:48:56 +04:00
return ret ;
2012-08-28 15:47:35 +04:00
err_irq :
regmap_del_irq_chip ( palmas - > irq , palmas - > irq_data ) ;
2013-09-26 17:33:51 +04:00
err_i2c :
for ( i = 1 ; i < PALMAS_NUM_CLIENTS ; i + + ) {
if ( palmas - > i2c_clients [ i ] )
i2c_unregister_device ( palmas - > i2c_clients [ i ] ) ;
}
2012-05-15 10:48:56 +04:00
return ret ;
}
static int palmas_i2c_remove ( struct i2c_client * i2c )
{
struct palmas * palmas = i2c_get_clientdata ( i2c ) ;
2013-09-26 17:33:51 +04:00
int i ;
2012-05-15 10:48:56 +04:00
regmap_del_irq_chip ( palmas - > irq , palmas - > irq_data ) ;
2013-09-26 17:33:51 +04:00
for ( i = 1 ; i < PALMAS_NUM_CLIENTS ; i + + ) {
if ( palmas - > i2c_clients [ i ] )
i2c_unregister_device ( palmas - > i2c_clients [ i ] ) ;
}
2013-09-26 17:33:50 +04:00
if ( palmas = = palmas_dev ) {
pm_power_off = NULL ;
palmas_dev = NULL ;
}
2012-05-15 10:48:56 +04:00
return 0 ;
}
static const struct i2c_device_id palmas_i2c_id [ ] = {
{ " palmas " , } ,
{ " twl6035 " , } ,
{ " twl6037 " , } ,
{ " tps65913 " , } ,
2012-05-21 19:33:22 +04:00
{ /* end */ }
2012-05-15 10:48:56 +04:00
} ;
MODULE_DEVICE_TABLE ( i2c , palmas_i2c_id ) ;
static struct i2c_driver palmas_i2c_driver = {
. driver = {
. name = " palmas " ,
. of_match_table = of_palmas_match_tbl ,
. owner = THIS_MODULE ,
} ,
. probe = palmas_i2c_probe ,
. remove = palmas_i2c_remove ,
. id_table = palmas_i2c_id ,
} ;
static int __init palmas_i2c_init ( void )
{
return i2c_add_driver ( & palmas_i2c_driver ) ;
}
/* init early so consumer devices can complete system boot */
subsys_initcall ( palmas_i2c_init ) ;
static void __exit palmas_i2c_exit ( void )
{
i2c_del_driver ( & palmas_i2c_driver ) ;
}
module_exit ( palmas_i2c_exit ) ;
MODULE_AUTHOR ( " Graeme Gregory <gg@slimlogic.co.uk> " ) ;
MODULE_DESCRIPTION ( " Palmas chip family multi-function driver " ) ;
MODULE_LICENSE ( " GPL " ) ;