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> |
18 | #include <linux/mod_devicetable.h> |
19 | #include <linux/mutex.h> |
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(map: regmap, LOCHNAGAR_SOFTWARE_RESET, val: 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(map: regmap, LOCHNAGAR2_ANALOGUE_PATH_CTRL1, val: 0); |
250 | if (ret < 0) |
251 | return ret; |
252 | |
253 | ret = regmap_write(map: 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; |
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, size: sizeof(*lochnagar), GFP_KERNEL); |
281 | if (!lochnagar) |
282 | return -ENOMEM; |
283 | |
284 | config = i2c_get_match_data(client: i2c); |
285 | |
286 | lochnagar->dev = dev; |
287 | mutex_init(&lochnagar->analogue_config_lock); |
288 | |
289 | dev_set_drvdata(dev, data: lochnagar); |
290 | |
291 | reset = devm_gpiod_get(dev, con_id: "reset" , flags: GPIOD_OUT_LOW); |
292 | if (IS_ERR(ptr: reset)) { |
293 | ret = PTR_ERR(ptr: 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, con_id: "present" , flags: GPIOD_OUT_HIGH); |
299 | if (IS_ERR(ptr: present)) { |
300 | ret = PTR_ERR(ptr: 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(msecs: 20); |
307 | |
308 | /* Bring Lochnagar out of reset */ |
309 | gpiod_set_value_cansleep(desc: reset, value: 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(ptr: lochnagar->regmap)) { |
316 | ret = PTR_ERR(ptr: 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(regmap: lochnagar->regmap, id: &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(map: lochnagar->regmap, LOCHNAGAR_FIRMWARE_ID1, val: &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(map: lochnagar->regmap, LOCHNAGAR_FIRMWARE_ID2, val: &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(map: lochnagar->regmap, regs: config->patch, |
359 | num_regs: 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" , |
377 | .of_match_table = lochnagar_of_match, |
378 | .suppress_bind_attrs = true, |
379 | }, |
380 | .probe = lochnagar_i2c_probe, |
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); |
394 | |