Blame view

drivers/regulator/lochnagar-regulator.c 7.21 KB
bef9391cb   Charles Keepax   regulator: lochna...
1
  // SPDX-License-Identifier: GPL-2.0
d8b2a8e9c   Charles Keepax   regulator: lochna...
2
3
4
5
6
7
8
  //
  // Lochnagar regulator driver
  //
  // Copyright (c) 2017-2018 Cirrus Logic, Inc. and
  //                         Cirrus Logic International Semiconductor Ltd.
  //
  // Author: Charles Keepax <ckeepax@opensource.cirrus.com>
bef9391cb   Charles Keepax   regulator: lochna...
9
10
11
12
13
14
15
  
  #include <linux/bitops.h>
  #include <linux/device.h>
  #include <linux/err.h>
  #include <linux/module.h>
  #include <linux/mutex.h>
  #include <linux/of.h>
d90acbc4e   Charles Keepax   regulator: lochna...
16
  #include <linux/of_device.h>
bef9391cb   Charles Keepax   regulator: lochna...
17
18
19
20
21
22
23
  #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>
fa2bb8b97   Charles Keepax   regulator: lochna...
24
25
  #include <linux/mfd/lochnagar1_regs.h>
  #include <linux/mfd/lochnagar2_regs.h>
bef9391cb   Charles Keepax   regulator: lochna...
26
27
28
29
30
31
32
33
34
35
36
37
  
  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,
  };
60ab7f415   Matti Vaittinen   regulator: use li...
38
  static const struct linear_range lochnagar_micvdd_ranges[] = {
bef9391cb   Charles Keepax   regulator: lochna...
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
  	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,
  };
60ab7f415   Matti Vaittinen   regulator: use li...
98
  static const struct linear_range lochnagar_vddcore_ranges[] = {
6dc9674d9   Charles Keepax   regulator: lochna...
99
  	REGULATOR_LINEAR_RANGE(600000, 0,    0x7, 0),
bef9391cb   Charles Keepax   regulator: lochna...
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
  	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
  ", 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,
9df3bb319   Axel Lin   regulator: lochna...
197
  		.n_voltages = 66,
bef9391cb   Charles Keepax   regulator: lochna...
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
  		.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,
f75841aa3   Charles Keepax   regulator: lochna...
213
  		.off_on_delay = 15000,
bef9391cb   Charles Keepax   regulator: lochna...
214
215
216
217
  
  		.owner = THIS_MODULE,
  	},
  };
d90acbc4e   Charles Keepax   regulator: lochna...
218
219
220
221
222
223
224
225
226
227
228
  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",
4cac31e2b   Axel Lin   regulator: lochna...
229
  		.data = &lochnagar_regulators[LOCHNAGAR_MIC2VDD],
d90acbc4e   Charles Keepax   regulator: lochna...
230
231
232
233
234
  	},
  	{
  		.compatible = "cirrus,lochnagar2-vddcore",
  		.data = &lochnagar_regulators[LOCHNAGAR_VDDCORE],
  	},
692f8b56b   Charles Keepax   regulator: lochna...
235
  	{}
d90acbc4e   Charles Keepax   regulator: lochna...
236
  };
692f8b56b   Charles Keepax   regulator: lochna...
237
  MODULE_DEVICE_TABLE(of, lochnagar_of_match);
d90acbc4e   Charles Keepax   regulator: lochna...
238

bef9391cb   Charles Keepax   regulator: lochna...
239
240
241
242
243
  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 = { };
d90acbc4e   Charles Keepax   regulator: lochna...
244
245
  	const struct of_device_id *of_id;
  	const struct regulator_desc *desc;
bef9391cb   Charles Keepax   regulator: lochna...
246
  	struct regulator_dev *rdev;
d90acbc4e   Charles Keepax   regulator: lochna...
247
  	int ret;
bef9391cb   Charles Keepax   regulator: lochna...
248

d90acbc4e   Charles Keepax   regulator: lochna...
249
  	config.dev = dev;
bef9391cb   Charles Keepax   regulator: lochna...
250
251
  	config.regmap = lochnagar->regmap;
  	config.driver_data = lochnagar;
d90acbc4e   Charles Keepax   regulator: lochna...
252
253
254
  	of_id = of_match_device(lochnagar_of_match, dev);
  	if (!of_id)
  		return -EINVAL;
bef9391cb   Charles Keepax   regulator: lochna...
255

d90acbc4e   Charles Keepax   regulator: lochna...
256
257
258
259
260
261
262
263
264
  	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
  ",
  			desc->name, ret);
  		return ret;
bef9391cb   Charles Keepax   regulator: lochna...
265
266
267
268
269
270
271
272
  	}
  
  	return 0;
  }
  
  static struct platform_driver lochnagar_regulator_driver = {
  	.driver = {
  		.name = "lochnagar-regulator",
d90acbc4e   Charles Keepax   regulator: lochna...
273
  		.of_match_table = of_match_ptr(lochnagar_of_match),
bef9391cb   Charles Keepax   regulator: lochna...
274
275
276
277
278
279
280
281
282
283
  	},
  
  	.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");