mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
285 lines
7.2 KiB
285 lines
7.2 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
// |
|
// Lochnagar regulator driver |
|
// |
|
// Copyright (c) 2017-2018 Cirrus Logic, Inc. and |
|
// Cirrus Logic International Semiconductor Ltd. |
|
// |
|
// Author: Charles Keepax <[email protected]> |
|
|
|
#include <linux/bitops.h> |
|
#include <linux/device.h> |
|
#include <linux/err.h> |
|
#include <linux/module.h> |
|
#include <linux/mutex.h> |
|
#include <linux/of.h> |
|
#include <linux/of_device.h> |
|
#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> |
|
#include <linux/mfd/lochnagar1_regs.h> |
|
#include <linux/mfd/lochnagar2_regs.h> |
|
|
|
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 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 linear_range lochnagar_vddcore_ranges[] = { |
|
REGULATOR_LINEAR_RANGE(600000, 0, 0x7, 0), |
|
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 = 66, |
|
.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, |
|
.off_on_delay = 15000, |
|
|
|
.owner = THIS_MODULE, |
|
}, |
|
}; |
|
|
|
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", |
|
.data = &lochnagar_regulators[LOCHNAGAR_MIC2VDD], |
|
}, |
|
{ |
|
.compatible = "cirrus,lochnagar2-vddcore", |
|
.data = &lochnagar_regulators[LOCHNAGAR_VDDCORE], |
|
}, |
|
{} |
|
}; |
|
MODULE_DEVICE_TABLE(of, lochnagar_of_match); |
|
|
|
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 = { }; |
|
const struct of_device_id *of_id; |
|
const struct regulator_desc *desc; |
|
struct regulator_dev *rdev; |
|
int ret; |
|
|
|
config.dev = dev; |
|
config.regmap = lochnagar->regmap; |
|
config.driver_data = lochnagar; |
|
|
|
of_id = of_match_device(lochnagar_of_match, dev); |
|
if (!of_id) |
|
return -EINVAL; |
|
|
|
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; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static struct platform_driver lochnagar_regulator_driver = { |
|
.driver = { |
|
.name = "lochnagar-regulator", |
|
.of_match_table = of_match_ptr(lochnagar_of_match), |
|
}, |
|
|
|
.probe = lochnagar_regulator_probe, |
|
}; |
|
module_platform_driver(lochnagar_regulator_driver); |
|
|
|
MODULE_AUTHOR("Charles Keepax <[email protected]>"); |
|
MODULE_DESCRIPTION("Regulator driver for Cirrus Logic Lochnagar Board"); |
|
MODULE_LICENSE("GPL v2"); |
|
MODULE_ALIAS("platform:lochnagar-regulator");
|
|
|