2022-01-07 01:43:50 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
//
// max20086-regulator.c - MAX20086-MAX20089 camera power protector driver
//
// Copyright (C) 2022 Laurent Pinchart <laurent.pinchart@idesonboard.com>
// Copyright (C) 2018 Avnet, Inc.
# include <linux/err.h>
2022-01-15 06:36:03 +03:00
# include <linux/gpio/consumer.h>
2022-01-07 01:43:50 +03:00
# include <linux/i2c.h>
# include <linux/module.h>
# include <linux/regmap.h>
# include <linux/regulator/driver.h>
# include <linux/regulator/machine.h>
# include <linux/regulator/of_regulator.h>
# include <linux/slab.h>
/* Register Offset */
# define MAX20086_REG_MASK 0x00
# define MAX20086_REG_CONFIG 0x01
# define MAX20086_REG_ID 0x02
# define MAX20086_REG_STAT1 0x03
# define MAX20086_REG_STAT2_L 0x04
# define MAX20086_REG_STAT2_H 0x05
# define MAX20086_REG_ADC1 0x06
# define MAX20086_REG_ADC2 0x07
# define MAX20086_REG_ADC3 0x08
# define MAX20086_REG_ADC4 0x09
/* DEVICE IDs */
# define MAX20086_DEVICE_ID_MAX20086 0x40
# define MAX20086_DEVICE_ID_MAX20087 0x20
# define MAX20086_DEVICE_ID_MAX20088 0x10
# define MAX20086_DEVICE_ID_MAX20089 0x00
# define DEVICE_ID_MASK 0xf0
/* Register bits */
# define MAX20086_EN_MASK 0x0f
# define MAX20086_EN_OUT1 0x01
# define MAX20086_EN_OUT2 0x02
# define MAX20086_EN_OUT3 0x04
# define MAX20086_EN_OUT4 0x08
# define MAX20086_INT_DISABLE_ALL 0x3f
# define MAX20086_MAX_REGULATORS 4
struct max20086_chip_info {
u8 id ;
unsigned int num_outputs ;
} ;
struct max20086_regulator {
struct device_node * of_node ;
struct regulator_init_data * init_data ;
const struct regulator_desc * desc ;
struct regulator_dev * rdev ;
} ;
struct max20086 {
struct device * dev ;
struct regmap * regmap ;
struct gpio_desc * ena_gpiod ;
const struct max20086_chip_info * info ;
struct max20086_regulator regulators [ MAX20086_MAX_REGULATORS ] ;
} ;
static const struct regulator_ops max20086_buck_ops = {
. enable = regulator_enable_regmap ,
. disable = regulator_disable_regmap ,
. is_enabled = regulator_is_enabled_regmap ,
} ;
# define MAX20086_REGULATOR_DESC(n) \
{ \
. name = " OUT " # n , \
. supply_name = " in " , \
. id = ( n ) - 1 , \
. ops = & max20086_buck_ops , \
. type = REGULATOR_VOLTAGE , \
. owner = THIS_MODULE , \
. enable_reg = MAX20086_REG_CONFIG , \
. enable_mask = 1 < < ( ( n ) - 1 ) , \
. enable_val = 1 < < ( ( n ) - 1 ) , \
. disable_val = 0 , \
}
static const char * const max20086_output_names [ ] = {
" OUT1 " ,
" OUT2 " ,
" OUT3 " ,
" OUT4 " ,
} ;
static const struct regulator_desc max20086_regulators [ ] = {
MAX20086_REGULATOR_DESC ( 1 ) ,
MAX20086_REGULATOR_DESC ( 2 ) ,
MAX20086_REGULATOR_DESC ( 3 ) ,
MAX20086_REGULATOR_DESC ( 4 ) ,
} ;
static int max20086_regulators_register ( struct max20086 * chip )
{
unsigned int i ;
for ( i = 0 ; i < chip - > info - > num_outputs ; i + + ) {
struct max20086_regulator * reg = & chip - > regulators [ i ] ;
struct regulator_config config = { } ;
struct regulator_dev * rdev ;
config . dev = chip - > dev ;
config . init_data = reg - > init_data ;
config . driver_data = chip ;
config . of_node = reg - > of_node ;
config . regmap = chip - > regmap ;
config . ena_gpiod = chip - > ena_gpiod ;
rdev = devm_regulator_register ( chip - > dev , reg - > desc , & config ) ;
if ( IS_ERR ( rdev ) ) {
dev_err ( chip - > dev ,
" Failed to register regulator output %s \n " ,
reg - > desc - > name ) ;
return PTR_ERR ( rdev ) ;
}
reg - > rdev = rdev ;
}
return 0 ;
}
static int max20086_parse_regulators_dt ( struct max20086 * chip , bool * boot_on )
{
struct of_regulator_match matches [ MAX20086_MAX_REGULATORS ] = { } ;
struct device_node * node ;
unsigned int i ;
int ret ;
node = of_get_child_by_name ( chip - > dev - > of_node , " regulators " ) ;
if ( ! node ) {
dev_err ( chip - > dev , " regulators node not found \n " ) ;
2022-01-11 10:26:58 +03:00
return - ENODEV ;
2022-01-07 01:43:50 +03:00
}
for ( i = 0 ; i < chip - > info - > num_outputs ; + + i )
matches [ i ] . name = max20086_output_names [ i ] ;
ret = of_regulator_match ( chip - > dev , node , matches ,
chip - > info - > num_outputs ) ;
of_node_put ( node ) ;
if ( ret < 0 ) {
dev_err ( chip - > dev , " Failed to match regulators \n " ) ;
return - EINVAL ;
}
* boot_on = false ;
for ( i = 0 ; i < chip - > info - > num_outputs ; i + + ) {
struct max20086_regulator * reg = & chip - > regulators [ i ] ;
reg - > init_data = matches [ i ] . init_data ;
reg - > of_node = matches [ i ] . of_node ;
reg - > desc = & max20086_regulators [ i ] ;
if ( reg - > init_data ) {
if ( reg - > init_data - > constraints . always_on | |
reg - > init_data - > constraints . boot_on )
* boot_on = true ;
}
}
return 0 ;
}
static int max20086_detect ( struct max20086 * chip )
{
unsigned int data ;
int ret ;
ret = regmap_read ( chip - > regmap , MAX20086_REG_ID , & data ) ;
if ( ret < 0 ) {
dev_err ( chip - > dev , " Failed to read DEVICE_ID reg: %d \n " , ret ) ;
return ret ;
}
if ( ( data & DEVICE_ID_MASK ) ! = chip - > info - > id ) {
dev_err ( chip - > dev , " Invalid device ID 0x%02x \n " , data ) ;
return - ENXIO ;
}
return 0 ;
}
static bool max20086_gen_is_writeable_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case MAX20086_REG_MASK :
case MAX20086_REG_CONFIG :
return true ;
default :
return false ;
}
}
static const struct regmap_config max20086_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
. writeable_reg = max20086_gen_is_writeable_reg ,
. max_register = 0x9 ,
. cache_type = REGCACHE_NONE ,
} ;
static int max20086_i2c_probe ( struct i2c_client * i2c )
{
struct max20086 * chip ;
enum gpiod_flags flags ;
bool boot_on ;
int ret ;
chip = devm_kzalloc ( & i2c - > dev , sizeof ( * chip ) , GFP_KERNEL ) ;
if ( ! chip )
return - ENOMEM ;
chip - > dev = & i2c - > dev ;
2023-09-03 18:42:57 +03:00
chip - > info = i2c_get_match_data ( i2c ) ;
2022-01-07 01:43:50 +03:00
i2c_set_clientdata ( i2c , chip ) ;
chip - > regmap = devm_regmap_init_i2c ( i2c , & max20086_regmap_config ) ;
if ( IS_ERR ( chip - > regmap ) ) {
ret = PTR_ERR ( chip - > regmap ) ;
dev_err ( chip - > dev , " Failed to allocate register map: %d \n " , ret ) ;
return ret ;
}
ret = max20086_parse_regulators_dt ( chip , & boot_on ) ;
if ( ret < 0 )
return ret ;
ret = max20086_detect ( chip ) ;
if ( ret < 0 )
return ret ;
/* Until IRQ support is added, just disable all interrupts. */
ret = regmap_update_bits ( chip - > regmap , MAX20086_REG_MASK ,
MAX20086_INT_DISABLE_ALL ,
MAX20086_INT_DISABLE_ALL ) ;
if ( ret < 0 ) {
dev_err ( chip - > dev , " Failed to disable interrupts: %d \n " , ret ) ;
return ret ;
}
/*
* Get the enable GPIO . If any of the outputs is marked as being
* enabled at boot , request the GPIO with an initial high state to
* avoid disabling outputs that may have been turned on by the boot
* loader . Otherwise , request it with a low state to enter lower - power
* shutdown .
*/
flags = boot_on ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW ;
chip - > ena_gpiod = devm_gpiod_get ( chip - > dev , " enable " , flags ) ;
if ( IS_ERR ( chip - > ena_gpiod ) ) {
ret = PTR_ERR ( chip - > ena_gpiod ) ;
dev_err ( chip - > dev , " Failed to get enable GPIO: %d \n " , ret ) ;
return ret ;
}
ret = max20086_regulators_register ( chip ) ;
if ( ret < 0 ) {
dev_err ( chip - > dev , " Failed to register regulators: %d \n " , ret ) ;
return ret ;
}
return 0 ;
}
2023-09-03 18:42:57 +03:00
static const struct max20086_chip_info max20086_chip_info = {
. id = MAX20086_DEVICE_ID_MAX20086 ,
. num_outputs = 4 ,
} ;
static const struct max20086_chip_info max20087_chip_info = {
. id = MAX20086_DEVICE_ID_MAX20087 ,
. num_outputs = 4 ,
} ;
static const struct max20086_chip_info max20088_chip_info = {
. id = MAX20086_DEVICE_ID_MAX20088 ,
. num_outputs = 2 ,
} ;
static const struct max20086_chip_info max20089_chip_info = {
. id = MAX20086_DEVICE_ID_MAX20089 ,
. num_outputs = 2 ,
2022-01-07 01:43:50 +03:00
} ;
2023-09-03 18:42:57 +03:00
static const struct i2c_device_id max20086_i2c_id [ ] = {
{ " max20086 " , ( kernel_ulong_t ) & max20086_chip_info } ,
{ " max20087 " , ( kernel_ulong_t ) & max20087_chip_info } ,
{ " max20088 " , ( kernel_ulong_t ) & max20088_chip_info } ,
{ " max20089 " , ( kernel_ulong_t ) & max20089_chip_info } ,
{ /* Sentinel */ }
} ;
2022-01-07 01:43:50 +03:00
MODULE_DEVICE_TABLE ( i2c , max20086_i2c_id ) ;
2023-03-11 00:45:46 +03:00
static const struct of_device_id max20086_dt_ids [ ] __maybe_unused = {
2023-09-03 18:42:57 +03:00
{ . compatible = " maxim,max20086 " , . data = & max20086_chip_info } ,
{ . compatible = " maxim,max20087 " , . data = & max20087_chip_info } ,
{ . compatible = " maxim,max20088 " , . data = & max20088_chip_info } ,
{ . compatible = " maxim,max20089 " , . data = & max20089_chip_info } ,
{ /* Sentinel */ }
2022-01-07 01:43:50 +03:00
} ;
MODULE_DEVICE_TABLE ( of , max20086_dt_ids ) ;
static struct i2c_driver max20086_regulator_driver = {
. driver = {
. name = " max20086 " ,
2023-03-16 22:54:43 +03:00
. probe_type = PROBE_PREFER_ASYNCHRONOUS ,
2022-01-07 01:43:50 +03:00
. of_match_table = of_match_ptr ( max20086_dt_ids ) ,
} ,
2023-05-06 01:02:18 +03:00
. probe = max20086_i2c_probe ,
2022-01-07 01:43:50 +03:00
. id_table = max20086_i2c_id ,
} ;
module_i2c_driver ( max20086_regulator_driver ) ;
MODULE_AUTHOR ( " Watson Chow <watson.chow@avnet.com> " ) ;
MODULE_DESCRIPTION ( " MAX20086-MAX20089 Camera Power Protector Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;