18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Core MFD support for Cirrus Logic Madera codecs 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015-2018 Cirrus Logic 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/device.h> 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/err.h> 118c2ecf20Sopenharmony_ci#include <linux/gpio.h> 128c2ecf20Sopenharmony_ci#include <linux/mfd/core.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/mutex.h> 158c2ecf20Sopenharmony_ci#include <linux/notifier.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci#include <linux/of_gpio.h> 188c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 198c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 208c2ecf20Sopenharmony_ci#include <linux/regmap.h> 218c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 228c2ecf20Sopenharmony_ci#include <linux/regulator/machine.h> 238c2ecf20Sopenharmony_ci#include <linux/regulator/of_regulator.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <linux/mfd/madera/core.h> 268c2ecf20Sopenharmony_ci#include <linux/mfd/madera/registers.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "madera.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define CS47L15_SILICON_ID 0x6370 318c2ecf20Sopenharmony_ci#define CS47L35_SILICON_ID 0x6360 328c2ecf20Sopenharmony_ci#define CS47L85_SILICON_ID 0x6338 338c2ecf20Sopenharmony_ci#define CS47L90_SILICON_ID 0x6364 348c2ecf20Sopenharmony_ci#define CS47L92_SILICON_ID 0x6371 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define MADERA_32KZ_MCLK2 1 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define MADERA_RESET_MIN_US 2000 398c2ecf20Sopenharmony_ci#define MADERA_RESET_MAX_US 3000 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic const char * const madera_core_supplies[] = { 428c2ecf20Sopenharmony_ci "AVDD", 438c2ecf20Sopenharmony_ci "DBVDD1", 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic const struct mfd_cell madera_ldo1_devs[] = { 478c2ecf20Sopenharmony_ci { 488c2ecf20Sopenharmony_ci .name = "madera-ldo1", 498c2ecf20Sopenharmony_ci .level = MFD_DEP_LEVEL_HIGH, 508c2ecf20Sopenharmony_ci }, 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic const char * const cs47l15_supplies[] = { 548c2ecf20Sopenharmony_ci "MICVDD", 558c2ecf20Sopenharmony_ci "CPVDD1", 568c2ecf20Sopenharmony_ci "SPKVDD", 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic const struct mfd_cell cs47l15_devs[] = { 608c2ecf20Sopenharmony_ci { .name = "madera-pinctrl", }, 618c2ecf20Sopenharmony_ci { .name = "madera-irq", }, 628c2ecf20Sopenharmony_ci { .name = "madera-gpio", }, 638c2ecf20Sopenharmony_ci { 648c2ecf20Sopenharmony_ci .name = "madera-extcon", 658c2ecf20Sopenharmony_ci .parent_supplies = cs47l15_supplies, 668c2ecf20Sopenharmony_ci .num_parent_supplies = 1, /* We only need MICVDD */ 678c2ecf20Sopenharmony_ci }, 688c2ecf20Sopenharmony_ci { 698c2ecf20Sopenharmony_ci .name = "cs47l15-codec", 708c2ecf20Sopenharmony_ci .parent_supplies = cs47l15_supplies, 718c2ecf20Sopenharmony_ci .num_parent_supplies = ARRAY_SIZE(cs47l15_supplies), 728c2ecf20Sopenharmony_ci }, 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic const char * const cs47l35_supplies[] = { 768c2ecf20Sopenharmony_ci "MICVDD", 778c2ecf20Sopenharmony_ci "DBVDD2", 788c2ecf20Sopenharmony_ci "CPVDD1", 798c2ecf20Sopenharmony_ci "CPVDD2", 808c2ecf20Sopenharmony_ci "SPKVDD", 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic const struct mfd_cell cs47l35_devs[] = { 848c2ecf20Sopenharmony_ci { .name = "madera-pinctrl", }, 858c2ecf20Sopenharmony_ci { .name = "madera-irq", }, 868c2ecf20Sopenharmony_ci { .name = "madera-micsupp", }, 878c2ecf20Sopenharmony_ci { .name = "madera-gpio", }, 888c2ecf20Sopenharmony_ci { 898c2ecf20Sopenharmony_ci .name = "madera-extcon", 908c2ecf20Sopenharmony_ci .parent_supplies = cs47l35_supplies, 918c2ecf20Sopenharmony_ci .num_parent_supplies = 1, /* We only need MICVDD */ 928c2ecf20Sopenharmony_ci }, 938c2ecf20Sopenharmony_ci { 948c2ecf20Sopenharmony_ci .name = "cs47l35-codec", 958c2ecf20Sopenharmony_ci .parent_supplies = cs47l35_supplies, 968c2ecf20Sopenharmony_ci .num_parent_supplies = ARRAY_SIZE(cs47l35_supplies), 978c2ecf20Sopenharmony_ci }, 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic const char * const cs47l85_supplies[] = { 1018c2ecf20Sopenharmony_ci "MICVDD", 1028c2ecf20Sopenharmony_ci "DBVDD2", 1038c2ecf20Sopenharmony_ci "DBVDD3", 1048c2ecf20Sopenharmony_ci "DBVDD4", 1058c2ecf20Sopenharmony_ci "CPVDD1", 1068c2ecf20Sopenharmony_ci "CPVDD2", 1078c2ecf20Sopenharmony_ci "SPKVDDL", 1088c2ecf20Sopenharmony_ci "SPKVDDR", 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic const struct mfd_cell cs47l85_devs[] = { 1128c2ecf20Sopenharmony_ci { .name = "madera-pinctrl", }, 1138c2ecf20Sopenharmony_ci { .name = "madera-irq", }, 1148c2ecf20Sopenharmony_ci { .name = "madera-micsupp", }, 1158c2ecf20Sopenharmony_ci { .name = "madera-gpio", }, 1168c2ecf20Sopenharmony_ci { 1178c2ecf20Sopenharmony_ci .name = "madera-extcon", 1188c2ecf20Sopenharmony_ci .parent_supplies = cs47l85_supplies, 1198c2ecf20Sopenharmony_ci .num_parent_supplies = 1, /* We only need MICVDD */ 1208c2ecf20Sopenharmony_ci }, 1218c2ecf20Sopenharmony_ci { 1228c2ecf20Sopenharmony_ci .name = "cs47l85-codec", 1238c2ecf20Sopenharmony_ci .parent_supplies = cs47l85_supplies, 1248c2ecf20Sopenharmony_ci .num_parent_supplies = ARRAY_SIZE(cs47l85_supplies), 1258c2ecf20Sopenharmony_ci }, 1268c2ecf20Sopenharmony_ci}; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic const char * const cs47l90_supplies[] = { 1298c2ecf20Sopenharmony_ci "MICVDD", 1308c2ecf20Sopenharmony_ci "DBVDD2", 1318c2ecf20Sopenharmony_ci "DBVDD3", 1328c2ecf20Sopenharmony_ci "DBVDD4", 1338c2ecf20Sopenharmony_ci "CPVDD1", 1348c2ecf20Sopenharmony_ci "CPVDD2", 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic const struct mfd_cell cs47l90_devs[] = { 1388c2ecf20Sopenharmony_ci { .name = "madera-pinctrl", }, 1398c2ecf20Sopenharmony_ci { .name = "madera-irq", }, 1408c2ecf20Sopenharmony_ci { .name = "madera-micsupp", }, 1418c2ecf20Sopenharmony_ci { .name = "madera-gpio", }, 1428c2ecf20Sopenharmony_ci { 1438c2ecf20Sopenharmony_ci .name = "madera-extcon", 1448c2ecf20Sopenharmony_ci .parent_supplies = cs47l90_supplies, 1458c2ecf20Sopenharmony_ci .num_parent_supplies = 1, /* We only need MICVDD */ 1468c2ecf20Sopenharmony_ci }, 1478c2ecf20Sopenharmony_ci { 1488c2ecf20Sopenharmony_ci .name = "cs47l90-codec", 1498c2ecf20Sopenharmony_ci .parent_supplies = cs47l90_supplies, 1508c2ecf20Sopenharmony_ci .num_parent_supplies = ARRAY_SIZE(cs47l90_supplies), 1518c2ecf20Sopenharmony_ci }, 1528c2ecf20Sopenharmony_ci}; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic const char * const cs47l92_supplies[] = { 1558c2ecf20Sopenharmony_ci "MICVDD", 1568c2ecf20Sopenharmony_ci "CPVDD1", 1578c2ecf20Sopenharmony_ci "CPVDD2", 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic const struct mfd_cell cs47l92_devs[] = { 1618c2ecf20Sopenharmony_ci { .name = "madera-pinctrl", }, 1628c2ecf20Sopenharmony_ci { .name = "madera-irq", }, 1638c2ecf20Sopenharmony_ci { .name = "madera-micsupp", }, 1648c2ecf20Sopenharmony_ci { .name = "madera-gpio", }, 1658c2ecf20Sopenharmony_ci { 1668c2ecf20Sopenharmony_ci .name = "madera-extcon", 1678c2ecf20Sopenharmony_ci .parent_supplies = cs47l92_supplies, 1688c2ecf20Sopenharmony_ci .num_parent_supplies = 1, /* We only need MICVDD */ 1698c2ecf20Sopenharmony_ci }, 1708c2ecf20Sopenharmony_ci { 1718c2ecf20Sopenharmony_ci .name = "cs47l92-codec", 1728c2ecf20Sopenharmony_ci .parent_supplies = cs47l92_supplies, 1738c2ecf20Sopenharmony_ci .num_parent_supplies = ARRAY_SIZE(cs47l92_supplies), 1748c2ecf20Sopenharmony_ci }, 1758c2ecf20Sopenharmony_ci}; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci/* Used by madera-i2c and madera-spi drivers */ 1788c2ecf20Sopenharmony_ciconst char *madera_name_from_type(enum madera_type type) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci switch (type) { 1818c2ecf20Sopenharmony_ci case CS47L15: 1828c2ecf20Sopenharmony_ci return "CS47L15"; 1838c2ecf20Sopenharmony_ci case CS47L35: 1848c2ecf20Sopenharmony_ci return "CS47L35"; 1858c2ecf20Sopenharmony_ci case CS47L85: 1868c2ecf20Sopenharmony_ci return "CS47L85"; 1878c2ecf20Sopenharmony_ci case CS47L90: 1888c2ecf20Sopenharmony_ci return "CS47L90"; 1898c2ecf20Sopenharmony_ci case CS47L91: 1908c2ecf20Sopenharmony_ci return "CS47L91"; 1918c2ecf20Sopenharmony_ci case CS42L92: 1928c2ecf20Sopenharmony_ci return "CS42L92"; 1938c2ecf20Sopenharmony_ci case CS47L92: 1948c2ecf20Sopenharmony_ci return "CS47L92"; 1958c2ecf20Sopenharmony_ci case CS47L93: 1968c2ecf20Sopenharmony_ci return "CS47L93"; 1978c2ecf20Sopenharmony_ci case WM1840: 1988c2ecf20Sopenharmony_ci return "WM1840"; 1998c2ecf20Sopenharmony_ci default: 2008c2ecf20Sopenharmony_ci return "Unknown"; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(madera_name_from_type); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci#define MADERA_BOOT_POLL_INTERVAL_USEC 5000 2068c2ecf20Sopenharmony_ci#define MADERA_BOOT_POLL_TIMEOUT_USEC 25000 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int madera_wait_for_boot_noack(struct madera *madera) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci ktime_t timeout; 2118c2ecf20Sopenharmony_ci unsigned int val = 0; 2128c2ecf20Sopenharmony_ci int ret = 0; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* 2158c2ecf20Sopenharmony_ci * We can't use an interrupt as we need to runtime resume to do so, 2168c2ecf20Sopenharmony_ci * so we poll the status bit. This won't race with the interrupt 2178c2ecf20Sopenharmony_ci * handler because it will be blocked on runtime resume. 2188c2ecf20Sopenharmony_ci * The chip could NAK a read request while it is booting so ignore 2198c2ecf20Sopenharmony_ci * errors from regmap_read. 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_ci timeout = ktime_add_us(ktime_get(), MADERA_BOOT_POLL_TIMEOUT_USEC); 2228c2ecf20Sopenharmony_ci regmap_read(madera->regmap, MADERA_IRQ1_RAW_STATUS_1, &val); 2238c2ecf20Sopenharmony_ci while (!(val & MADERA_BOOT_DONE_STS1) && 2248c2ecf20Sopenharmony_ci !ktime_after(ktime_get(), timeout)) { 2258c2ecf20Sopenharmony_ci usleep_range(MADERA_BOOT_POLL_INTERVAL_USEC / 2, 2268c2ecf20Sopenharmony_ci MADERA_BOOT_POLL_INTERVAL_USEC); 2278c2ecf20Sopenharmony_ci regmap_read(madera->regmap, MADERA_IRQ1_RAW_STATUS_1, &val); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (!(val & MADERA_BOOT_DONE_STS1)) { 2318c2ecf20Sopenharmony_ci dev_err(madera->dev, "Polling BOOT_DONE_STS timed out\n"); 2328c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return ret; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic int madera_wait_for_boot(struct madera *madera) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci int ret = madera_wait_for_boot_noack(madera); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* 2438c2ecf20Sopenharmony_ci * BOOT_DONE defaults to unmasked on boot so we must ack it. 2448c2ecf20Sopenharmony_ci * Do this even after a timeout to avoid interrupt storms. 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_ci regmap_write(madera->regmap, MADERA_IRQ1_STATUS_1, 2478c2ecf20Sopenharmony_ci MADERA_BOOT_DONE_EINT1); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(madera->dev); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return ret; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic int madera_soft_reset(struct madera *madera) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci int ret; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci ret = regmap_write(madera->regmap, MADERA_SOFTWARE_RESET, 0); 2598c2ecf20Sopenharmony_ci if (ret != 0) { 2608c2ecf20Sopenharmony_ci dev_err(madera->dev, "Failed to soft reset device: %d\n", ret); 2618c2ecf20Sopenharmony_ci return ret; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* Allow time for internal clocks to startup after reset */ 2658c2ecf20Sopenharmony_ci usleep_range(MADERA_RESET_MIN_US, MADERA_RESET_MAX_US); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic void madera_enable_hard_reset(struct madera *madera) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci /* 2738c2ecf20Sopenharmony_ci * There are many existing out-of-tree users of these codecs that we 2748c2ecf20Sopenharmony_ci * can't break so preserve the expected behaviour of setting the line 2758c2ecf20Sopenharmony_ci * low to assert reset. 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_ci gpiod_set_raw_value_cansleep(madera->pdata.reset, 0); 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic void madera_disable_hard_reset(struct madera *madera) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci gpiod_set_raw_value_cansleep(madera->pdata.reset, 1); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci usleep_range(MADERA_RESET_MIN_US, MADERA_RESET_MAX_US); 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic int __maybe_unused madera_runtime_resume(struct device *dev) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct madera *madera = dev_get_drvdata(dev); 2908c2ecf20Sopenharmony_ci int ret; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci dev_dbg(dev, "Leaving sleep mode\n"); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci ret = regulator_enable(madera->dcvdd); 2958c2ecf20Sopenharmony_ci if (ret) { 2968c2ecf20Sopenharmony_ci dev_err(dev, "Failed to enable DCVDD: %d\n", ret); 2978c2ecf20Sopenharmony_ci return ret; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci regcache_cache_only(madera->regmap, false); 3018c2ecf20Sopenharmony_ci regcache_cache_only(madera->regmap_32bit, false); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci usleep_range(MADERA_RESET_MIN_US, MADERA_RESET_MAX_US); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci ret = madera_wait_for_boot(madera); 3068c2ecf20Sopenharmony_ci if (ret) 3078c2ecf20Sopenharmony_ci goto err; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci ret = regcache_sync(madera->regmap); 3108c2ecf20Sopenharmony_ci if (ret) { 3118c2ecf20Sopenharmony_ci dev_err(dev, "Failed to restore 16-bit register cache\n"); 3128c2ecf20Sopenharmony_ci goto err; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci ret = regcache_sync(madera->regmap_32bit); 3168c2ecf20Sopenharmony_ci if (ret) { 3178c2ecf20Sopenharmony_ci dev_err(dev, "Failed to restore 32-bit register cache\n"); 3188c2ecf20Sopenharmony_ci goto err; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cierr: 3248c2ecf20Sopenharmony_ci regcache_cache_only(madera->regmap_32bit, true); 3258c2ecf20Sopenharmony_ci regcache_cache_only(madera->regmap, true); 3268c2ecf20Sopenharmony_ci regulator_disable(madera->dcvdd); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci return ret; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int __maybe_unused madera_runtime_suspend(struct device *dev) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct madera *madera = dev_get_drvdata(dev); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci dev_dbg(madera->dev, "Entering sleep mode\n"); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci regcache_cache_only(madera->regmap, true); 3388c2ecf20Sopenharmony_ci regcache_mark_dirty(madera->regmap); 3398c2ecf20Sopenharmony_ci regcache_cache_only(madera->regmap_32bit, true); 3408c2ecf20Sopenharmony_ci regcache_mark_dirty(madera->regmap_32bit); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci regulator_disable(madera->dcvdd); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ciconst struct dev_pm_ops madera_pm_ops = { 3488c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(madera_runtime_suspend, 3498c2ecf20Sopenharmony_ci madera_runtime_resume, 3508c2ecf20Sopenharmony_ci NULL) 3518c2ecf20Sopenharmony_ci}; 3528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(madera_pm_ops); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ciconst struct of_device_id madera_of_match[] = { 3558c2ecf20Sopenharmony_ci { .compatible = "cirrus,cs47l15", .data = (void *)CS47L15 }, 3568c2ecf20Sopenharmony_ci { .compatible = "cirrus,cs47l35", .data = (void *)CS47L35 }, 3578c2ecf20Sopenharmony_ci { .compatible = "cirrus,cs47l85", .data = (void *)CS47L85 }, 3588c2ecf20Sopenharmony_ci { .compatible = "cirrus,cs47l90", .data = (void *)CS47L90 }, 3598c2ecf20Sopenharmony_ci { .compatible = "cirrus,cs47l91", .data = (void *)CS47L91 }, 3608c2ecf20Sopenharmony_ci { .compatible = "cirrus,cs42l92", .data = (void *)CS42L92 }, 3618c2ecf20Sopenharmony_ci { .compatible = "cirrus,cs47l92", .data = (void *)CS47L92 }, 3628c2ecf20Sopenharmony_ci { .compatible = "cirrus,cs47l93", .data = (void *)CS47L93 }, 3638c2ecf20Sopenharmony_ci { .compatible = "cirrus,wm1840", .data = (void *)WM1840 }, 3648c2ecf20Sopenharmony_ci {} 3658c2ecf20Sopenharmony_ci}; 3668c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, madera_of_match); 3678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(madera_of_match); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic int madera_get_reset_gpio(struct madera *madera) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci struct gpio_desc *reset; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (madera->pdata.reset) 3748c2ecf20Sopenharmony_ci return 0; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci reset = devm_gpiod_get_optional(madera->dev, "reset", GPIOD_OUT_LOW); 3778c2ecf20Sopenharmony_ci if (IS_ERR(reset)) 3788c2ecf20Sopenharmony_ci return dev_err_probe(madera->dev, PTR_ERR(reset), 3798c2ecf20Sopenharmony_ci "Failed to request /RESET"); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci /* 3828c2ecf20Sopenharmony_ci * A hard reset is needed for full reset of the chip. We allow running 3838c2ecf20Sopenharmony_ci * without hard reset only because it can be useful for early 3848c2ecf20Sopenharmony_ci * prototyping and some debugging, but we need to warn it's not ideal. 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_ci if (!reset) 3878c2ecf20Sopenharmony_ci dev_warn(madera->dev, 3888c2ecf20Sopenharmony_ci "Running without reset GPIO is not recommended\n"); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci madera->pdata.reset = reset; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci return 0; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic void madera_set_micbias_info(struct madera *madera) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci /* 3988c2ecf20Sopenharmony_ci * num_childbias is an array because future codecs can have different 3998c2ecf20Sopenharmony_ci * childbiases for each micbias. Unspecified values default to 0. 4008c2ecf20Sopenharmony_ci */ 4018c2ecf20Sopenharmony_ci switch (madera->type) { 4028c2ecf20Sopenharmony_ci case CS47L15: 4038c2ecf20Sopenharmony_ci madera->num_micbias = 1; 4048c2ecf20Sopenharmony_ci madera->num_childbias[0] = 3; 4058c2ecf20Sopenharmony_ci return; 4068c2ecf20Sopenharmony_ci case CS47L35: 4078c2ecf20Sopenharmony_ci madera->num_micbias = 2; 4088c2ecf20Sopenharmony_ci madera->num_childbias[0] = 2; 4098c2ecf20Sopenharmony_ci madera->num_childbias[1] = 2; 4108c2ecf20Sopenharmony_ci return; 4118c2ecf20Sopenharmony_ci case CS47L85: 4128c2ecf20Sopenharmony_ci case WM1840: 4138c2ecf20Sopenharmony_ci madera->num_micbias = 4; 4148c2ecf20Sopenharmony_ci /* no child biases */ 4158c2ecf20Sopenharmony_ci return; 4168c2ecf20Sopenharmony_ci case CS47L90: 4178c2ecf20Sopenharmony_ci case CS47L91: 4188c2ecf20Sopenharmony_ci madera->num_micbias = 2; 4198c2ecf20Sopenharmony_ci madera->num_childbias[0] = 4; 4208c2ecf20Sopenharmony_ci madera->num_childbias[1] = 4; 4218c2ecf20Sopenharmony_ci return; 4228c2ecf20Sopenharmony_ci case CS42L92: 4238c2ecf20Sopenharmony_ci case CS47L92: 4248c2ecf20Sopenharmony_ci case CS47L93: 4258c2ecf20Sopenharmony_ci madera->num_micbias = 2; 4268c2ecf20Sopenharmony_ci madera->num_childbias[0] = 4; 4278c2ecf20Sopenharmony_ci madera->num_childbias[1] = 2; 4288c2ecf20Sopenharmony_ci return; 4298c2ecf20Sopenharmony_ci default: 4308c2ecf20Sopenharmony_ci return; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ciint madera_dev_init(struct madera *madera) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci struct device *dev = madera->dev; 4378c2ecf20Sopenharmony_ci unsigned int hwid; 4388c2ecf20Sopenharmony_ci int (*patch_fn)(struct madera *) = NULL; 4398c2ecf20Sopenharmony_ci const struct mfd_cell *mfd_devs; 4408c2ecf20Sopenharmony_ci int n_devs = 0; 4418c2ecf20Sopenharmony_ci int i, ret; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci dev_set_drvdata(madera->dev, madera); 4448c2ecf20Sopenharmony_ci BLOCKING_INIT_NOTIFIER_HEAD(&madera->notifier); 4458c2ecf20Sopenharmony_ci mutex_init(&madera->dapm_ptr_lock); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci madera_set_micbias_info(madera); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* 4508c2ecf20Sopenharmony_ci * We need writable hw config info that all children can share. 4518c2ecf20Sopenharmony_ci * Simplest to take one shared copy of pdata struct. 4528c2ecf20Sopenharmony_ci */ 4538c2ecf20Sopenharmony_ci if (dev_get_platdata(madera->dev)) { 4548c2ecf20Sopenharmony_ci memcpy(&madera->pdata, dev_get_platdata(madera->dev), 4558c2ecf20Sopenharmony_ci sizeof(madera->pdata)); 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci madera->mclk[MADERA_MCLK1].id = "mclk1"; 4598c2ecf20Sopenharmony_ci madera->mclk[MADERA_MCLK2].id = "mclk2"; 4608c2ecf20Sopenharmony_ci madera->mclk[MADERA_MCLK3].id = "mclk3"; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci ret = devm_clk_bulk_get_optional(madera->dev, ARRAY_SIZE(madera->mclk), 4638c2ecf20Sopenharmony_ci madera->mclk); 4648c2ecf20Sopenharmony_ci if (ret) { 4658c2ecf20Sopenharmony_ci dev_err(madera->dev, "Failed to get clocks: %d\n", ret); 4668c2ecf20Sopenharmony_ci return ret; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci /* Not using devm_clk_get to prevent breakage of existing DTs */ 4708c2ecf20Sopenharmony_ci if (!madera->mclk[MADERA_MCLK2].clk) 4718c2ecf20Sopenharmony_ci dev_warn(madera->dev, "Missing MCLK2, requires 32kHz clock\n"); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci ret = madera_get_reset_gpio(madera); 4748c2ecf20Sopenharmony_ci if (ret) 4758c2ecf20Sopenharmony_ci return ret; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci regcache_cache_only(madera->regmap, true); 4788c2ecf20Sopenharmony_ci regcache_cache_only(madera->regmap_32bit, true); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(madera_core_supplies); i++) 4818c2ecf20Sopenharmony_ci madera->core_supplies[i].supply = madera_core_supplies[i]; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci madera->num_core_supplies = ARRAY_SIZE(madera_core_supplies); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* 4868c2ecf20Sopenharmony_ci * On some codecs DCVDD could be supplied by the internal LDO1. 4878c2ecf20Sopenharmony_ci * For those we must add the LDO1 driver before requesting DCVDD 4888c2ecf20Sopenharmony_ci * No devm_ because we need to control shutdown order of children. 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_ci switch (madera->type) { 4918c2ecf20Sopenharmony_ci case CS47L15: 4928c2ecf20Sopenharmony_ci case CS47L35: 4938c2ecf20Sopenharmony_ci case CS47L90: 4948c2ecf20Sopenharmony_ci case CS47L91: 4958c2ecf20Sopenharmony_ci case CS42L92: 4968c2ecf20Sopenharmony_ci case CS47L92: 4978c2ecf20Sopenharmony_ci case CS47L93: 4988c2ecf20Sopenharmony_ci break; 4998c2ecf20Sopenharmony_ci case CS47L85: 5008c2ecf20Sopenharmony_ci case WM1840: 5018c2ecf20Sopenharmony_ci ret = mfd_add_devices(madera->dev, PLATFORM_DEVID_NONE, 5028c2ecf20Sopenharmony_ci madera_ldo1_devs, 5038c2ecf20Sopenharmony_ci ARRAY_SIZE(madera_ldo1_devs), 5048c2ecf20Sopenharmony_ci NULL, 0, NULL); 5058c2ecf20Sopenharmony_ci if (ret) { 5068c2ecf20Sopenharmony_ci dev_err(dev, "Failed to add LDO1 child: %d\n", ret); 5078c2ecf20Sopenharmony_ci return ret; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci break; 5108c2ecf20Sopenharmony_ci default: 5118c2ecf20Sopenharmony_ci /* No point continuing if the type is unknown */ 5128c2ecf20Sopenharmony_ci dev_err(madera->dev, "Unknown device type %d\n", madera->type); 5138c2ecf20Sopenharmony_ci return -ENODEV; 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci ret = devm_regulator_bulk_get(dev, madera->num_core_supplies, 5178c2ecf20Sopenharmony_ci madera->core_supplies); 5188c2ecf20Sopenharmony_ci if (ret) { 5198c2ecf20Sopenharmony_ci dev_err(dev, "Failed to request core supplies: %d\n", ret); 5208c2ecf20Sopenharmony_ci goto err_devs; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci /* 5248c2ecf20Sopenharmony_ci * Don't use devres here. If the regulator is one of our children it 5258c2ecf20Sopenharmony_ci * will already have been removed before devres cleanup on this mfd 5268c2ecf20Sopenharmony_ci * driver tries to call put() on it. We need control of shutdown order. 5278c2ecf20Sopenharmony_ci */ 5288c2ecf20Sopenharmony_ci madera->dcvdd = regulator_get(madera->dev, "DCVDD"); 5298c2ecf20Sopenharmony_ci if (IS_ERR(madera->dcvdd)) { 5308c2ecf20Sopenharmony_ci ret = PTR_ERR(madera->dcvdd); 5318c2ecf20Sopenharmony_ci dev_err(dev, "Failed to request DCVDD: %d\n", ret); 5328c2ecf20Sopenharmony_ci goto err_devs; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci ret = regulator_bulk_enable(madera->num_core_supplies, 5368c2ecf20Sopenharmony_ci madera->core_supplies); 5378c2ecf20Sopenharmony_ci if (ret) { 5388c2ecf20Sopenharmony_ci dev_err(dev, "Failed to enable core supplies: %d\n", ret); 5398c2ecf20Sopenharmony_ci goto err_dcvdd; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci ret = regulator_enable(madera->dcvdd); 5438c2ecf20Sopenharmony_ci if (ret) { 5448c2ecf20Sopenharmony_ci dev_err(dev, "Failed to enable DCVDD: %d\n", ret); 5458c2ecf20Sopenharmony_ci goto err_enable; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci madera_disable_hard_reset(madera); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci regcache_cache_only(madera->regmap, false); 5518c2ecf20Sopenharmony_ci regcache_cache_only(madera->regmap_32bit, false); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci ret = madera_wait_for_boot_noack(madera); 5548c2ecf20Sopenharmony_ci if (ret) { 5558c2ecf20Sopenharmony_ci dev_err(madera->dev, "Device failed initial boot: %d\n", ret); 5568c2ecf20Sopenharmony_ci goto err_reset; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* 5608c2ecf20Sopenharmony_ci * Now we can power up and verify that this is a chip we know about 5618c2ecf20Sopenharmony_ci * before we start doing any writes to its registers. 5628c2ecf20Sopenharmony_ci */ 5638c2ecf20Sopenharmony_ci ret = regmap_read(madera->regmap, MADERA_SOFTWARE_RESET, &hwid); 5648c2ecf20Sopenharmony_ci if (ret) { 5658c2ecf20Sopenharmony_ci dev_err(dev, "Failed to read ID register: %d\n", ret); 5668c2ecf20Sopenharmony_ci goto err_reset; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci switch (hwid) { 5708c2ecf20Sopenharmony_ci case CS47L15_SILICON_ID: 5718c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_MFD_CS47L15)) { 5728c2ecf20Sopenharmony_ci switch (madera->type) { 5738c2ecf20Sopenharmony_ci case CS47L15: 5748c2ecf20Sopenharmony_ci patch_fn = &cs47l15_patch; 5758c2ecf20Sopenharmony_ci mfd_devs = cs47l15_devs; 5768c2ecf20Sopenharmony_ci n_devs = ARRAY_SIZE(cs47l15_devs); 5778c2ecf20Sopenharmony_ci break; 5788c2ecf20Sopenharmony_ci default: 5798c2ecf20Sopenharmony_ci break; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci break; 5838c2ecf20Sopenharmony_ci case CS47L35_SILICON_ID: 5848c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_MFD_CS47L35)) { 5858c2ecf20Sopenharmony_ci switch (madera->type) { 5868c2ecf20Sopenharmony_ci case CS47L35: 5878c2ecf20Sopenharmony_ci patch_fn = cs47l35_patch; 5888c2ecf20Sopenharmony_ci mfd_devs = cs47l35_devs; 5898c2ecf20Sopenharmony_ci n_devs = ARRAY_SIZE(cs47l35_devs); 5908c2ecf20Sopenharmony_ci break; 5918c2ecf20Sopenharmony_ci default: 5928c2ecf20Sopenharmony_ci break; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci break; 5968c2ecf20Sopenharmony_ci case CS47L85_SILICON_ID: 5978c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_MFD_CS47L85)) { 5988c2ecf20Sopenharmony_ci switch (madera->type) { 5998c2ecf20Sopenharmony_ci case CS47L85: 6008c2ecf20Sopenharmony_ci case WM1840: 6018c2ecf20Sopenharmony_ci patch_fn = cs47l85_patch; 6028c2ecf20Sopenharmony_ci mfd_devs = cs47l85_devs; 6038c2ecf20Sopenharmony_ci n_devs = ARRAY_SIZE(cs47l85_devs); 6048c2ecf20Sopenharmony_ci break; 6058c2ecf20Sopenharmony_ci default: 6068c2ecf20Sopenharmony_ci break; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci break; 6108c2ecf20Sopenharmony_ci case CS47L90_SILICON_ID: 6118c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_MFD_CS47L90)) { 6128c2ecf20Sopenharmony_ci switch (madera->type) { 6138c2ecf20Sopenharmony_ci case CS47L90: 6148c2ecf20Sopenharmony_ci case CS47L91: 6158c2ecf20Sopenharmony_ci patch_fn = cs47l90_patch; 6168c2ecf20Sopenharmony_ci mfd_devs = cs47l90_devs; 6178c2ecf20Sopenharmony_ci n_devs = ARRAY_SIZE(cs47l90_devs); 6188c2ecf20Sopenharmony_ci break; 6198c2ecf20Sopenharmony_ci default: 6208c2ecf20Sopenharmony_ci break; 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci break; 6248c2ecf20Sopenharmony_ci case CS47L92_SILICON_ID: 6258c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_MFD_CS47L92)) { 6268c2ecf20Sopenharmony_ci switch (madera->type) { 6278c2ecf20Sopenharmony_ci case CS42L92: 6288c2ecf20Sopenharmony_ci case CS47L92: 6298c2ecf20Sopenharmony_ci case CS47L93: 6308c2ecf20Sopenharmony_ci patch_fn = cs47l92_patch; 6318c2ecf20Sopenharmony_ci mfd_devs = cs47l92_devs; 6328c2ecf20Sopenharmony_ci n_devs = ARRAY_SIZE(cs47l92_devs); 6338c2ecf20Sopenharmony_ci break; 6348c2ecf20Sopenharmony_ci default: 6358c2ecf20Sopenharmony_ci break; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci break; 6398c2ecf20Sopenharmony_ci default: 6408c2ecf20Sopenharmony_ci dev_err(madera->dev, "Unknown device ID: %x\n", hwid); 6418c2ecf20Sopenharmony_ci ret = -EINVAL; 6428c2ecf20Sopenharmony_ci goto err_reset; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci if (!n_devs) { 6468c2ecf20Sopenharmony_ci dev_err(madera->dev, "Device ID 0x%x not a %s\n", hwid, 6478c2ecf20Sopenharmony_ci madera->type_name); 6488c2ecf20Sopenharmony_ci ret = -ENODEV; 6498c2ecf20Sopenharmony_ci goto err_reset; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci /* 6538c2ecf20Sopenharmony_ci * It looks like a device we support. If we don't have a hard reset 6548c2ecf20Sopenharmony_ci * we can now attempt a soft reset. 6558c2ecf20Sopenharmony_ci */ 6568c2ecf20Sopenharmony_ci if (!madera->pdata.reset) { 6578c2ecf20Sopenharmony_ci ret = madera_soft_reset(madera); 6588c2ecf20Sopenharmony_ci if (ret) 6598c2ecf20Sopenharmony_ci goto err_reset; 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci ret = madera_wait_for_boot(madera); 6638c2ecf20Sopenharmony_ci if (ret) { 6648c2ecf20Sopenharmony_ci dev_err(madera->dev, "Failed to clear boot done: %d\n", ret); 6658c2ecf20Sopenharmony_ci goto err_reset; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci ret = regmap_read(madera->regmap, MADERA_HARDWARE_REVISION, 6698c2ecf20Sopenharmony_ci &madera->rev); 6708c2ecf20Sopenharmony_ci if (ret) { 6718c2ecf20Sopenharmony_ci dev_err(dev, "Failed to read revision register: %d\n", ret); 6728c2ecf20Sopenharmony_ci goto err_reset; 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci madera->rev &= MADERA_HW_REVISION_MASK; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci dev_info(dev, "%s silicon revision %d\n", madera->type_name, 6778c2ecf20Sopenharmony_ci madera->rev); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci /* Apply hardware patch */ 6808c2ecf20Sopenharmony_ci if (patch_fn) { 6818c2ecf20Sopenharmony_ci ret = patch_fn(madera); 6828c2ecf20Sopenharmony_ci if (ret) { 6838c2ecf20Sopenharmony_ci dev_err(madera->dev, "Failed to apply patch %d\n", ret); 6848c2ecf20Sopenharmony_ci goto err_reset; 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci /* Init 32k clock sourced from MCLK2 */ 6898c2ecf20Sopenharmony_ci ret = clk_prepare_enable(madera->mclk[MADERA_MCLK2].clk); 6908c2ecf20Sopenharmony_ci if (ret) { 6918c2ecf20Sopenharmony_ci dev_err(madera->dev, "Failed to enable 32k clock: %d\n", ret); 6928c2ecf20Sopenharmony_ci goto err_reset; 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci ret = regmap_update_bits(madera->regmap, 6968c2ecf20Sopenharmony_ci MADERA_CLOCK_32K_1, 6978c2ecf20Sopenharmony_ci MADERA_CLK_32K_ENA_MASK | MADERA_CLK_32K_SRC_MASK, 6988c2ecf20Sopenharmony_ci MADERA_CLK_32K_ENA | MADERA_32KZ_MCLK2); 6998c2ecf20Sopenharmony_ci if (ret) { 7008c2ecf20Sopenharmony_ci dev_err(madera->dev, "Failed to init 32k clock: %d\n", ret); 7018c2ecf20Sopenharmony_ci goto err_clock; 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci pm_runtime_set_active(madera->dev); 7058c2ecf20Sopenharmony_ci pm_runtime_enable(madera->dev); 7068c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(madera->dev, 100); 7078c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(madera->dev); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci /* No devm_ because we need to control shutdown order of children */ 7108c2ecf20Sopenharmony_ci ret = mfd_add_devices(madera->dev, PLATFORM_DEVID_NONE, 7118c2ecf20Sopenharmony_ci mfd_devs, n_devs, 7128c2ecf20Sopenharmony_ci NULL, 0, NULL); 7138c2ecf20Sopenharmony_ci if (ret) { 7148c2ecf20Sopenharmony_ci dev_err(madera->dev, "Failed to add subdevices: %d\n", ret); 7158c2ecf20Sopenharmony_ci goto err_pm_runtime; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci return 0; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_cierr_pm_runtime: 7218c2ecf20Sopenharmony_ci pm_runtime_disable(madera->dev); 7228c2ecf20Sopenharmony_cierr_clock: 7238c2ecf20Sopenharmony_ci clk_disable_unprepare(madera->mclk[MADERA_MCLK2].clk); 7248c2ecf20Sopenharmony_cierr_reset: 7258c2ecf20Sopenharmony_ci madera_enable_hard_reset(madera); 7268c2ecf20Sopenharmony_ci regulator_disable(madera->dcvdd); 7278c2ecf20Sopenharmony_cierr_enable: 7288c2ecf20Sopenharmony_ci regulator_bulk_disable(madera->num_core_supplies, 7298c2ecf20Sopenharmony_ci madera->core_supplies); 7308c2ecf20Sopenharmony_cierr_dcvdd: 7318c2ecf20Sopenharmony_ci regulator_put(madera->dcvdd); 7328c2ecf20Sopenharmony_cierr_devs: 7338c2ecf20Sopenharmony_ci mfd_remove_devices(dev); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci return ret; 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(madera_dev_init); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ciint madera_dev_exit(struct madera *madera) 7408c2ecf20Sopenharmony_ci{ 7418c2ecf20Sopenharmony_ci /* Prevent any IRQs being serviced while we clean up */ 7428c2ecf20Sopenharmony_ci disable_irq(madera->irq); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci pm_runtime_get_sync(madera->dev); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci mfd_remove_devices(madera->dev); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci pm_runtime_disable(madera->dev); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci regulator_disable(madera->dcvdd); 7518c2ecf20Sopenharmony_ci regulator_put(madera->dcvdd); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci mfd_remove_devices_late(madera->dev); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci pm_runtime_set_suspended(madera->dev); 7568c2ecf20Sopenharmony_ci pm_runtime_put_noidle(madera->dev); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci clk_disable_unprepare(madera->mclk[MADERA_MCLK2].clk); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci madera_enable_hard_reset(madera); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci regulator_bulk_disable(madera->num_core_supplies, 7638c2ecf20Sopenharmony_ci madera->core_supplies); 7648c2ecf20Sopenharmony_ci return 0; 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(madera_dev_exit); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Madera core MFD driver"); 7698c2ecf20Sopenharmony_ciMODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); 7708c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 771