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 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 linear_range lochnagar_vddcore_ranges
[] = {
101 REGULATOR_LINEAR_RANGE(600000, 0, 0x7, 0),
102 REGULATOR_LINEAR_RANGE(600000, 0x8, 0x41, 12500),
105 enum lochnagar_regulators
{
112 static int lochnagar_micbias_of_parse(struct device_node
*np
,
113 const struct regulator_desc
*desc
,
114 struct regulator_config
*config
)
116 struct lochnagar
*lochnagar
= config
->driver_data
;
117 int shift
= (desc
->id
- LOCHNAGAR_MIC1VDD
) *
118 LOCHNAGAR2_P2_MICBIAS_SRC_SHIFT
;
119 int mask
= LOCHNAGAR2_P1_MICBIAS_SRC_MASK
<< shift
;
123 ret
= of_property_read_u32(np
, "cirrus,micbias-input", &val
);
125 mutex_lock(&lochnagar
->analogue_config_lock
);
126 ret
= regmap_update_bits(lochnagar
->regmap
,
127 LOCHNAGAR2_ANALOGUE_PATH_CTRL2
,
129 mutex_unlock(&lochnagar
->analogue_config_lock
);
131 dev_err(lochnagar
->dev
,
132 "Failed to update micbias source: %d\n", ret
);
140 static const struct regulator_desc lochnagar_regulators
[] = {
141 [LOCHNAGAR_MICVDD
] = {
143 .supply_name
= "SYSVDD",
144 .type
= REGULATOR_VOLTAGE
,
146 .ops
= &lochnagar_micvdd_ops
,
148 .id
= LOCHNAGAR_MICVDD
,
149 .of_match
= of_match_ptr("MICVDD"),
151 .enable_reg
= LOCHNAGAR2_MICVDD_CTRL1
,
152 .enable_mask
= LOCHNAGAR2_MICVDD_REG_ENA_MASK
,
153 .vsel_reg
= LOCHNAGAR2_MICVDD_CTRL2
,
154 .vsel_mask
= LOCHNAGAR2_MICVDD_VSEL_MASK
,
156 .linear_ranges
= lochnagar_micvdd_ranges
,
157 .n_linear_ranges
= ARRAY_SIZE(lochnagar_micvdd_ranges
),
162 .owner
= THIS_MODULE
,
164 [LOCHNAGAR_MIC1VDD
] = {
166 .supply_name
= "MICBIAS1",
167 .type
= REGULATOR_VOLTAGE
,
168 .ops
= &lochnagar_micbias_ops
,
170 .id
= LOCHNAGAR_MIC1VDD
,
171 .of_match
= of_match_ptr("MIC1VDD"),
172 .of_parse_cb
= lochnagar_micbias_of_parse
,
174 .enable_reg
= LOCHNAGAR2_ANALOGUE_PATH_CTRL2
,
175 .enable_mask
= LOCHNAGAR2_P1_INPUT_BIAS_ENA_MASK
,
177 .owner
= THIS_MODULE
,
179 [LOCHNAGAR_MIC2VDD
] = {
181 .supply_name
= "MICBIAS2",
182 .type
= REGULATOR_VOLTAGE
,
183 .ops
= &lochnagar_micbias_ops
,
185 .id
= LOCHNAGAR_MIC2VDD
,
186 .of_match
= of_match_ptr("MIC2VDD"),
187 .of_parse_cb
= lochnagar_micbias_of_parse
,
189 .enable_reg
= LOCHNAGAR2_ANALOGUE_PATH_CTRL2
,
190 .enable_mask
= LOCHNAGAR2_P2_INPUT_BIAS_ENA_MASK
,
192 .owner
= THIS_MODULE
,
194 [LOCHNAGAR_VDDCORE
] = {
196 .supply_name
= "SYSVDD",
197 .type
= REGULATOR_VOLTAGE
,
199 .ops
= &lochnagar_vddcore_ops
,
201 .id
= LOCHNAGAR_VDDCORE
,
202 .of_match
= of_match_ptr("VDDCORE"),
204 .enable_reg
= LOCHNAGAR2_VDDCORE_CDC_CTRL1
,
205 .enable_mask
= LOCHNAGAR2_VDDCORE_CDC_REG_ENA_MASK
,
206 .vsel_reg
= LOCHNAGAR2_VDDCORE_CDC_CTRL2
,
207 .vsel_mask
= LOCHNAGAR2_VDDCORE_CDC_VSEL_MASK
,
209 .linear_ranges
= lochnagar_vddcore_ranges
,
210 .n_linear_ranges
= ARRAY_SIZE(lochnagar_vddcore_ranges
),
214 .off_on_delay
= 15000,
216 .owner
= THIS_MODULE
,
220 static const struct of_device_id lochnagar_of_match
[] = {
222 .compatible
= "cirrus,lochnagar2-micvdd",
223 .data
= &lochnagar_regulators
[LOCHNAGAR_MICVDD
],
226 .compatible
= "cirrus,lochnagar2-mic1vdd",
227 .data
= &lochnagar_regulators
[LOCHNAGAR_MIC1VDD
],
230 .compatible
= "cirrus,lochnagar2-mic2vdd",
231 .data
= &lochnagar_regulators
[LOCHNAGAR_MIC2VDD
],
234 .compatible
= "cirrus,lochnagar2-vddcore",
235 .data
= &lochnagar_regulators
[LOCHNAGAR_VDDCORE
],
239 MODULE_DEVICE_TABLE(of
, lochnagar_of_match
);
241 static int lochnagar_regulator_probe(struct platform_device
*pdev
)
243 struct device
*dev
= &pdev
->dev
;
244 struct lochnagar
*lochnagar
= dev_get_drvdata(dev
->parent
);
245 struct regulator_config config
= { };
246 const struct of_device_id
*of_id
;
247 const struct regulator_desc
*desc
;
248 struct regulator_dev
*rdev
;
252 config
.regmap
= lochnagar
->regmap
;
253 config
.driver_data
= lochnagar
;
255 of_id
= of_match_device(lochnagar_of_match
, dev
);
261 rdev
= devm_regulator_register(dev
, desc
, &config
);
264 dev_err(dev
, "Failed to register %s regulator: %d\n",
272 static struct platform_driver lochnagar_regulator_driver
= {
274 .name
= "lochnagar-regulator",
275 .of_match_table
= of_match_ptr(lochnagar_of_match
),
278 .probe
= lochnagar_regulator_probe
,
280 module_platform_driver(lochnagar_regulator_driver
);
282 MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");
283 MODULE_DESCRIPTION("Regulator driver for Cirrus Logic Lochnagar Board");
284 MODULE_LICENSE("GPL v2");
285 MODULE_ALIAS("platform:lochnagar-regulator");