Charles Keepax | 422dcaf | 2019-01-30 11:41:26 +0000 | [diff] [blame] | 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/mutex.h> |
| 19 | #include <linux/of.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(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; |
| 273 | const struct of_device_id *of_id; |
| 274 | struct lochnagar *lochnagar; |
| 275 | struct gpio_desc *reset, *present; |
| 276 | unsigned int val; |
| 277 | unsigned int firmwareid; |
| 278 | unsigned int devid, rev; |
| 279 | int ret; |
| 280 | |
| 281 | lochnagar = devm_kzalloc(dev, sizeof(*lochnagar), GFP_KERNEL); |
| 282 | if (!lochnagar) |
| 283 | return -ENOMEM; |
| 284 | |
| 285 | of_id = of_match_device(lochnagar_of_match, dev); |
| 286 | if (!of_id) |
| 287 | return -EINVAL; |
| 288 | |
| 289 | config = of_id->data; |
| 290 | |
| 291 | lochnagar->dev = dev; |
| 292 | mutex_init(&lochnagar->analogue_config_lock); |
| 293 | |
| 294 | dev_set_drvdata(dev, lochnagar); |
| 295 | |
| 296 | reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); |
| 297 | if (IS_ERR(reset)) { |
| 298 | ret = PTR_ERR(reset); |
| 299 | dev_err(dev, "Failed to get reset GPIO: %d\n", ret); |
| 300 | return ret; |
| 301 | } |
| 302 | |
| 303 | present = devm_gpiod_get_optional(dev, "present", GPIOD_OUT_HIGH); |
| 304 | if (IS_ERR(present)) { |
| 305 | ret = PTR_ERR(present); |
| 306 | dev_err(dev, "Failed to get present GPIO: %d\n", ret); |
| 307 | return ret; |
| 308 | } |
| 309 | |
| 310 | /* Leave the Lochnagar in reset for a reasonable amount of time */ |
| 311 | msleep(20); |
| 312 | |
| 313 | /* Bring Lochnagar out of reset */ |
| 314 | gpiod_set_value_cansleep(reset, 1); |
| 315 | |
| 316 | /* Identify Lochnagar */ |
| 317 | lochnagar->type = config->type; |
| 318 | |
| 319 | lochnagar->regmap = devm_regmap_init_i2c(i2c, config->regmap); |
| 320 | if (IS_ERR(lochnagar->regmap)) { |
| 321 | ret = PTR_ERR(lochnagar->regmap); |
| 322 | dev_err(dev, "Failed to allocate register map: %d\n", ret); |
| 323 | return ret; |
| 324 | } |
| 325 | |
| 326 | /* Wait for Lochnagar to boot */ |
| 327 | ret = lochnagar_wait_for_boot(lochnagar->regmap, &val); |
| 328 | if (ret < 0) { |
| 329 | dev_err(dev, "Failed to read device ID: %d\n", ret); |
| 330 | return ret; |
| 331 | } |
| 332 | |
| 333 | devid = val & LOCHNAGAR_DEVICE_ID_MASK; |
| 334 | rev = val & LOCHNAGAR_REV_ID_MASK; |
| 335 | |
| 336 | if (devid != config->id) { |
| 337 | dev_err(dev, |
| 338 | "ID does not match %s (expected 0x%x got 0x%x)\n", |
| 339 | config->name, config->id, devid); |
| 340 | return -ENODEV; |
| 341 | } |
| 342 | |
| 343 | /* Identify firmware */ |
| 344 | ret = regmap_read(lochnagar->regmap, LOCHNAGAR_FIRMWARE_ID1, &val); |
| 345 | if (ret < 0) { |
| 346 | dev_err(dev, "Failed to read firmware id 1: %d\n", ret); |
| 347 | return ret; |
| 348 | } |
| 349 | |
| 350 | firmwareid = val; |
| 351 | |
| 352 | ret = regmap_read(lochnagar->regmap, LOCHNAGAR_FIRMWARE_ID2, &val); |
| 353 | if (ret < 0) { |
| 354 | dev_err(dev, "Failed to read firmware id 2: %d\n", ret); |
| 355 | return ret; |
| 356 | } |
| 357 | |
| 358 | firmwareid |= (val << config->regmap->val_bits); |
| 359 | |
| 360 | dev_info(dev, "Found %s (0x%x) revision %u firmware 0x%.6x\n", |
| 361 | config->name, devid, rev + 1, firmwareid); |
| 362 | |
| 363 | ret = regmap_register_patch(lochnagar->regmap, config->patch, |
| 364 | config->npatch); |
| 365 | if (ret < 0) { |
| 366 | dev_err(dev, "Failed to register patch: %d\n", ret); |
| 367 | return ret; |
| 368 | } |
| 369 | |
| 370 | ret = devm_of_platform_populate(dev); |
| 371 | if (ret < 0) { |
| 372 | dev_err(dev, "Failed to populate child nodes: %d\n", ret); |
| 373 | return ret; |
| 374 | } |
| 375 | |
| 376 | return ret; |
| 377 | } |
| 378 | |
| 379 | static struct i2c_driver lochnagar_i2c_driver = { |
| 380 | .driver = { |
| 381 | .name = "lochnagar", |
| 382 | .of_match_table = of_match_ptr(lochnagar_of_match), |
| 383 | .suppress_bind_attrs = true, |
| 384 | }, |
| 385 | .probe_new = lochnagar_i2c_probe, |
| 386 | }; |
| 387 | |
| 388 | static int __init lochnagar_i2c_init(void) |
| 389 | { |
| 390 | int ret; |
| 391 | |
| 392 | ret = i2c_add_driver(&lochnagar_i2c_driver); |
| 393 | if (ret) |
| 394 | pr_err("Failed to register Lochnagar driver: %d\n", ret); |
| 395 | |
| 396 | return ret; |
| 397 | } |
| 398 | subsys_initcall(lochnagar_i2c_init); |