162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Core support for ATC260x PMICs 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2019 Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> 662306a36Sopenharmony_ci * Copyright (C) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/interrupt.h> 1062306a36Sopenharmony_ci#include <linux/mfd/atc260x/core.h> 1162306a36Sopenharmony_ci#include <linux/mfd/core.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <linux/regmap.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define ATC260X_CHIP_REV_MAX 31 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistruct atc260x_init_regs { 1962306a36Sopenharmony_ci unsigned int cmu_devrst; 2062306a36Sopenharmony_ci unsigned int cmu_devrst_ints; 2162306a36Sopenharmony_ci unsigned int ints_msk; 2262306a36Sopenharmony_ci unsigned int pad_en; 2362306a36Sopenharmony_ci unsigned int pad_en_extirq; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic void regmap_lock_mutex(void *__mutex) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct mutex *mutex = __mutex; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci /* 3162306a36Sopenharmony_ci * Using regmap within an atomic context (e.g. accessing a PMIC when 3262306a36Sopenharmony_ci * powering system down) is normally allowed only if the regmap type 3362306a36Sopenharmony_ci * is MMIO and the regcache type is either REGCACHE_NONE or 3462306a36Sopenharmony_ci * REGCACHE_FLAT. For slow buses like I2C and SPI, the regmap is 3562306a36Sopenharmony_ci * internally protected by a mutex which is acquired non-atomically. 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * Let's improve this by using a customized locking scheme inspired 3862306a36Sopenharmony_ci * from I2C atomic transfer. See i2c_in_atomic_xfer_mode() for a 3962306a36Sopenharmony_ci * starting point. 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci if (system_state > SYSTEM_RUNNING && irqs_disabled()) 4262306a36Sopenharmony_ci mutex_trylock(mutex); 4362306a36Sopenharmony_ci else 4462306a36Sopenharmony_ci mutex_lock(mutex); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic void regmap_unlock_mutex(void *__mutex) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct mutex *mutex = __mutex; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci mutex_unlock(mutex); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic const struct regmap_config atc2603c_regmap_config = { 5562306a36Sopenharmony_ci .reg_bits = 8, 5662306a36Sopenharmony_ci .val_bits = 16, 5762306a36Sopenharmony_ci .max_register = ATC2603C_SADDR, 5862306a36Sopenharmony_ci .cache_type = REGCACHE_NONE, 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic const struct regmap_config atc2609a_regmap_config = { 6262306a36Sopenharmony_ci .reg_bits = 8, 6362306a36Sopenharmony_ci .val_bits = 16, 6462306a36Sopenharmony_ci .max_register = ATC2609A_SADDR, 6562306a36Sopenharmony_ci .cache_type = REGCACHE_NONE, 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic const struct regmap_irq atc2603c_regmap_irqs[] = { 6962306a36Sopenharmony_ci REGMAP_IRQ_REG(ATC2603C_IRQ_AUDIO, 0, ATC2603C_INTS_MSK_AUDIO), 7062306a36Sopenharmony_ci REGMAP_IRQ_REG(ATC2603C_IRQ_OV, 0, ATC2603C_INTS_MSK_OV), 7162306a36Sopenharmony_ci REGMAP_IRQ_REG(ATC2603C_IRQ_OC, 0, ATC2603C_INTS_MSK_OC), 7262306a36Sopenharmony_ci REGMAP_IRQ_REG(ATC2603C_IRQ_OT, 0, ATC2603C_INTS_MSK_OT), 7362306a36Sopenharmony_ci REGMAP_IRQ_REG(ATC2603C_IRQ_UV, 0, ATC2603C_INTS_MSK_UV), 7462306a36Sopenharmony_ci REGMAP_IRQ_REG(ATC2603C_IRQ_ALARM, 0, ATC2603C_INTS_MSK_ALARM), 7562306a36Sopenharmony_ci REGMAP_IRQ_REG(ATC2603C_IRQ_ONOFF, 0, ATC2603C_INTS_MSK_ONOFF), 7662306a36Sopenharmony_ci REGMAP_IRQ_REG(ATC2603C_IRQ_SGPIO, 0, ATC2603C_INTS_MSK_SGPIO), 7762306a36Sopenharmony_ci REGMAP_IRQ_REG(ATC2603C_IRQ_IR, 0, ATC2603C_INTS_MSK_IR), 7862306a36Sopenharmony_ci REGMAP_IRQ_REG(ATC2603C_IRQ_REMCON, 0, ATC2603C_INTS_MSK_REMCON), 7962306a36Sopenharmony_ci REGMAP_IRQ_REG(ATC2603C_IRQ_POWER_IN, 0, ATC2603C_INTS_MSK_POWERIN), 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic const struct regmap_irq atc2609a_regmap_irqs[] = { 8362306a36Sopenharmony_ci REGMAP_IRQ_REG(ATC2609A_IRQ_AUDIO, 0, ATC2609A_INTS_MSK_AUDIO), 8462306a36Sopenharmony_ci REGMAP_IRQ_REG(ATC2609A_IRQ_OV, 0, ATC2609A_INTS_MSK_OV), 8562306a36Sopenharmony_ci REGMAP_IRQ_REG(ATC2609A_IRQ_OC, 0, ATC2609A_INTS_MSK_OC), 8662306a36Sopenharmony_ci REGMAP_IRQ_REG(ATC2609A_IRQ_OT, 0, ATC2609A_INTS_MSK_OT), 8762306a36Sopenharmony_ci REGMAP_IRQ_REG(ATC2609A_IRQ_UV, 0, ATC2609A_INTS_MSK_UV), 8862306a36Sopenharmony_ci REGMAP_IRQ_REG(ATC2609A_IRQ_ALARM, 0, ATC2609A_INTS_MSK_ALARM), 8962306a36Sopenharmony_ci REGMAP_IRQ_REG(ATC2609A_IRQ_ONOFF, 0, ATC2609A_INTS_MSK_ONOFF), 9062306a36Sopenharmony_ci REGMAP_IRQ_REG(ATC2609A_IRQ_WKUP, 0, ATC2609A_INTS_MSK_WKUP), 9162306a36Sopenharmony_ci REGMAP_IRQ_REG(ATC2609A_IRQ_IR, 0, ATC2609A_INTS_MSK_IR), 9262306a36Sopenharmony_ci REGMAP_IRQ_REG(ATC2609A_IRQ_REMCON, 0, ATC2609A_INTS_MSK_REMCON), 9362306a36Sopenharmony_ci REGMAP_IRQ_REG(ATC2609A_IRQ_POWER_IN, 0, ATC2609A_INTS_MSK_POWERIN), 9462306a36Sopenharmony_ci}; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic const struct regmap_irq_chip atc2603c_regmap_irq_chip = { 9762306a36Sopenharmony_ci .name = "atc2603c", 9862306a36Sopenharmony_ci .irqs = atc2603c_regmap_irqs, 9962306a36Sopenharmony_ci .num_irqs = ARRAY_SIZE(atc2603c_regmap_irqs), 10062306a36Sopenharmony_ci .num_regs = 1, 10162306a36Sopenharmony_ci .status_base = ATC2603C_INTS_PD, 10262306a36Sopenharmony_ci .unmask_base = ATC2603C_INTS_MSK, 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic const struct regmap_irq_chip atc2609a_regmap_irq_chip = { 10662306a36Sopenharmony_ci .name = "atc2609a", 10762306a36Sopenharmony_ci .irqs = atc2609a_regmap_irqs, 10862306a36Sopenharmony_ci .num_irqs = ARRAY_SIZE(atc2609a_regmap_irqs), 10962306a36Sopenharmony_ci .num_regs = 1, 11062306a36Sopenharmony_ci .status_base = ATC2609A_INTS_PD, 11162306a36Sopenharmony_ci .unmask_base = ATC2609A_INTS_MSK, 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic const struct resource atc2603c_onkey_resources[] = { 11562306a36Sopenharmony_ci DEFINE_RES_IRQ(ATC2603C_IRQ_ONOFF), 11662306a36Sopenharmony_ci}; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic const struct resource atc2609a_onkey_resources[] = { 11962306a36Sopenharmony_ci DEFINE_RES_IRQ(ATC2609A_IRQ_ONOFF), 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic const struct mfd_cell atc2603c_mfd_cells[] = { 12362306a36Sopenharmony_ci { .name = "atc260x-regulator" }, 12462306a36Sopenharmony_ci { .name = "atc260x-pwrc" }, 12562306a36Sopenharmony_ci { 12662306a36Sopenharmony_ci .name = "atc260x-onkey", 12762306a36Sopenharmony_ci .num_resources = ARRAY_SIZE(atc2603c_onkey_resources), 12862306a36Sopenharmony_ci .resources = atc2603c_onkey_resources, 12962306a36Sopenharmony_ci }, 13062306a36Sopenharmony_ci}; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic const struct mfd_cell atc2609a_mfd_cells[] = { 13362306a36Sopenharmony_ci { .name = "atc260x-regulator" }, 13462306a36Sopenharmony_ci { .name = "atc260x-pwrc" }, 13562306a36Sopenharmony_ci { 13662306a36Sopenharmony_ci .name = "atc260x-onkey", 13762306a36Sopenharmony_ci .num_resources = ARRAY_SIZE(atc2609a_onkey_resources), 13862306a36Sopenharmony_ci .resources = atc2609a_onkey_resources, 13962306a36Sopenharmony_ci }, 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic const struct atc260x_init_regs atc2603c_init_regs = { 14362306a36Sopenharmony_ci .cmu_devrst = ATC2603C_CMU_DEVRST, 14462306a36Sopenharmony_ci .cmu_devrst_ints = ATC2603C_CMU_DEVRST_INTS, 14562306a36Sopenharmony_ci .ints_msk = ATC2603C_INTS_MSK, 14662306a36Sopenharmony_ci .pad_en = ATC2603C_PAD_EN, 14762306a36Sopenharmony_ci .pad_en_extirq = ATC2603C_PAD_EN_EXTIRQ, 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic const struct atc260x_init_regs atc2609a_init_regs = { 15162306a36Sopenharmony_ci .cmu_devrst = ATC2609A_CMU_DEVRST, 15262306a36Sopenharmony_ci .cmu_devrst_ints = ATC2609A_CMU_DEVRST_INTS, 15362306a36Sopenharmony_ci .ints_msk = ATC2609A_INTS_MSK, 15462306a36Sopenharmony_ci .pad_en = ATC2609A_PAD_EN, 15562306a36Sopenharmony_ci .pad_en_extirq = ATC2609A_PAD_EN_EXTIRQ, 15662306a36Sopenharmony_ci}; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic void atc260x_cmu_reset(struct atc260x *atc260x) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci const struct atc260x_init_regs *regs = atc260x->init_regs; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* Assert reset */ 16362306a36Sopenharmony_ci regmap_update_bits(atc260x->regmap, regs->cmu_devrst, 16462306a36Sopenharmony_ci regs->cmu_devrst_ints, ~regs->cmu_devrst_ints); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* De-assert reset */ 16762306a36Sopenharmony_ci regmap_update_bits(atc260x->regmap, regs->cmu_devrst, 16862306a36Sopenharmony_ci regs->cmu_devrst_ints, regs->cmu_devrst_ints); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void atc260x_dev_init(struct atc260x *atc260x) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci const struct atc260x_init_regs *regs = atc260x->init_regs; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* Initialize interrupt block */ 17662306a36Sopenharmony_ci atc260x_cmu_reset(atc260x); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* Disable all interrupt sources */ 17962306a36Sopenharmony_ci regmap_write(atc260x->regmap, regs->ints_msk, 0); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* Enable EXTIRQ pad */ 18262306a36Sopenharmony_ci regmap_update_bits(atc260x->regmap, regs->pad_en, 18362306a36Sopenharmony_ci regs->pad_en_extirq, regs->pad_en_extirq); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/** 18762306a36Sopenharmony_ci * atc260x_match_device(): Setup ATC260x variant related fields 18862306a36Sopenharmony_ci * 18962306a36Sopenharmony_ci * @atc260x: ATC260x device to setup (.dev field must be set) 19062306a36Sopenharmony_ci * @regmap_cfg: regmap config associated with this ATC260x device 19162306a36Sopenharmony_ci * 19262306a36Sopenharmony_ci * This lets the ATC260x core configure the MFD cells and register maps 19362306a36Sopenharmony_ci * for later use. 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_ciint atc260x_match_device(struct atc260x *atc260x, struct regmap_config *regmap_cfg) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct device *dev = atc260x->dev; 19862306a36Sopenharmony_ci const void *of_data; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci of_data = of_device_get_match_data(dev); 20162306a36Sopenharmony_ci if (!of_data) 20262306a36Sopenharmony_ci return -ENODEV; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci atc260x->ic_type = (unsigned long)of_data; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci switch (atc260x->ic_type) { 20762306a36Sopenharmony_ci case ATC2603C: 20862306a36Sopenharmony_ci *regmap_cfg = atc2603c_regmap_config; 20962306a36Sopenharmony_ci atc260x->regmap_irq_chip = &atc2603c_regmap_irq_chip; 21062306a36Sopenharmony_ci atc260x->cells = atc2603c_mfd_cells; 21162306a36Sopenharmony_ci atc260x->nr_cells = ARRAY_SIZE(atc2603c_mfd_cells); 21262306a36Sopenharmony_ci atc260x->type_name = "atc2603c"; 21362306a36Sopenharmony_ci atc260x->rev_reg = ATC2603C_CHIP_VER; 21462306a36Sopenharmony_ci atc260x->init_regs = &atc2603c_init_regs; 21562306a36Sopenharmony_ci break; 21662306a36Sopenharmony_ci case ATC2609A: 21762306a36Sopenharmony_ci *regmap_cfg = atc2609a_regmap_config; 21862306a36Sopenharmony_ci atc260x->regmap_irq_chip = &atc2609a_regmap_irq_chip; 21962306a36Sopenharmony_ci atc260x->cells = atc2609a_mfd_cells; 22062306a36Sopenharmony_ci atc260x->nr_cells = ARRAY_SIZE(atc2609a_mfd_cells); 22162306a36Sopenharmony_ci atc260x->type_name = "atc2609a"; 22262306a36Sopenharmony_ci atc260x->rev_reg = ATC2609A_CHIP_VER; 22362306a36Sopenharmony_ci atc260x->init_regs = &atc2609a_init_regs; 22462306a36Sopenharmony_ci break; 22562306a36Sopenharmony_ci default: 22662306a36Sopenharmony_ci dev_err(dev, "Unsupported ATC260x device type: %u\n", 22762306a36Sopenharmony_ci atc260x->ic_type); 22862306a36Sopenharmony_ci return -EINVAL; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci atc260x->regmap_mutex = devm_kzalloc(dev, sizeof(*atc260x->regmap_mutex), 23262306a36Sopenharmony_ci GFP_KERNEL); 23362306a36Sopenharmony_ci if (!atc260x->regmap_mutex) 23462306a36Sopenharmony_ci return -ENOMEM; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci mutex_init(atc260x->regmap_mutex); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci regmap_cfg->lock = regmap_lock_mutex, 23962306a36Sopenharmony_ci regmap_cfg->unlock = regmap_unlock_mutex, 24062306a36Sopenharmony_ci regmap_cfg->lock_arg = atc260x->regmap_mutex; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return 0; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(atc260x_match_device); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci/** 24762306a36Sopenharmony_ci * atc260x_device_probe(): Probe a configured ATC260x device 24862306a36Sopenharmony_ci * 24962306a36Sopenharmony_ci * @atc260x: ATC260x device to probe (must be configured) 25062306a36Sopenharmony_ci * 25162306a36Sopenharmony_ci * This function lets the ATC260x core register the ATC260x MFD devices 25262306a36Sopenharmony_ci * and IRQCHIP. The ATC260x device passed in must be fully configured 25362306a36Sopenharmony_ci * with atc260x_match_device, its IRQ set, and regmap created. 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_ciint atc260x_device_probe(struct atc260x *atc260x) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci struct device *dev = atc260x->dev; 25862306a36Sopenharmony_ci unsigned int chip_rev; 25962306a36Sopenharmony_ci int ret; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (!atc260x->irq) { 26262306a36Sopenharmony_ci dev_err(dev, "No interrupt support\n"); 26362306a36Sopenharmony_ci return -EINVAL; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* Initialize the hardware */ 26762306a36Sopenharmony_ci atc260x_dev_init(atc260x); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci ret = regmap_read(atc260x->regmap, atc260x->rev_reg, &chip_rev); 27062306a36Sopenharmony_ci if (ret) { 27162306a36Sopenharmony_ci dev_err(dev, "Failed to get chip revision\n"); 27262306a36Sopenharmony_ci return ret; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (chip_rev > ATC260X_CHIP_REV_MAX) { 27662306a36Sopenharmony_ci dev_err(dev, "Unknown chip revision: %u\n", chip_rev); 27762306a36Sopenharmony_ci return -EINVAL; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci atc260x->ic_ver = __ffs(chip_rev + 1U); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci dev_info(dev, "Detected chip type %s rev.%c\n", 28362306a36Sopenharmony_ci atc260x->type_name, 'A' + atc260x->ic_ver); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci ret = devm_regmap_add_irq_chip(dev, atc260x->regmap, atc260x->irq, IRQF_ONESHOT, 28662306a36Sopenharmony_ci -1, atc260x->regmap_irq_chip, &atc260x->irq_data); 28762306a36Sopenharmony_ci if (ret) { 28862306a36Sopenharmony_ci dev_err(dev, "Failed to add IRQ chip: %d\n", ret); 28962306a36Sopenharmony_ci return ret; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 29362306a36Sopenharmony_ci atc260x->cells, atc260x->nr_cells, NULL, 0, 29462306a36Sopenharmony_ci regmap_irq_get_domain(atc260x->irq_data)); 29562306a36Sopenharmony_ci if (ret) { 29662306a36Sopenharmony_ci dev_err(dev, "Failed to add child devices: %d\n", ret); 29762306a36Sopenharmony_ci regmap_del_irq_chip(atc260x->irq, atc260x->irq_data); 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci return ret; 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(atc260x_device_probe); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ciMODULE_DESCRIPTION("ATC260x PMICs Core support"); 30562306a36Sopenharmony_ciMODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>"); 30662306a36Sopenharmony_ciMODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>"); 30762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 308