2018-10-08 16:25:41 +03:00
// SPDX-License-Identifier: GPL-2.0
2018-10-19 15:44:38 +03:00
//
// Lochnagar regulator driver
//
// Copyright (c) 2017-2018 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
//
// Author: Charles Keepax <ckeepax@opensource.cirrus.com>
2018-10-08 16:25:41 +03:00
# include <linux/bitops.h>
# include <linux/device.h>
# include <linux/err.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/of.h>
2018-11-20 17:16:24 +03:00
# include <linux/of_device.h>
2018-10-08 16:25:41 +03:00
# include <linux/platform_device.h>
# include <linux/regmap.h>
# include <linux/regulator/driver.h>
# include <linux/regulator/machine.h>
# include <linux/regulator/of_regulator.h>
# include <linux/mfd/lochnagar.h>
2018-11-08 13:14:00 +03:00
# include <linux/mfd/lochnagar1_regs.h>
# include <linux/mfd/lochnagar2_regs.h>
2018-10-08 16:25:41 +03:00
static const struct regulator_ops lochnagar_micvdd_ops = {
. enable = regulator_enable_regmap ,
. disable = regulator_disable_regmap ,
. is_enabled = regulator_is_enabled_regmap ,
. list_voltage = regulator_list_voltage_linear_range ,
. map_voltage = regulator_map_voltage_linear_range ,
. get_voltage_sel = regulator_get_voltage_sel_regmap ,
. set_voltage_sel = regulator_set_voltage_sel_regmap ,
} ;
static const struct regulator_linear_range lochnagar_micvdd_ranges [ ] = {
REGULATOR_LINEAR_RANGE ( 1000000 , 0 , 0xC , 50000 ) ,
REGULATOR_LINEAR_RANGE ( 1700000 , 0xD , 0x1F , 100000 ) ,
} ;
static int lochnagar_micbias_enable ( struct regulator_dev * rdev )
{
struct lochnagar * lochnagar = rdev_get_drvdata ( rdev ) ;
int ret ;
mutex_lock ( & lochnagar - > analogue_config_lock ) ;
ret = regulator_enable_regmap ( rdev ) ;
if ( ret < 0 )
goto err ;
ret = lochnagar_update_config ( lochnagar ) ;
err :
mutex_unlock ( & lochnagar - > analogue_config_lock ) ;
return ret ;
}
static int lochnagar_micbias_disable ( struct regulator_dev * rdev )
{
struct lochnagar * lochnagar = rdev_get_drvdata ( rdev ) ;
int ret ;
mutex_lock ( & lochnagar - > analogue_config_lock ) ;
ret = regulator_disable_regmap ( rdev ) ;
if ( ret < 0 )
goto err ;
ret = lochnagar_update_config ( lochnagar ) ;
err :
mutex_unlock ( & lochnagar - > analogue_config_lock ) ;
return ret ;
}
static const struct regulator_ops lochnagar_micbias_ops = {
. enable = lochnagar_micbias_enable ,
. disable = lochnagar_micbias_disable ,
. is_enabled = regulator_is_enabled_regmap ,
} ;
static const struct regulator_ops lochnagar_vddcore_ops = {
. enable = regulator_enable_regmap ,
. disable = regulator_disable_regmap ,
. is_enabled = regulator_is_enabled_regmap ,
. list_voltage = regulator_list_voltage_linear_range ,
. map_voltage = regulator_map_voltage_linear_range ,
. get_voltage_sel = regulator_get_voltage_sel_regmap ,
. set_voltage_sel = regulator_set_voltage_sel_regmap ,
} ;
static const struct regulator_linear_range lochnagar_vddcore_ranges [ ] = {
REGULATOR_LINEAR_RANGE ( 600000 , 0x8 , 0x41 , 12500 ) ,
} ;
enum lochnagar_regulators {
LOCHNAGAR_MICVDD ,
LOCHNAGAR_MIC1VDD ,
LOCHNAGAR_MIC2VDD ,
LOCHNAGAR_VDDCORE ,
} ;
static int lochnagar_micbias_of_parse ( struct device_node * np ,
const struct regulator_desc * desc ,
struct regulator_config * config )
{
struct lochnagar * lochnagar = config - > driver_data ;
int shift = ( desc - > id - LOCHNAGAR_MIC1VDD ) *
LOCHNAGAR2_P2_MICBIAS_SRC_SHIFT ;
int mask = LOCHNAGAR2_P1_MICBIAS_SRC_MASK < < shift ;
unsigned int val ;
int ret ;
ret = of_property_read_u32 ( np , " cirrus,micbias-input " , & val ) ;
if ( ret > = 0 ) {
mutex_lock ( & lochnagar - > analogue_config_lock ) ;
ret = regmap_update_bits ( lochnagar - > regmap ,
LOCHNAGAR2_ANALOGUE_PATH_CTRL2 ,
mask , val < < shift ) ;
mutex_unlock ( & lochnagar - > analogue_config_lock ) ;
if ( ret < 0 ) {
dev_err ( lochnagar - > dev ,
" Failed to update micbias source: %d \n " , ret ) ;
return ret ;
}
}
return 0 ;
}
static const struct regulator_desc lochnagar_regulators [ ] = {
[ LOCHNAGAR_MICVDD ] = {
. name = " MICVDD " ,
. supply_name = " SYSVDD " ,
. type = REGULATOR_VOLTAGE ,
. n_voltages = 32 ,
. ops = & lochnagar_micvdd_ops ,
. id = LOCHNAGAR_MICVDD ,
. of_match = of_match_ptr ( " MICVDD " ) ,
. enable_reg = LOCHNAGAR2_MICVDD_CTRL1 ,
. enable_mask = LOCHNAGAR2_MICVDD_REG_ENA_MASK ,
. vsel_reg = LOCHNAGAR2_MICVDD_CTRL2 ,
. vsel_mask = LOCHNAGAR2_MICVDD_VSEL_MASK ,
. linear_ranges = lochnagar_micvdd_ranges ,
. n_linear_ranges = ARRAY_SIZE ( lochnagar_micvdd_ranges ) ,
. enable_time = 3000 ,
. ramp_delay = 1000 ,
. owner = THIS_MODULE ,
} ,
[ LOCHNAGAR_MIC1VDD ] = {
. name = " MIC1VDD " ,
. supply_name = " MICBIAS1 " ,
. type = REGULATOR_VOLTAGE ,
. ops = & lochnagar_micbias_ops ,
. id = LOCHNAGAR_MIC1VDD ,
. of_match = of_match_ptr ( " MIC1VDD " ) ,
. of_parse_cb = lochnagar_micbias_of_parse ,
. enable_reg = LOCHNAGAR2_ANALOGUE_PATH_CTRL2 ,
. enable_mask = LOCHNAGAR2_P1_INPUT_BIAS_ENA_MASK ,
. owner = THIS_MODULE ,
} ,
[ LOCHNAGAR_MIC2VDD ] = {
. name = " MIC2VDD " ,
. supply_name = " MICBIAS2 " ,
. type = REGULATOR_VOLTAGE ,
. ops = & lochnagar_micbias_ops ,
. id = LOCHNAGAR_MIC2VDD ,
. of_match = of_match_ptr ( " MIC2VDD " ) ,
. of_parse_cb = lochnagar_micbias_of_parse ,
. enable_reg = LOCHNAGAR2_ANALOGUE_PATH_CTRL2 ,
. enable_mask = LOCHNAGAR2_P2_INPUT_BIAS_ENA_MASK ,
. owner = THIS_MODULE ,
} ,
[ LOCHNAGAR_VDDCORE ] = {
. name = " VDDCORE " ,
. supply_name = " SYSVDD " ,
. type = REGULATOR_VOLTAGE ,
. n_voltages = 57 ,
. ops = & lochnagar_vddcore_ops ,
. id = LOCHNAGAR_VDDCORE ,
. of_match = of_match_ptr ( " VDDCORE " ) ,
. enable_reg = LOCHNAGAR2_VDDCORE_CDC_CTRL1 ,
. enable_mask = LOCHNAGAR2_VDDCORE_CDC_REG_ENA_MASK ,
. vsel_reg = LOCHNAGAR2_VDDCORE_CDC_CTRL2 ,
. vsel_mask = LOCHNAGAR2_VDDCORE_CDC_VSEL_MASK ,
. linear_ranges = lochnagar_vddcore_ranges ,
. n_linear_ranges = ARRAY_SIZE ( lochnagar_vddcore_ranges ) ,
. enable_time = 3000 ,
. ramp_delay = 1000 ,
. owner = THIS_MODULE ,
} ,
} ;
2018-11-20 17:16:24 +03:00
static const struct of_device_id lochnagar_of_match [ ] = {
{
. compatible = " cirrus,lochnagar2-micvdd " ,
. data = & lochnagar_regulators [ LOCHNAGAR_MICVDD ] ,
} ,
{
. compatible = " cirrus,lochnagar2-mic1vdd " ,
. data = & lochnagar_regulators [ LOCHNAGAR_MIC1VDD ] ,
} ,
{
. compatible = " cirrus,lochnagar2-mic2vdd " ,
2018-12-29 14:40:47 +03:00
. data = & lochnagar_regulators [ LOCHNAGAR_MIC2VDD ] ,
2018-11-20 17:16:24 +03:00
} ,
{
. compatible = " cirrus,lochnagar2-vddcore " ,
. data = & lochnagar_regulators [ LOCHNAGAR_VDDCORE ] ,
} ,
{ } ,
} ;
2018-10-08 16:25:41 +03:00
static int lochnagar_regulator_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct lochnagar * lochnagar = dev_get_drvdata ( dev - > parent ) ;
struct regulator_config config = { } ;
2018-11-20 17:16:24 +03:00
const struct of_device_id * of_id ;
const struct regulator_desc * desc ;
2018-10-08 16:25:41 +03:00
struct regulator_dev * rdev ;
2018-11-20 17:16:24 +03:00
int ret ;
2018-10-08 16:25:41 +03:00
2018-11-20 17:16:24 +03:00
config . dev = dev ;
2018-10-08 16:25:41 +03:00
config . regmap = lochnagar - > regmap ;
config . driver_data = lochnagar ;
2018-11-20 17:16:24 +03:00
of_id = of_match_device ( lochnagar_of_match , dev ) ;
if ( ! of_id )
return - EINVAL ;
2018-10-08 16:25:41 +03:00
2018-11-20 17:16:24 +03:00
desc = of_id - > data ;
rdev = devm_regulator_register ( dev , desc , & config ) ;
if ( IS_ERR ( rdev ) ) {
ret = PTR_ERR ( rdev ) ;
dev_err ( dev , " Failed to register %s regulator: %d \n " ,
desc - > name , ret ) ;
return ret ;
2018-10-08 16:25:41 +03:00
}
return 0 ;
}
static struct platform_driver lochnagar_regulator_driver = {
. driver = {
. name = " lochnagar-regulator " ,
2018-11-20 17:16:24 +03:00
. of_match_table = of_match_ptr ( lochnagar_of_match ) ,
2018-10-08 16:25:41 +03:00
} ,
. probe = lochnagar_regulator_probe ,
} ;
module_platform_driver ( lochnagar_regulator_driver ) ;
MODULE_AUTHOR ( " Charles Keepax <ckeepax@opensource.cirrus.com> " ) ;
MODULE_DESCRIPTION ( " Regulator driver for Cirrus Logic Lochnagar Board " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform:lochnagar-regulator " ) ;