Merge tag 'drm-next-2018-11-02' of git://anongit.freedesktop.org/drm/drm
[linux-2.6-block.git] / drivers / regulator / lochnagar-regulator.c
CommitLineData
bef9391c 1// SPDX-License-Identifier: GPL-2.0
d8b2a8e9
CK
2//
3// Lochnagar regulator driver
4//
5// Copyright (c) 2017-2018 Cirrus Logic, Inc. and
6// Cirrus Logic International Semiconductor Ltd.
7//
8// Author: Charles Keepax <ckeepax@opensource.cirrus.com>
bef9391c
CK
9
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>
15#include <linux/of.h>
16#include <linux/platform_device.h>
17#include <linux/regmap.h>
18#include <linux/regulator/driver.h>
19#include <linux/regulator/machine.h>
20#include <linux/regulator/of_regulator.h>
21
22#include <linux/mfd/lochnagar.h>
23
24static const struct regulator_ops lochnagar_micvdd_ops = {
25 .enable = regulator_enable_regmap,
26 .disable = regulator_disable_regmap,
27 .is_enabled = regulator_is_enabled_regmap,
28
29 .list_voltage = regulator_list_voltage_linear_range,
30 .map_voltage = regulator_map_voltage_linear_range,
31
32 .get_voltage_sel = regulator_get_voltage_sel_regmap,
33 .set_voltage_sel = regulator_set_voltage_sel_regmap,
34};
35
36static const struct regulator_linear_range lochnagar_micvdd_ranges[] = {
37 REGULATOR_LINEAR_RANGE(1000000, 0, 0xC, 50000),
38 REGULATOR_LINEAR_RANGE(1700000, 0xD, 0x1F, 100000),
39};
40
41static int lochnagar_micbias_enable(struct regulator_dev *rdev)
42{
43 struct lochnagar *lochnagar = rdev_get_drvdata(rdev);
44 int ret;
45
46 mutex_lock(&lochnagar->analogue_config_lock);
47
48 ret = regulator_enable_regmap(rdev);
49 if (ret < 0)
50 goto err;
51
52 ret = lochnagar_update_config(lochnagar);
53
54err:
55 mutex_unlock(&lochnagar->analogue_config_lock);
56
57 return ret;
58}
59
60static int lochnagar_micbias_disable(struct regulator_dev *rdev)
61{
62 struct lochnagar *lochnagar = rdev_get_drvdata(rdev);
63 int ret;
64
65 mutex_lock(&lochnagar->analogue_config_lock);
66
67 ret = regulator_disable_regmap(rdev);
68 if (ret < 0)
69 goto err;
70
71 ret = lochnagar_update_config(lochnagar);
72
73err:
74 mutex_unlock(&lochnagar->analogue_config_lock);
75
76 return ret;
77}
78
79static const struct regulator_ops lochnagar_micbias_ops = {
80 .enable = lochnagar_micbias_enable,
81 .disable = lochnagar_micbias_disable,
82 .is_enabled = regulator_is_enabled_regmap,
83};
84
85static const struct regulator_ops lochnagar_vddcore_ops = {
86 .enable = regulator_enable_regmap,
87 .disable = regulator_disable_regmap,
88 .is_enabled = regulator_is_enabled_regmap,
89
90 .list_voltage = regulator_list_voltage_linear_range,
91 .map_voltage = regulator_map_voltage_linear_range,
92
93 .get_voltage_sel = regulator_get_voltage_sel_regmap,
94 .set_voltage_sel = regulator_set_voltage_sel_regmap,
95};
96
97static const struct regulator_linear_range lochnagar_vddcore_ranges[] = {
98 REGULATOR_LINEAR_RANGE(600000, 0x8, 0x41, 12500),
99};
100
101enum lochnagar_regulators {
102 LOCHNAGAR_MICVDD,
103 LOCHNAGAR_MIC1VDD,
104 LOCHNAGAR_MIC2VDD,
105 LOCHNAGAR_VDDCORE,
106};
107
108static int lochnagar_micbias_of_parse(struct device_node *np,
109 const struct regulator_desc *desc,
110 struct regulator_config *config)
111{
112 struct lochnagar *lochnagar = config->driver_data;
113 int shift = (desc->id - LOCHNAGAR_MIC1VDD) *
114 LOCHNAGAR2_P2_MICBIAS_SRC_SHIFT;
115 int mask = LOCHNAGAR2_P1_MICBIAS_SRC_MASK << shift;
116 unsigned int val;
117 int ret;
118
119 ret = of_property_read_u32(np, "cirrus,micbias-input", &val);
120 if (ret >= 0) {
121 mutex_lock(&lochnagar->analogue_config_lock);
122 ret = regmap_update_bits(lochnagar->regmap,
123 LOCHNAGAR2_ANALOGUE_PATH_CTRL2,
124 mask, val << shift);
125 mutex_unlock(&lochnagar->analogue_config_lock);
126 if (ret < 0) {
127 dev_err(lochnagar->dev,
128 "Failed to update micbias source: %d\n", ret);
129 return ret;
130 }
131 }
132
133 return 0;
134}
135
136static const struct regulator_desc lochnagar_regulators[] = {
137 [LOCHNAGAR_MICVDD] = {
138 .name = "MICVDD",
139 .supply_name = "SYSVDD",
140 .type = REGULATOR_VOLTAGE,
141 .n_voltages = 32,
142 .ops = &lochnagar_micvdd_ops,
143
144 .id = LOCHNAGAR_MICVDD,
145 .of_match = of_match_ptr("MICVDD"),
146
147 .enable_reg = LOCHNAGAR2_MICVDD_CTRL1,
148 .enable_mask = LOCHNAGAR2_MICVDD_REG_ENA_MASK,
149 .vsel_reg = LOCHNAGAR2_MICVDD_CTRL2,
150 .vsel_mask = LOCHNAGAR2_MICVDD_VSEL_MASK,
151
152 .linear_ranges = lochnagar_micvdd_ranges,
153 .n_linear_ranges = ARRAY_SIZE(lochnagar_micvdd_ranges),
154
155 .enable_time = 3000,
156 .ramp_delay = 1000,
157
158 .owner = THIS_MODULE,
159 },
160 [LOCHNAGAR_MIC1VDD] = {
161 .name = "MIC1VDD",
162 .supply_name = "MICBIAS1",
163 .type = REGULATOR_VOLTAGE,
164 .ops = &lochnagar_micbias_ops,
165
166 .id = LOCHNAGAR_MIC1VDD,
167 .of_match = of_match_ptr("MIC1VDD"),
168 .of_parse_cb = lochnagar_micbias_of_parse,
169
170 .enable_reg = LOCHNAGAR2_ANALOGUE_PATH_CTRL2,
171 .enable_mask = LOCHNAGAR2_P1_INPUT_BIAS_ENA_MASK,
172
173 .owner = THIS_MODULE,
174 },
175 [LOCHNAGAR_MIC2VDD] = {
176 .name = "MIC2VDD",
177 .supply_name = "MICBIAS2",
178 .type = REGULATOR_VOLTAGE,
179 .ops = &lochnagar_micbias_ops,
180
181 .id = LOCHNAGAR_MIC2VDD,
182 .of_match = of_match_ptr("MIC2VDD"),
183 .of_parse_cb = lochnagar_micbias_of_parse,
184
185 .enable_reg = LOCHNAGAR2_ANALOGUE_PATH_CTRL2,
186 .enable_mask = LOCHNAGAR2_P2_INPUT_BIAS_ENA_MASK,
187
188 .owner = THIS_MODULE,
189 },
190 [LOCHNAGAR_VDDCORE] = {
191 .name = "VDDCORE",
192 .supply_name = "SYSVDD",
193 .type = REGULATOR_VOLTAGE,
194 .n_voltages = 57,
195 .ops = &lochnagar_vddcore_ops,
196
197 .id = LOCHNAGAR_VDDCORE,
198 .of_match = of_match_ptr("VDDCORE"),
199
200 .enable_reg = LOCHNAGAR2_VDDCORE_CDC_CTRL1,
201 .enable_mask = LOCHNAGAR2_VDDCORE_CDC_REG_ENA_MASK,
202 .vsel_reg = LOCHNAGAR2_VDDCORE_CDC_CTRL2,
203 .vsel_mask = LOCHNAGAR2_VDDCORE_CDC_VSEL_MASK,
204
205 .linear_ranges = lochnagar_vddcore_ranges,
206 .n_linear_ranges = ARRAY_SIZE(lochnagar_vddcore_ranges),
207
208 .enable_time = 3000,
209 .ramp_delay = 1000,
210
211 .owner = THIS_MODULE,
212 },
213};
214
215static int lochnagar_regulator_probe(struct platform_device *pdev)
216{
217 struct device *dev = &pdev->dev;
218 struct lochnagar *lochnagar = dev_get_drvdata(dev->parent);
219 struct regulator_config config = { };
220 struct regulator_dev *rdev;
221 int ret, i;
222
223 config.dev = lochnagar->dev;
224 config.regmap = lochnagar->regmap;
225 config.driver_data = lochnagar;
226
227 for (i = 0; i < ARRAY_SIZE(lochnagar_regulators); i++) {
228 const struct regulator_desc *desc = &lochnagar_regulators[i];
229
230 rdev = devm_regulator_register(dev, desc, &config);
231 if (IS_ERR(rdev)) {
232 ret = PTR_ERR(rdev);
233 dev_err(dev, "Failed to register %s regulator: %d\n",
234 desc->name, ret);
235 return ret;
236 }
237 }
238
239 return 0;
240}
241
242static struct platform_driver lochnagar_regulator_driver = {
243 .driver = {
244 .name = "lochnagar-regulator",
245 },
246
247 .probe = lochnagar_regulator_probe,
248};
249module_platform_driver(lochnagar_regulator_driver);
250
251MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");
252MODULE_DESCRIPTION("Regulator driver for Cirrus Logic Lochnagar Board");
253MODULE_LICENSE("GPL v2");
254MODULE_ALIAS("platform:lochnagar-regulator");