123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Core support for ATC260x PMICs
- *
- * Copyright (C) 2019 Manivannan Sadhasivam <[email protected]>
- * Copyright (C) 2020 Cristian Ciocaltea <[email protected]>
- */
- #include <linux/interrupt.h>
- #include <linux/mfd/atc260x/core.h>
- #include <linux/mfd/core.h>
- #include <linux/module.h>
- #include <linux/of.h>
- #include <linux/of_device.h>
- #include <linux/regmap.h>
- #define ATC260X_CHIP_REV_MAX 31
- struct atc260x_init_regs {
- unsigned int cmu_devrst;
- unsigned int cmu_devrst_ints;
- unsigned int ints_msk;
- unsigned int pad_en;
- unsigned int pad_en_extirq;
- };
- static void regmap_lock_mutex(void *__mutex)
- {
- struct mutex *mutex = __mutex;
- /*
- * Using regmap within an atomic context (e.g. accessing a PMIC when
- * powering system down) is normally allowed only if the regmap type
- * is MMIO and the regcache type is either REGCACHE_NONE or
- * REGCACHE_FLAT. For slow buses like I2C and SPI, the regmap is
- * internally protected by a mutex which is acquired non-atomically.
- *
- * Let's improve this by using a customized locking scheme inspired
- * from I2C atomic transfer. See i2c_in_atomic_xfer_mode() for a
- * starting point.
- */
- if (system_state > SYSTEM_RUNNING && irqs_disabled())
- mutex_trylock(mutex);
- else
- mutex_lock(mutex);
- }
- static void regmap_unlock_mutex(void *__mutex)
- {
- struct mutex *mutex = __mutex;
- mutex_unlock(mutex);
- }
- static const struct regmap_config atc2603c_regmap_config = {
- .reg_bits = 8,
- .val_bits = 16,
- .max_register = ATC2603C_SADDR,
- .cache_type = REGCACHE_NONE,
- };
- static const struct regmap_config atc2609a_regmap_config = {
- .reg_bits = 8,
- .val_bits = 16,
- .max_register = ATC2609A_SADDR,
- .cache_type = REGCACHE_NONE,
- };
- static const struct regmap_irq atc2603c_regmap_irqs[] = {
- REGMAP_IRQ_REG(ATC2603C_IRQ_AUDIO, 0, ATC2603C_INTS_MSK_AUDIO),
- REGMAP_IRQ_REG(ATC2603C_IRQ_OV, 0, ATC2603C_INTS_MSK_OV),
- REGMAP_IRQ_REG(ATC2603C_IRQ_OC, 0, ATC2603C_INTS_MSK_OC),
- REGMAP_IRQ_REG(ATC2603C_IRQ_OT, 0, ATC2603C_INTS_MSK_OT),
- REGMAP_IRQ_REG(ATC2603C_IRQ_UV, 0, ATC2603C_INTS_MSK_UV),
- REGMAP_IRQ_REG(ATC2603C_IRQ_ALARM, 0, ATC2603C_INTS_MSK_ALARM),
- REGMAP_IRQ_REG(ATC2603C_IRQ_ONOFF, 0, ATC2603C_INTS_MSK_ONOFF),
- REGMAP_IRQ_REG(ATC2603C_IRQ_SGPIO, 0, ATC2603C_INTS_MSK_SGPIO),
- REGMAP_IRQ_REG(ATC2603C_IRQ_IR, 0, ATC2603C_INTS_MSK_IR),
- REGMAP_IRQ_REG(ATC2603C_IRQ_REMCON, 0, ATC2603C_INTS_MSK_REMCON),
- REGMAP_IRQ_REG(ATC2603C_IRQ_POWER_IN, 0, ATC2603C_INTS_MSK_POWERIN),
- };
- static const struct regmap_irq atc2609a_regmap_irqs[] = {
- REGMAP_IRQ_REG(ATC2609A_IRQ_AUDIO, 0, ATC2609A_INTS_MSK_AUDIO),
- REGMAP_IRQ_REG(ATC2609A_IRQ_OV, 0, ATC2609A_INTS_MSK_OV),
- REGMAP_IRQ_REG(ATC2609A_IRQ_OC, 0, ATC2609A_INTS_MSK_OC),
- REGMAP_IRQ_REG(ATC2609A_IRQ_OT, 0, ATC2609A_INTS_MSK_OT),
- REGMAP_IRQ_REG(ATC2609A_IRQ_UV, 0, ATC2609A_INTS_MSK_UV),
- REGMAP_IRQ_REG(ATC2609A_IRQ_ALARM, 0, ATC2609A_INTS_MSK_ALARM),
- REGMAP_IRQ_REG(ATC2609A_IRQ_ONOFF, 0, ATC2609A_INTS_MSK_ONOFF),
- REGMAP_IRQ_REG(ATC2609A_IRQ_WKUP, 0, ATC2609A_INTS_MSK_WKUP),
- REGMAP_IRQ_REG(ATC2609A_IRQ_IR, 0, ATC2609A_INTS_MSK_IR),
- REGMAP_IRQ_REG(ATC2609A_IRQ_REMCON, 0, ATC2609A_INTS_MSK_REMCON),
- REGMAP_IRQ_REG(ATC2609A_IRQ_POWER_IN, 0, ATC2609A_INTS_MSK_POWERIN),
- };
- static const struct regmap_irq_chip atc2603c_regmap_irq_chip = {
- .name = "atc2603c",
- .irqs = atc2603c_regmap_irqs,
- .num_irqs = ARRAY_SIZE(atc2603c_regmap_irqs),
- .num_regs = 1,
- .status_base = ATC2603C_INTS_PD,
- .mask_base = ATC2603C_INTS_MSK,
- .mask_invert = true,
- };
- static const struct regmap_irq_chip atc2609a_regmap_irq_chip = {
- .name = "atc2609a",
- .irqs = atc2609a_regmap_irqs,
- .num_irqs = ARRAY_SIZE(atc2609a_regmap_irqs),
- .num_regs = 1,
- .status_base = ATC2609A_INTS_PD,
- .mask_base = ATC2609A_INTS_MSK,
- .mask_invert = true,
- };
- static const struct resource atc2603c_onkey_resources[] = {
- DEFINE_RES_IRQ(ATC2603C_IRQ_ONOFF),
- };
- static const struct resource atc2609a_onkey_resources[] = {
- DEFINE_RES_IRQ(ATC2609A_IRQ_ONOFF),
- };
- static const struct mfd_cell atc2603c_mfd_cells[] = {
- { .name = "atc260x-regulator" },
- { .name = "atc260x-pwrc" },
- {
- .name = "atc260x-onkey",
- .num_resources = ARRAY_SIZE(atc2603c_onkey_resources),
- .resources = atc2603c_onkey_resources,
- },
- };
- static const struct mfd_cell atc2609a_mfd_cells[] = {
- { .name = "atc260x-regulator" },
- { .name = "atc260x-pwrc" },
- {
- .name = "atc260x-onkey",
- .num_resources = ARRAY_SIZE(atc2609a_onkey_resources),
- .resources = atc2609a_onkey_resources,
- },
- };
- static const struct atc260x_init_regs atc2603c_init_regs = {
- .cmu_devrst = ATC2603C_CMU_DEVRST,
- .cmu_devrst_ints = ATC2603C_CMU_DEVRST_INTS,
- .ints_msk = ATC2603C_INTS_MSK,
- .pad_en = ATC2603C_PAD_EN,
- .pad_en_extirq = ATC2603C_PAD_EN_EXTIRQ,
- };
- static const struct atc260x_init_regs atc2609a_init_regs = {
- .cmu_devrst = ATC2609A_CMU_DEVRST,
- .cmu_devrst_ints = ATC2609A_CMU_DEVRST_INTS,
- .ints_msk = ATC2609A_INTS_MSK,
- .pad_en = ATC2609A_PAD_EN,
- .pad_en_extirq = ATC2609A_PAD_EN_EXTIRQ,
- };
- static void atc260x_cmu_reset(struct atc260x *atc260x)
- {
- const struct atc260x_init_regs *regs = atc260x->init_regs;
- /* Assert reset */
- regmap_update_bits(atc260x->regmap, regs->cmu_devrst,
- regs->cmu_devrst_ints, ~regs->cmu_devrst_ints);
- /* De-assert reset */
- regmap_update_bits(atc260x->regmap, regs->cmu_devrst,
- regs->cmu_devrst_ints, regs->cmu_devrst_ints);
- }
- static void atc260x_dev_init(struct atc260x *atc260x)
- {
- const struct atc260x_init_regs *regs = atc260x->init_regs;
- /* Initialize interrupt block */
- atc260x_cmu_reset(atc260x);
- /* Disable all interrupt sources */
- regmap_write(atc260x->regmap, regs->ints_msk, 0);
- /* Enable EXTIRQ pad */
- regmap_update_bits(atc260x->regmap, regs->pad_en,
- regs->pad_en_extirq, regs->pad_en_extirq);
- }
- /**
- * atc260x_match_device(): Setup ATC260x variant related fields
- *
- * @atc260x: ATC260x device to setup (.dev field must be set)
- * @regmap_cfg: regmap config associated with this ATC260x device
- *
- * This lets the ATC260x core configure the MFD cells and register maps
- * for later use.
- */
- int atc260x_match_device(struct atc260x *atc260x, struct regmap_config *regmap_cfg)
- {
- struct device *dev = atc260x->dev;
- const void *of_data;
- of_data = of_device_get_match_data(dev);
- if (!of_data)
- return -ENODEV;
- atc260x->ic_type = (unsigned long)of_data;
- switch (atc260x->ic_type) {
- case ATC2603C:
- *regmap_cfg = atc2603c_regmap_config;
- atc260x->regmap_irq_chip = &atc2603c_regmap_irq_chip;
- atc260x->cells = atc2603c_mfd_cells;
- atc260x->nr_cells = ARRAY_SIZE(atc2603c_mfd_cells);
- atc260x->type_name = "atc2603c";
- atc260x->rev_reg = ATC2603C_CHIP_VER;
- atc260x->init_regs = &atc2603c_init_regs;
- break;
- case ATC2609A:
- *regmap_cfg = atc2609a_regmap_config;
- atc260x->regmap_irq_chip = &atc2609a_regmap_irq_chip;
- atc260x->cells = atc2609a_mfd_cells;
- atc260x->nr_cells = ARRAY_SIZE(atc2609a_mfd_cells);
- atc260x->type_name = "atc2609a";
- atc260x->rev_reg = ATC2609A_CHIP_VER;
- atc260x->init_regs = &atc2609a_init_regs;
- break;
- default:
- dev_err(dev, "Unsupported ATC260x device type: %u\n",
- atc260x->ic_type);
- return -EINVAL;
- }
- atc260x->regmap_mutex = devm_kzalloc(dev, sizeof(*atc260x->regmap_mutex),
- GFP_KERNEL);
- if (!atc260x->regmap_mutex)
- return -ENOMEM;
- mutex_init(atc260x->regmap_mutex);
- regmap_cfg->lock = regmap_lock_mutex,
- regmap_cfg->unlock = regmap_unlock_mutex,
- regmap_cfg->lock_arg = atc260x->regmap_mutex;
- return 0;
- }
- EXPORT_SYMBOL_GPL(atc260x_match_device);
- /**
- * atc260x_device_probe(): Probe a configured ATC260x device
- *
- * @atc260x: ATC260x device to probe (must be configured)
- *
- * This function lets the ATC260x core register the ATC260x MFD devices
- * and IRQCHIP. The ATC260x device passed in must be fully configured
- * with atc260x_match_device, its IRQ set, and regmap created.
- */
- int atc260x_device_probe(struct atc260x *atc260x)
- {
- struct device *dev = atc260x->dev;
- unsigned int chip_rev;
- int ret;
- if (!atc260x->irq) {
- dev_err(dev, "No interrupt support\n");
- return -EINVAL;
- }
- /* Initialize the hardware */
- atc260x_dev_init(atc260x);
- ret = regmap_read(atc260x->regmap, atc260x->rev_reg, &chip_rev);
- if (ret) {
- dev_err(dev, "Failed to get chip revision\n");
- return ret;
- }
- if (chip_rev > ATC260X_CHIP_REV_MAX) {
- dev_err(dev, "Unknown chip revision: %u\n", chip_rev);
- return -EINVAL;
- }
- atc260x->ic_ver = __ffs(chip_rev + 1U);
- dev_info(dev, "Detected chip type %s rev.%c\n",
- atc260x->type_name, 'A' + atc260x->ic_ver);
- ret = devm_regmap_add_irq_chip(dev, atc260x->regmap, atc260x->irq, IRQF_ONESHOT,
- -1, atc260x->regmap_irq_chip, &atc260x->irq_data);
- if (ret) {
- dev_err(dev, "Failed to add IRQ chip: %d\n", ret);
- return ret;
- }
- ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
- atc260x->cells, atc260x->nr_cells, NULL, 0,
- regmap_irq_get_domain(atc260x->irq_data));
- if (ret) {
- dev_err(dev, "Failed to add child devices: %d\n", ret);
- regmap_del_irq_chip(atc260x->irq, atc260x->irq_data);
- }
- return ret;
- }
- EXPORT_SYMBOL_GPL(atc260x_device_probe);
- MODULE_DESCRIPTION("ATC260x PMICs Core support");
- MODULE_AUTHOR("Manivannan Sadhasivam <[email protected]>");
- MODULE_AUTHOR("Cristian Ciocaltea <[email protected]>");
- MODULE_LICENSE("GPL");
|