Commit | Line | Data |
---|---|---|
422dcafe CK |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Lochnagar I2C bus interface | |
4 | * | |
5 | * Copyright (c) 2012-2018 Cirrus Logic, Inc. and | |
6 | * Cirrus Logic International Semiconductor Ltd. | |
7 | * | |
8 | * Author: Charles Keepax <ckeepax@opensource.cirrus.com> | |
9 | */ | |
10 | ||
11 | #include <linux/delay.h> | |
12 | #include <linux/device.h> | |
13 | #include <linux/err.h> | |
14 | #include <linux/gpio/consumer.h> | |
15 | #include <linux/i2c.h> | |
16 | #include <linux/lockdep.h> | |
17 | #include <linux/mfd/core.h> | |
0db434f5 | 18 | #include <linux/mod_devicetable.h> |
422dcafe | 19 | #include <linux/mutex.h> |
422dcafe CK |
20 | #include <linux/of_platform.h> |
21 | #include <linux/regmap.h> | |
22 | ||
23 | #include <linux/mfd/lochnagar.h> | |
24 | #include <linux/mfd/lochnagar1_regs.h> | |
25 | #include <linux/mfd/lochnagar2_regs.h> | |
26 | ||
27 | #define LOCHNAGAR_BOOT_RETRIES 10 | |
28 | #define LOCHNAGAR_BOOT_DELAY_MS 350 | |
29 | ||
30 | #define LOCHNAGAR_CONFIG_POLL_US 10000 | |
31 | ||
32 | static bool lochnagar1_readable_register(struct device *dev, unsigned int reg) | |
33 | { | |
34 | switch (reg) { | |
35 | case LOCHNAGAR_SOFTWARE_RESET: | |
36 | case LOCHNAGAR_FIRMWARE_ID1...LOCHNAGAR_FIRMWARE_ID2: | |
37 | case LOCHNAGAR1_CDC_AIF1_SEL...LOCHNAGAR1_CDC_AIF3_SEL: | |
38 | case LOCHNAGAR1_CDC_MCLK1_SEL...LOCHNAGAR1_CDC_MCLK2_SEL: | |
39 | case LOCHNAGAR1_CDC_AIF_CTRL1...LOCHNAGAR1_CDC_AIF_CTRL2: | |
40 | case LOCHNAGAR1_EXT_AIF_CTRL: | |
41 | case LOCHNAGAR1_DSP_AIF1_SEL...LOCHNAGAR1_DSP_AIF2_SEL: | |
42 | case LOCHNAGAR1_DSP_CLKIN_SEL: | |
43 | case LOCHNAGAR1_DSP_AIF: | |
44 | case LOCHNAGAR1_GF_AIF1...LOCHNAGAR1_GF_AIF2: | |
45 | case LOCHNAGAR1_PSIA_AIF: | |
46 | case LOCHNAGAR1_PSIA1_SEL...LOCHNAGAR1_PSIA2_SEL: | |
47 | case LOCHNAGAR1_SPDIF_AIF_SEL: | |
48 | case LOCHNAGAR1_GF_AIF3_SEL...LOCHNAGAR1_GF_AIF4_SEL: | |
49 | case LOCHNAGAR1_GF_CLKOUT1_SEL: | |
50 | case LOCHNAGAR1_GF_AIF1_SEL...LOCHNAGAR1_GF_AIF2_SEL: | |
51 | case LOCHNAGAR1_GF_GPIO2...LOCHNAGAR1_GF_GPIO7: | |
52 | case LOCHNAGAR1_RST: | |
53 | case LOCHNAGAR1_LED1...LOCHNAGAR1_LED2: | |
54 | case LOCHNAGAR1_I2C_CTRL: | |
55 | return true; | |
56 | default: | |
57 | return false; | |
58 | } | |
59 | } | |
60 | ||
61 | static const struct regmap_config lochnagar1_i2c_regmap = { | |
62 | .reg_bits = 8, | |
63 | .val_bits = 8, | |
64 | .reg_format_endian = REGMAP_ENDIAN_BIG, | |
65 | .val_format_endian = REGMAP_ENDIAN_BIG, | |
66 | ||
67 | .max_register = 0x50, | |
68 | .readable_reg = lochnagar1_readable_register, | |
69 | ||
70 | .use_single_read = true, | |
71 | .use_single_write = true, | |
72 | ||
73 | .cache_type = REGCACHE_RBTREE, | |
74 | }; | |
75 | ||
76 | static const struct reg_sequence lochnagar1_patch[] = { | |
77 | { 0x40, 0x0083 }, | |
78 | { 0x47, 0x0018 }, | |
79 | { 0x50, 0x0000 }, | |
80 | }; | |
81 | ||
82 | static bool lochnagar2_readable_register(struct device *dev, unsigned int reg) | |
83 | { | |
84 | switch (reg) { | |
85 | case LOCHNAGAR_SOFTWARE_RESET: | |
86 | case LOCHNAGAR_FIRMWARE_ID1...LOCHNAGAR_FIRMWARE_ID2: | |
87 | case LOCHNAGAR2_CDC_AIF1_CTRL...LOCHNAGAR2_CDC_AIF3_CTRL: | |
88 | case LOCHNAGAR2_DSP_AIF1_CTRL...LOCHNAGAR2_DSP_AIF2_CTRL: | |
89 | case LOCHNAGAR2_PSIA1_CTRL...LOCHNAGAR2_PSIA2_CTRL: | |
90 | case LOCHNAGAR2_GF_AIF3_CTRL...LOCHNAGAR2_GF_AIF4_CTRL: | |
91 | case LOCHNAGAR2_GF_AIF1_CTRL...LOCHNAGAR2_GF_AIF2_CTRL: | |
92 | case LOCHNAGAR2_SPDIF_AIF_CTRL: | |
93 | case LOCHNAGAR2_USB_AIF1_CTRL...LOCHNAGAR2_USB_AIF2_CTRL: | |
94 | case LOCHNAGAR2_ADAT_AIF_CTRL: | |
95 | case LOCHNAGAR2_CDC_MCLK1_CTRL...LOCHNAGAR2_CDC_MCLK2_CTRL: | |
96 | case LOCHNAGAR2_DSP_CLKIN_CTRL: | |
97 | case LOCHNAGAR2_PSIA1_MCLK_CTRL...LOCHNAGAR2_PSIA2_MCLK_CTRL: | |
98 | case LOCHNAGAR2_SPDIF_MCLK_CTRL: | |
99 | case LOCHNAGAR2_GF_CLKOUT1_CTRL...LOCHNAGAR2_GF_CLKOUT2_CTRL: | |
100 | case LOCHNAGAR2_ADAT_MCLK_CTRL: | |
101 | case LOCHNAGAR2_SOUNDCARD_MCLK_CTRL: | |
102 | case LOCHNAGAR2_GPIO_FPGA_GPIO1...LOCHNAGAR2_GPIO_FPGA_GPIO6: | |
103 | case LOCHNAGAR2_GPIO_CDC_GPIO1...LOCHNAGAR2_GPIO_CDC_GPIO8: | |
104 | case LOCHNAGAR2_GPIO_DSP_GPIO1...LOCHNAGAR2_GPIO_DSP_GPIO6: | |
105 | case LOCHNAGAR2_GPIO_GF_GPIO2...LOCHNAGAR2_GPIO_GF_GPIO7: | |
106 | case LOCHNAGAR2_GPIO_CDC_AIF1_BCLK...LOCHNAGAR2_GPIO_CDC_AIF3_TXDAT: | |
107 | case LOCHNAGAR2_GPIO_DSP_AIF1_BCLK...LOCHNAGAR2_GPIO_DSP_AIF2_TXDAT: | |
108 | case LOCHNAGAR2_GPIO_PSIA1_BCLK...LOCHNAGAR2_GPIO_PSIA2_TXDAT: | |
109 | case LOCHNAGAR2_GPIO_GF_AIF3_BCLK...LOCHNAGAR2_GPIO_GF_AIF4_TXDAT: | |
110 | case LOCHNAGAR2_GPIO_GF_AIF1_BCLK...LOCHNAGAR2_GPIO_GF_AIF2_TXDAT: | |
111 | case LOCHNAGAR2_GPIO_DSP_UART1_RX...LOCHNAGAR2_GPIO_DSP_UART2_TX: | |
112 | case LOCHNAGAR2_GPIO_GF_UART2_RX...LOCHNAGAR2_GPIO_GF_UART2_TX: | |
113 | case LOCHNAGAR2_GPIO_USB_UART_RX: | |
114 | case LOCHNAGAR2_GPIO_CDC_PDMCLK1...LOCHNAGAR2_GPIO_CDC_PDMDAT2: | |
115 | case LOCHNAGAR2_GPIO_CDC_DMICCLK1...LOCHNAGAR2_GPIO_CDC_DMICDAT4: | |
116 | case LOCHNAGAR2_GPIO_DSP_DMICCLK1...LOCHNAGAR2_GPIO_DSP_DMICDAT2: | |
117 | case LOCHNAGAR2_GPIO_I2C2_SCL...LOCHNAGAR2_GPIO_I2C4_SDA: | |
118 | case LOCHNAGAR2_GPIO_DSP_STANDBY: | |
119 | case LOCHNAGAR2_GPIO_CDC_MCLK1...LOCHNAGAR2_GPIO_CDC_MCLK2: | |
120 | case LOCHNAGAR2_GPIO_DSP_CLKIN: | |
121 | case LOCHNAGAR2_GPIO_PSIA1_MCLK...LOCHNAGAR2_GPIO_PSIA2_MCLK: | |
122 | case LOCHNAGAR2_GPIO_GF_GPIO1...LOCHNAGAR2_GPIO_GF_GPIO5: | |
123 | case LOCHNAGAR2_GPIO_DSP_GPIO20: | |
124 | case LOCHNAGAR2_GPIO_CHANNEL1...LOCHNAGAR2_GPIO_CHANNEL16: | |
125 | case LOCHNAGAR2_MINICARD_RESETS: | |
126 | case LOCHNAGAR2_ANALOGUE_PATH_CTRL1...LOCHNAGAR2_ANALOGUE_PATH_CTRL2: | |
127 | case LOCHNAGAR2_COMMS_CTRL4: | |
128 | case LOCHNAGAR2_SPDIF_CTRL: | |
129 | case LOCHNAGAR2_IMON_CTRL1...LOCHNAGAR2_IMON_CTRL4: | |
130 | case LOCHNAGAR2_IMON_DATA1...LOCHNAGAR2_IMON_DATA2: | |
131 | case LOCHNAGAR2_POWER_CTRL: | |
132 | case LOCHNAGAR2_MICVDD_CTRL1: | |
133 | case LOCHNAGAR2_MICVDD_CTRL2: | |
134 | case LOCHNAGAR2_VDDCORE_CDC_CTRL1: | |
135 | case LOCHNAGAR2_VDDCORE_CDC_CTRL2: | |
136 | case LOCHNAGAR2_SOUNDCARD_AIF_CTRL: | |
137 | return true; | |
138 | default: | |
139 | return false; | |
140 | } | |
141 | } | |
142 | ||
143 | static bool lochnagar2_volatile_register(struct device *dev, unsigned int reg) | |
144 | { | |
145 | switch (reg) { | |
146 | case LOCHNAGAR2_GPIO_CHANNEL1...LOCHNAGAR2_GPIO_CHANNEL16: | |
147 | case LOCHNAGAR2_ANALOGUE_PATH_CTRL1: | |
148 | case LOCHNAGAR2_IMON_CTRL3...LOCHNAGAR2_IMON_CTRL4: | |
149 | case LOCHNAGAR2_IMON_DATA1...LOCHNAGAR2_IMON_DATA2: | |
150 | return true; | |
151 | default: | |
152 | return false; | |
153 | } | |
154 | } | |
155 | ||
156 | static const struct regmap_config lochnagar2_i2c_regmap = { | |
157 | .reg_bits = 16, | |
158 | .val_bits = 16, | |
159 | .reg_format_endian = REGMAP_ENDIAN_BIG, | |
160 | .val_format_endian = REGMAP_ENDIAN_BIG, | |
161 | ||
162 | .max_register = 0x1F1F, | |
163 | .readable_reg = lochnagar2_readable_register, | |
164 | .volatile_reg = lochnagar2_volatile_register, | |
165 | ||
166 | .cache_type = REGCACHE_RBTREE, | |
167 | }; | |
168 | ||
169 | static const struct reg_sequence lochnagar2_patch[] = { | |
170 | { 0x00EE, 0x0000 }, | |
171 | }; | |
172 | ||
173 | struct lochnagar_config { | |
174 | int id; | |
175 | const char * const name; | |
176 | enum lochnagar_type type; | |
177 | const struct regmap_config *regmap; | |
178 | const struct reg_sequence *patch; | |
179 | int npatch; | |
180 | }; | |
181 | ||
182 | static struct lochnagar_config lochnagar_configs[] = { | |
183 | { | |
184 | .id = 0x50, | |
185 | .name = "lochnagar1", | |
186 | .type = LOCHNAGAR1, | |
187 | .regmap = &lochnagar1_i2c_regmap, | |
188 | .patch = lochnagar1_patch, | |
189 | .npatch = ARRAY_SIZE(lochnagar1_patch), | |
190 | }, | |
191 | { | |
192 | .id = 0xCB58, | |
193 | .name = "lochnagar2", | |
194 | .type = LOCHNAGAR2, | |
195 | .regmap = &lochnagar2_i2c_regmap, | |
196 | .patch = lochnagar2_patch, | |
197 | .npatch = ARRAY_SIZE(lochnagar2_patch), | |
198 | }, | |
199 | }; | |
200 | ||
201 | static const struct of_device_id lochnagar_of_match[] = { | |
202 | { .compatible = "cirrus,lochnagar1", .data = &lochnagar_configs[0] }, | |
203 | { .compatible = "cirrus,lochnagar2", .data = &lochnagar_configs[1] }, | |
204 | {}, | |
205 | }; | |
206 | ||
207 | static int lochnagar_wait_for_boot(struct regmap *regmap, unsigned int *id) | |
208 | { | |
209 | int i, ret; | |
210 | ||
211 | for (i = 0; i < LOCHNAGAR_BOOT_RETRIES; ++i) { | |
212 | msleep(LOCHNAGAR_BOOT_DELAY_MS); | |
213 | ||
214 | /* The reset register will return the device ID when read */ | |
215 | ret = regmap_read(regmap, LOCHNAGAR_SOFTWARE_RESET, id); | |
216 | if (!ret) | |
217 | return ret; | |
218 | } | |
219 | ||
220 | return -ETIMEDOUT; | |
221 | } | |
222 | ||
223 | /** | |
224 | * lochnagar_update_config - Synchronise the boards analogue configuration to | |
225 | * the hardware. | |
226 | * | |
227 | * @lochnagar: A pointer to the primary core data structure. | |
228 | * | |
229 | * Return: Zero on success or an appropriate negative error code on failure. | |
230 | */ | |
231 | int lochnagar_update_config(struct lochnagar *lochnagar) | |
232 | { | |
233 | struct regmap *regmap = lochnagar->regmap; | |
234 | unsigned int done = LOCHNAGAR2_ANALOGUE_PATH_UPDATE_STS_MASK; | |
235 | int timeout_ms = LOCHNAGAR_BOOT_DELAY_MS * LOCHNAGAR_BOOT_RETRIES; | |
236 | unsigned int val = 0; | |
237 | int ret; | |
238 | ||
239 | lockdep_assert_held(&lochnagar->analogue_config_lock); | |
240 | ||
241 | if (lochnagar->type != LOCHNAGAR2) | |
242 | return 0; | |
243 | ||
244 | /* | |
245 | * Toggle the ANALOGUE_PATH_UPDATE bit and wait for the device to | |
246 | * acknowledge that any outstanding changes to the analogue | |
247 | * configuration have been applied. | |
248 | */ | |
249 | ret = regmap_write(regmap, LOCHNAGAR2_ANALOGUE_PATH_CTRL1, 0); | |
250 | if (ret < 0) | |
251 | return ret; | |
252 | ||
253 | ret = regmap_write(regmap, LOCHNAGAR2_ANALOGUE_PATH_CTRL1, | |
254 | LOCHNAGAR2_ANALOGUE_PATH_UPDATE_MASK); | |
255 | if (ret < 0) | |
256 | return ret; | |
257 | ||
258 | ret = regmap_read_poll_timeout(regmap, | |
259 | LOCHNAGAR2_ANALOGUE_PATH_CTRL1, val, | |
260 | (val & done), LOCHNAGAR_CONFIG_POLL_US, | |
261 | timeout_ms * 1000); | |
262 | if (ret < 0) | |
263 | return ret; | |
264 | ||
265 | return 0; | |
266 | } | |
267 | EXPORT_SYMBOL_GPL(lochnagar_update_config); | |
268 | ||
269 | static int lochnagar_i2c_probe(struct i2c_client *i2c) | |
270 | { | |
271 | struct device *dev = &i2c->dev; | |
272 | const struct lochnagar_config *config = NULL; | |
422dcafe CK |
273 | struct lochnagar *lochnagar; |
274 | struct gpio_desc *reset, *present; | |
275 | unsigned int val; | |
276 | unsigned int firmwareid; | |
277 | unsigned int devid, rev; | |
278 | int ret; | |
279 | ||
280 | lochnagar = devm_kzalloc(dev, sizeof(*lochnagar), GFP_KERNEL); | |
281 | if (!lochnagar) | |
282 | return -ENOMEM; | |
283 | ||
0db434f5 | 284 | config = i2c_get_match_data(i2c); |
422dcafe CK |
285 | |
286 | lochnagar->dev = dev; | |
287 | mutex_init(&lochnagar->analogue_config_lock); | |
288 | ||
289 | dev_set_drvdata(dev, lochnagar); | |
290 | ||
291 | reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); | |
292 | if (IS_ERR(reset)) { | |
293 | ret = PTR_ERR(reset); | |
294 | dev_err(dev, "Failed to get reset GPIO: %d\n", ret); | |
295 | return ret; | |
296 | } | |
297 | ||
298 | present = devm_gpiod_get_optional(dev, "present", GPIOD_OUT_HIGH); | |
299 | if (IS_ERR(present)) { | |
300 | ret = PTR_ERR(present); | |
301 | dev_err(dev, "Failed to get present GPIO: %d\n", ret); | |
302 | return ret; | |
303 | } | |
304 | ||
305 | /* Leave the Lochnagar in reset for a reasonable amount of time */ | |
306 | msleep(20); | |
307 | ||
308 | /* Bring Lochnagar out of reset */ | |
309 | gpiod_set_value_cansleep(reset, 1); | |
310 | ||
311 | /* Identify Lochnagar */ | |
312 | lochnagar->type = config->type; | |
313 | ||
314 | lochnagar->regmap = devm_regmap_init_i2c(i2c, config->regmap); | |
315 | if (IS_ERR(lochnagar->regmap)) { | |
316 | ret = PTR_ERR(lochnagar->regmap); | |
317 | dev_err(dev, "Failed to allocate register map: %d\n", ret); | |
318 | return ret; | |
319 | } | |
320 | ||
321 | /* Wait for Lochnagar to boot */ | |
322 | ret = lochnagar_wait_for_boot(lochnagar->regmap, &val); | |
323 | if (ret < 0) { | |
324 | dev_err(dev, "Failed to read device ID: %d\n", ret); | |
325 | return ret; | |
326 | } | |
327 | ||
328 | devid = val & LOCHNAGAR_DEVICE_ID_MASK; | |
329 | rev = val & LOCHNAGAR_REV_ID_MASK; | |
330 | ||
331 | if (devid != config->id) { | |
332 | dev_err(dev, | |
333 | "ID does not match %s (expected 0x%x got 0x%x)\n", | |
334 | config->name, config->id, devid); | |
335 | return -ENODEV; | |
336 | } | |
337 | ||
338 | /* Identify firmware */ | |
339 | ret = regmap_read(lochnagar->regmap, LOCHNAGAR_FIRMWARE_ID1, &val); | |
340 | if (ret < 0) { | |
341 | dev_err(dev, "Failed to read firmware id 1: %d\n", ret); | |
342 | return ret; | |
343 | } | |
344 | ||
345 | firmwareid = val; | |
346 | ||
347 | ret = regmap_read(lochnagar->regmap, LOCHNAGAR_FIRMWARE_ID2, &val); | |
348 | if (ret < 0) { | |
349 | dev_err(dev, "Failed to read firmware id 2: %d\n", ret); | |
350 | return ret; | |
351 | } | |
352 | ||
353 | firmwareid |= (val << config->regmap->val_bits); | |
354 | ||
355 | dev_info(dev, "Found %s (0x%x) revision %u firmware 0x%.6x\n", | |
356 | config->name, devid, rev + 1, firmwareid); | |
357 | ||
358 | ret = regmap_register_patch(lochnagar->regmap, config->patch, | |
359 | config->npatch); | |
360 | if (ret < 0) { | |
361 | dev_err(dev, "Failed to register patch: %d\n", ret); | |
362 | return ret; | |
363 | } | |
364 | ||
365 | ret = devm_of_platform_populate(dev); | |
366 | if (ret < 0) { | |
367 | dev_err(dev, "Failed to populate child nodes: %d\n", ret); | |
368 | return ret; | |
369 | } | |
370 | ||
371 | return ret; | |
372 | } | |
373 | ||
374 | static struct i2c_driver lochnagar_i2c_driver = { | |
375 | .driver = { | |
376 | .name = "lochnagar", | |
d20642ad | 377 | .of_match_table = lochnagar_of_match, |
422dcafe CK |
378 | .suppress_bind_attrs = true, |
379 | }, | |
9816d859 | 380 | .probe = lochnagar_i2c_probe, |
422dcafe CK |
381 | }; |
382 | ||
383 | static int __init lochnagar_i2c_init(void) | |
384 | { | |
385 | int ret; | |
386 | ||
387 | ret = i2c_add_driver(&lochnagar_i2c_driver); | |
388 | if (ret) | |
389 | pr_err("Failed to register Lochnagar driver: %d\n", ret); | |
390 | ||
391 | return ret; | |
392 | } | |
393 | subsys_initcall(lochnagar_i2c_init); |