Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Core support for ATC260x PMICs
0004  *
0005  * Copyright (C) 2019 Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
0006  * Copyright (C) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
0007  */
0008 
0009 #include <linux/interrupt.h>
0010 #include <linux/mfd/atc260x/core.h>
0011 #include <linux/mfd/core.h>
0012 #include <linux/module.h>
0013 #include <linux/of.h>
0014 #include <linux/of_device.h>
0015 #include <linux/regmap.h>
0016 
0017 #define ATC260X_CHIP_REV_MAX    31
0018 
0019 struct atc260x_init_regs {
0020     unsigned int cmu_devrst;
0021     unsigned int cmu_devrst_ints;
0022     unsigned int ints_msk;
0023     unsigned int pad_en;
0024     unsigned int pad_en_extirq;
0025 };
0026 
0027 static void regmap_lock_mutex(void *__mutex)
0028 {
0029     struct mutex *mutex = __mutex;
0030 
0031     /*
0032      * Using regmap within an atomic context (e.g. accessing a PMIC when
0033      * powering system down) is normally allowed only if the regmap type
0034      * is MMIO and the regcache type is either REGCACHE_NONE or
0035      * REGCACHE_FLAT. For slow buses like I2C and SPI, the regmap is
0036      * internally protected by a mutex which is acquired non-atomically.
0037      *
0038      * Let's improve this by using a customized locking scheme inspired
0039      * from I2C atomic transfer. See i2c_in_atomic_xfer_mode() for a
0040      * starting point.
0041      */
0042     if (system_state > SYSTEM_RUNNING && irqs_disabled())
0043         mutex_trylock(mutex);
0044     else
0045         mutex_lock(mutex);
0046 }
0047 
0048 static void regmap_unlock_mutex(void *__mutex)
0049 {
0050     struct mutex *mutex = __mutex;
0051 
0052     mutex_unlock(mutex);
0053 }
0054 
0055 static const struct regmap_config atc2603c_regmap_config = {
0056     .reg_bits = 8,
0057     .val_bits = 16,
0058     .max_register = ATC2603C_SADDR,
0059     .cache_type = REGCACHE_NONE,
0060 };
0061 
0062 static const struct regmap_config atc2609a_regmap_config = {
0063     .reg_bits = 8,
0064     .val_bits = 16,
0065     .max_register = ATC2609A_SADDR,
0066     .cache_type = REGCACHE_NONE,
0067 };
0068 
0069 static const struct regmap_irq atc2603c_regmap_irqs[] = {
0070     REGMAP_IRQ_REG(ATC2603C_IRQ_AUDIO,  0, ATC2603C_INTS_MSK_AUDIO),
0071     REGMAP_IRQ_REG(ATC2603C_IRQ_OV,     0, ATC2603C_INTS_MSK_OV),
0072     REGMAP_IRQ_REG(ATC2603C_IRQ_OC,     0, ATC2603C_INTS_MSK_OC),
0073     REGMAP_IRQ_REG(ATC2603C_IRQ_OT,     0, ATC2603C_INTS_MSK_OT),
0074     REGMAP_IRQ_REG(ATC2603C_IRQ_UV,     0, ATC2603C_INTS_MSK_UV),
0075     REGMAP_IRQ_REG(ATC2603C_IRQ_ALARM,  0, ATC2603C_INTS_MSK_ALARM),
0076     REGMAP_IRQ_REG(ATC2603C_IRQ_ONOFF,  0, ATC2603C_INTS_MSK_ONOFF),
0077     REGMAP_IRQ_REG(ATC2603C_IRQ_SGPIO,  0, ATC2603C_INTS_MSK_SGPIO),
0078     REGMAP_IRQ_REG(ATC2603C_IRQ_IR,     0, ATC2603C_INTS_MSK_IR),
0079     REGMAP_IRQ_REG(ATC2603C_IRQ_REMCON, 0, ATC2603C_INTS_MSK_REMCON),
0080     REGMAP_IRQ_REG(ATC2603C_IRQ_POWER_IN,   0, ATC2603C_INTS_MSK_POWERIN),
0081 };
0082 
0083 static const struct regmap_irq atc2609a_regmap_irqs[] = {
0084     REGMAP_IRQ_REG(ATC2609A_IRQ_AUDIO,  0, ATC2609A_INTS_MSK_AUDIO),
0085     REGMAP_IRQ_REG(ATC2609A_IRQ_OV,     0, ATC2609A_INTS_MSK_OV),
0086     REGMAP_IRQ_REG(ATC2609A_IRQ_OC,     0, ATC2609A_INTS_MSK_OC),
0087     REGMAP_IRQ_REG(ATC2609A_IRQ_OT,     0, ATC2609A_INTS_MSK_OT),
0088     REGMAP_IRQ_REG(ATC2609A_IRQ_UV,     0, ATC2609A_INTS_MSK_UV),
0089     REGMAP_IRQ_REG(ATC2609A_IRQ_ALARM,  0, ATC2609A_INTS_MSK_ALARM),
0090     REGMAP_IRQ_REG(ATC2609A_IRQ_ONOFF,  0, ATC2609A_INTS_MSK_ONOFF),
0091     REGMAP_IRQ_REG(ATC2609A_IRQ_WKUP,   0, ATC2609A_INTS_MSK_WKUP),
0092     REGMAP_IRQ_REG(ATC2609A_IRQ_IR,     0, ATC2609A_INTS_MSK_IR),
0093     REGMAP_IRQ_REG(ATC2609A_IRQ_REMCON, 0, ATC2609A_INTS_MSK_REMCON),
0094     REGMAP_IRQ_REG(ATC2609A_IRQ_POWER_IN,   0, ATC2609A_INTS_MSK_POWERIN),
0095 };
0096 
0097 static const struct regmap_irq_chip atc2603c_regmap_irq_chip = {
0098     .name = "atc2603c",
0099     .irqs = atc2603c_regmap_irqs,
0100     .num_irqs = ARRAY_SIZE(atc2603c_regmap_irqs),
0101     .num_regs = 1,
0102     .status_base = ATC2603C_INTS_PD,
0103     .mask_base = ATC2603C_INTS_MSK,
0104     .mask_invert = true,
0105 };
0106 
0107 static const struct regmap_irq_chip atc2609a_regmap_irq_chip = {
0108     .name = "atc2609a",
0109     .irqs = atc2609a_regmap_irqs,
0110     .num_irqs = ARRAY_SIZE(atc2609a_regmap_irqs),
0111     .num_regs = 1,
0112     .status_base = ATC2609A_INTS_PD,
0113     .mask_base = ATC2609A_INTS_MSK,
0114     .mask_invert = true,
0115 };
0116 
0117 static const struct resource atc2603c_onkey_resources[] = {
0118     DEFINE_RES_IRQ(ATC2603C_IRQ_ONOFF),
0119 };
0120 
0121 static const struct resource atc2609a_onkey_resources[] = {
0122     DEFINE_RES_IRQ(ATC2609A_IRQ_ONOFF),
0123 };
0124 
0125 static const struct mfd_cell atc2603c_mfd_cells[] = {
0126     { .name = "atc260x-regulator" },
0127     { .name = "atc260x-pwrc" },
0128     {
0129         .name = "atc260x-onkey",
0130         .num_resources = ARRAY_SIZE(atc2603c_onkey_resources),
0131         .resources = atc2603c_onkey_resources,
0132     },
0133 };
0134 
0135 static const struct mfd_cell atc2609a_mfd_cells[] = {
0136     { .name = "atc260x-regulator" },
0137     { .name = "atc260x-pwrc" },
0138     {
0139         .name = "atc260x-onkey",
0140         .num_resources = ARRAY_SIZE(atc2609a_onkey_resources),
0141         .resources = atc2609a_onkey_resources,
0142     },
0143 };
0144 
0145 static const struct atc260x_init_regs atc2603c_init_regs = {
0146     .cmu_devrst = ATC2603C_CMU_DEVRST,
0147     .cmu_devrst_ints = ATC2603C_CMU_DEVRST_INTS,
0148     .ints_msk = ATC2603C_INTS_MSK,
0149     .pad_en = ATC2603C_PAD_EN,
0150     .pad_en_extirq = ATC2603C_PAD_EN_EXTIRQ,
0151 };
0152 
0153 static const struct atc260x_init_regs atc2609a_init_regs = {
0154     .cmu_devrst = ATC2609A_CMU_DEVRST,
0155     .cmu_devrst_ints = ATC2609A_CMU_DEVRST_INTS,
0156     .ints_msk = ATC2609A_INTS_MSK,
0157     .pad_en = ATC2609A_PAD_EN,
0158     .pad_en_extirq = ATC2609A_PAD_EN_EXTIRQ,
0159 };
0160 
0161 static void atc260x_cmu_reset(struct atc260x *atc260x)
0162 {
0163     const struct atc260x_init_regs *regs = atc260x->init_regs;
0164 
0165     /* Assert reset */
0166     regmap_update_bits(atc260x->regmap, regs->cmu_devrst,
0167                regs->cmu_devrst_ints, ~regs->cmu_devrst_ints);
0168 
0169     /* De-assert reset */
0170     regmap_update_bits(atc260x->regmap, regs->cmu_devrst,
0171                regs->cmu_devrst_ints, regs->cmu_devrst_ints);
0172 }
0173 
0174 static void atc260x_dev_init(struct atc260x *atc260x)
0175 {
0176     const struct atc260x_init_regs *regs = atc260x->init_regs;
0177 
0178     /* Initialize interrupt block */
0179     atc260x_cmu_reset(atc260x);
0180 
0181     /* Disable all interrupt sources */
0182     regmap_write(atc260x->regmap, regs->ints_msk, 0);
0183 
0184     /* Enable EXTIRQ pad */
0185     regmap_update_bits(atc260x->regmap, regs->pad_en,
0186                regs->pad_en_extirq, regs->pad_en_extirq);
0187 }
0188 
0189 /**
0190  * atc260x_match_device(): Setup ATC260x variant related fields
0191  *
0192  * @atc260x: ATC260x device to setup (.dev field must be set)
0193  * @regmap_cfg: regmap config associated with this ATC260x device
0194  *
0195  * This lets the ATC260x core configure the MFD cells and register maps
0196  * for later use.
0197  */
0198 int atc260x_match_device(struct atc260x *atc260x, struct regmap_config *regmap_cfg)
0199 {
0200     struct device *dev = atc260x->dev;
0201     const void *of_data;
0202 
0203     of_data = of_device_get_match_data(dev);
0204     if (!of_data)
0205         return -ENODEV;
0206 
0207     atc260x->ic_type = (unsigned long)of_data;
0208 
0209     switch (atc260x->ic_type) {
0210     case ATC2603C:
0211         *regmap_cfg = atc2603c_regmap_config;
0212         atc260x->regmap_irq_chip = &atc2603c_regmap_irq_chip;
0213         atc260x->cells = atc2603c_mfd_cells;
0214         atc260x->nr_cells = ARRAY_SIZE(atc2603c_mfd_cells);
0215         atc260x->type_name = "atc2603c";
0216         atc260x->rev_reg = ATC2603C_CHIP_VER;
0217         atc260x->init_regs = &atc2603c_init_regs;
0218         break;
0219     case ATC2609A:
0220         *regmap_cfg = atc2609a_regmap_config;
0221         atc260x->regmap_irq_chip = &atc2609a_regmap_irq_chip;
0222         atc260x->cells = atc2609a_mfd_cells;
0223         atc260x->nr_cells = ARRAY_SIZE(atc2609a_mfd_cells);
0224         atc260x->type_name = "atc2609a";
0225         atc260x->rev_reg = ATC2609A_CHIP_VER;
0226         atc260x->init_regs = &atc2609a_init_regs;
0227         break;
0228     default:
0229         dev_err(dev, "Unsupported ATC260x device type: %u\n",
0230             atc260x->ic_type);
0231         return -EINVAL;
0232     }
0233 
0234     atc260x->regmap_mutex = devm_kzalloc(dev, sizeof(*atc260x->regmap_mutex),
0235                          GFP_KERNEL);
0236     if (!atc260x->regmap_mutex)
0237         return -ENOMEM;
0238 
0239     mutex_init(atc260x->regmap_mutex);
0240 
0241     regmap_cfg->lock = regmap_lock_mutex,
0242     regmap_cfg->unlock = regmap_unlock_mutex,
0243     regmap_cfg->lock_arg = atc260x->regmap_mutex;
0244 
0245     return 0;
0246 }
0247 EXPORT_SYMBOL_GPL(atc260x_match_device);
0248 
0249 /**
0250  * atc260x_device_probe(): Probe a configured ATC260x device
0251  *
0252  * @atc260x: ATC260x device to probe (must be configured)
0253  *
0254  * This function lets the ATC260x core register the ATC260x MFD devices
0255  * and IRQCHIP. The ATC260x device passed in must be fully configured
0256  * with atc260x_match_device, its IRQ set, and regmap created.
0257  */
0258 int atc260x_device_probe(struct atc260x *atc260x)
0259 {
0260     struct device *dev = atc260x->dev;
0261     unsigned int chip_rev;
0262     int ret;
0263 
0264     if (!atc260x->irq) {
0265         dev_err(dev, "No interrupt support\n");
0266         return -EINVAL;
0267     }
0268 
0269     /* Initialize the hardware */
0270     atc260x_dev_init(atc260x);
0271 
0272     ret = regmap_read(atc260x->regmap, atc260x->rev_reg, &chip_rev);
0273     if (ret) {
0274         dev_err(dev, "Failed to get chip revision\n");
0275         return ret;
0276     }
0277 
0278     if (chip_rev > ATC260X_CHIP_REV_MAX) {
0279         dev_err(dev, "Unknown chip revision: %u\n", chip_rev);
0280         return -EINVAL;
0281     }
0282 
0283     atc260x->ic_ver = __ffs(chip_rev + 1U);
0284 
0285     dev_info(dev, "Detected chip type %s rev.%c\n",
0286          atc260x->type_name, 'A' + atc260x->ic_ver);
0287 
0288     ret = devm_regmap_add_irq_chip(dev, atc260x->regmap, atc260x->irq, IRQF_ONESHOT,
0289                        -1, atc260x->regmap_irq_chip, &atc260x->irq_data);
0290     if (ret) {
0291         dev_err(dev, "Failed to add IRQ chip: %d\n", ret);
0292         return ret;
0293     }
0294 
0295     ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
0296                    atc260x->cells, atc260x->nr_cells, NULL, 0,
0297                    regmap_irq_get_domain(atc260x->irq_data));
0298     if (ret) {
0299         dev_err(dev, "Failed to add child devices: %d\n", ret);
0300         regmap_del_irq_chip(atc260x->irq, atc260x->irq_data);
0301     }
0302 
0303     return ret;
0304 }
0305 EXPORT_SYMBOL_GPL(atc260x_device_probe);
0306 
0307 MODULE_DESCRIPTION("ATC260x PMICs Core support");
0308 MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
0309 MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>");
0310 MODULE_LICENSE("GPL");