1 // SPDX-License-Identifier: GPL-2.0
3 // Lochnagar regulator driver
5 // Copyright (c) 2017-2018 Cirrus Logic, Inc. and
6 // Cirrus Logic International Semiconductor Ltd.
8 // Author: Charles Keepax <ckeepax@opensource.cirrus.com>
10 #include <linux/bitops.h>
11 #include <linux/device.h>
12 #include <linux/err.h>
13 #include <linux/module.h>
14 #include <linux/mutex.h>
16 #include <linux/of_device.h>
17 #include <linux/platform_device.h>
18 #include <linux/regmap.h>
19 #include <linux/regulator/driver.h>
20 #include <linux/regulator/machine.h>
21 #include <linux/regulator/of_regulator.h>
23 #include <linux/mfd/lochnagar.h>
24 #include <linux/mfd/lochnagar1_regs.h>
25 #include <linux/mfd/lochnagar2_regs.h>
27 static const struct regulator_ops lochnagar_micvdd_ops
= {
28 .enable
= regulator_enable_regmap
,
29 .disable
= regulator_disable_regmap
,
30 .is_enabled
= regulator_is_enabled_regmap
,
32 .list_voltage
= regulator_list_voltage_linear_range
,
33 .map_voltage
= regulator_map_voltage_linear_range
,
35 .get_voltage_sel
= regulator_get_voltage_sel_regmap
,
36 .set_voltage_sel
= regulator_set_voltage_sel_regmap
,
39 static const struct regulator_linear_range lochnagar_micvdd_ranges
[] = {
40 REGULATOR_LINEAR_RANGE(1000000, 0, 0xC, 50000),
41 REGULATOR_LINEAR_RANGE(1700000, 0xD, 0x1F, 100000),
44 static int lochnagar_micbias_enable(struct regulator_dev
*rdev
)
46 struct lochnagar
*lochnagar
= rdev_get_drvdata(rdev
);
49 mutex_lock(&lochnagar
->analogue_config_lock
);
51 ret
= regulator_enable_regmap(rdev
);
55 ret
= lochnagar_update_config(lochnagar
);
58 mutex_unlock(&lochnagar
->analogue_config_lock
);
63 static int lochnagar_micbias_disable(struct regulator_dev
*rdev
)
65 struct lochnagar
*lochnagar
= rdev_get_drvdata(rdev
);
68 mutex_lock(&lochnagar
->analogue_config_lock
);
70 ret
= regulator_disable_regmap(rdev
);
74 ret
= lochnagar_update_config(lochnagar
);
77 mutex_unlock(&lochnagar
->analogue_config_lock
);
82 static const struct regulator_ops lochnagar_micbias_ops
= {
83 .enable
= lochnagar_micbias_enable
,
84 .disable
= lochnagar_micbias_disable
,
85 .is_enabled
= regulator_is_enabled_regmap
,
88 static const struct regulator_ops lochnagar_vddcore_ops
= {
89 .enable
= regulator_enable_regmap
,
90 .disable
= regulator_disable_regmap
,
91 .is_enabled
= regulator_is_enabled_regmap
,
93 .list_voltage
= regulator_list_voltage_linear_range
,
94 .map_voltage
= regulator_map_voltage_linear_range
,
96 .get_voltage_sel
= regulator_get_voltage_sel_regmap
,
97 .set_voltage_sel
= regulator_set_voltage_sel_regmap
,
100 static const struct regulator_linear_range lochnagar_vddcore_ranges
[] = {
101 REGULATOR_LINEAR_RANGE(600000, 0x8, 0x41, 12500),
104 enum lochnagar_regulators
{
111 static int lochnagar_micbias_of_parse(struct device_node
*np
,
112 const struct regulator_desc
*desc
,
113 struct regulator_config
*config
)
115 struct lochnagar
*lochnagar
= config
->driver_data
;
116 int shift
= (desc
->id
- LOCHNAGAR_MIC1VDD
) *
117 LOCHNAGAR2_P2_MICBIAS_SRC_SHIFT
;
118 int mask
= LOCHNAGAR2_P1_MICBIAS_SRC_MASK
<< shift
;
122 ret
= of_property_read_u32(np
, "cirrus,micbias-input", &val
);
124 mutex_lock(&lochnagar
->analogue_config_lock
);
125 ret
= regmap_update_bits(lochnagar
->regmap
,
126 LOCHNAGAR2_ANALOGUE_PATH_CTRL2
,
128 mutex_unlock(&lochnagar
->analogue_config_lock
);
130 dev_err(lochnagar
->dev
,
131 "Failed to update micbias source: %d\n", ret
);
139 static const struct regulator_desc lochnagar_regulators
[] = {
140 [LOCHNAGAR_MICVDD
] = {
142 .supply_name
= "SYSVDD",
143 .type
= REGULATOR_VOLTAGE
,
145 .ops
= &lochnagar_micvdd_ops
,
147 .id
= LOCHNAGAR_MICVDD
,
148 .of_match
= of_match_ptr("MICVDD"),
150 .enable_reg
= LOCHNAGAR2_MICVDD_CTRL1
,
151 .enable_mask
= LOCHNAGAR2_MICVDD_REG_ENA_MASK
,
152 .vsel_reg
= LOCHNAGAR2_MICVDD_CTRL2
,
153 .vsel_mask
= LOCHNAGAR2_MICVDD_VSEL_MASK
,
155 .linear_ranges
= lochnagar_micvdd_ranges
,
156 .n_linear_ranges
= ARRAY_SIZE(lochnagar_micvdd_ranges
),
161 .owner
= THIS_MODULE
,
163 [LOCHNAGAR_MIC1VDD
] = {
165 .supply_name
= "MICBIAS1",
166 .type
= REGULATOR_VOLTAGE
,
167 .ops
= &lochnagar_micbias_ops
,
169 .id
= LOCHNAGAR_MIC1VDD
,
170 .of_match
= of_match_ptr("MIC1VDD"),
171 .of_parse_cb
= lochnagar_micbias_of_parse
,
173 .enable_reg
= LOCHNAGAR2_ANALOGUE_PATH_CTRL2
,
174 .enable_mask
= LOCHNAGAR2_P1_INPUT_BIAS_ENA_MASK
,
176 .owner
= THIS_MODULE
,
178 [LOCHNAGAR_MIC2VDD
] = {
180 .supply_name
= "MICBIAS2",
181 .type
= REGULATOR_VOLTAGE
,
182 .ops
= &lochnagar_micbias_ops
,
184 .id
= LOCHNAGAR_MIC2VDD
,
185 .of_match
= of_match_ptr("MIC2VDD"),
186 .of_parse_cb
= lochnagar_micbias_of_parse
,
188 .enable_reg
= LOCHNAGAR2_ANALOGUE_PATH_CTRL2
,
189 .enable_mask
= LOCHNAGAR2_P2_INPUT_BIAS_ENA_MASK
,
191 .owner
= THIS_MODULE
,
193 [LOCHNAGAR_VDDCORE
] = {
195 .supply_name
= "SYSVDD",
196 .type
= REGULATOR_VOLTAGE
,
198 .ops
= &lochnagar_vddcore_ops
,
200 .id
= LOCHNAGAR_VDDCORE
,
201 .of_match
= of_match_ptr("VDDCORE"),
203 .enable_reg
= LOCHNAGAR2_VDDCORE_CDC_CTRL1
,
204 .enable_mask
= LOCHNAGAR2_VDDCORE_CDC_REG_ENA_MASK
,
205 .vsel_reg
= LOCHNAGAR2_VDDCORE_CDC_CTRL2
,
206 .vsel_mask
= LOCHNAGAR2_VDDCORE_CDC_VSEL_MASK
,
208 .linear_ranges
= lochnagar_vddcore_ranges
,
209 .n_linear_ranges
= ARRAY_SIZE(lochnagar_vddcore_ranges
),
214 .owner
= THIS_MODULE
,
218 static const struct of_device_id lochnagar_of_match
[] = {
220 .compatible
= "cirrus,lochnagar2-micvdd",
221 .data
= &lochnagar_regulators
[LOCHNAGAR_MICVDD
],
224 .compatible
= "cirrus,lochnagar2-mic1vdd",
225 .data
= &lochnagar_regulators
[LOCHNAGAR_MIC1VDD
],
228 .compatible
= "cirrus,lochnagar2-mic2vdd",
229 .data
= &lochnagar_regulators
[LOCHNAGAR_MIC2VDD
],
232 .compatible
= "cirrus,lochnagar2-vddcore",
233 .data
= &lochnagar_regulators
[LOCHNAGAR_VDDCORE
],
237 MODULE_DEVICE_TABLE(of
, lochnagar_of_match
);
239 static int lochnagar_regulator_probe(struct platform_device
*pdev
)
241 struct device
*dev
= &pdev
->dev
;
242 struct lochnagar
*lochnagar
= dev_get_drvdata(dev
->parent
);
243 struct regulator_config config
= { };
244 const struct of_device_id
*of_id
;
245 const struct regulator_desc
*desc
;
246 struct regulator_dev
*rdev
;
250 config
.regmap
= lochnagar
->regmap
;
251 config
.driver_data
= lochnagar
;
253 of_id
= of_match_device(lochnagar_of_match
, dev
);
259 rdev
= devm_regulator_register(dev
, desc
, &config
);
262 dev_err(dev
, "Failed to register %s regulator: %d\n",
270 static struct platform_driver lochnagar_regulator_driver
= {
272 .name
= "lochnagar-regulator",
273 .of_match_table
= of_match_ptr(lochnagar_of_match
),
276 .probe
= lochnagar_regulator_probe
,
278 module_platform_driver(lochnagar_regulator_driver
);
280 MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");
281 MODULE_DESCRIPTION("Regulator driver for Cirrus Logic Lochnagar Board");
282 MODULE_LICENSE("GPL v2");
283 MODULE_ALIAS("platform:lochnagar-regulator");