Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Motorola CPCAP PMIC core driver
0004  *
0005  * Copyright (C) 2016 Tony Lindgren <tony@atomide.com>
0006  */
0007 
0008 #include <linux/device.h>
0009 #include <linux/err.h>
0010 #include <linux/interrupt.h>
0011 #include <linux/irq.h>
0012 #include <linux/kernel.h>
0013 #include <linux/module.h>
0014 #include <linux/of_device.h>
0015 #include <linux/regmap.h>
0016 #include <linux/sysfs.h>
0017 
0018 #include <linux/mfd/core.h>
0019 #include <linux/mfd/motorola-cpcap.h>
0020 #include <linux/spi/spi.h>
0021 
0022 #define CPCAP_NR_IRQ_REG_BANKS  6
0023 #define CPCAP_NR_IRQ_CHIPS  3
0024 #define CPCAP_REGISTER_SIZE 4
0025 #define CPCAP_REGISTER_BITS 16
0026 
0027 struct cpcap_ddata {
0028     struct spi_device *spi;
0029     struct regmap_irq *irqs;
0030     struct regmap_irq_chip_data *irqdata[CPCAP_NR_IRQ_CHIPS];
0031     const struct regmap_config *regmap_conf;
0032     struct regmap *regmap;
0033 };
0034 
0035 static int cpcap_sense_irq(struct regmap *regmap, int irq)
0036 {
0037     int regnum = irq / CPCAP_REGISTER_BITS;
0038     int mask = BIT(irq % CPCAP_REGISTER_BITS);
0039     int reg = CPCAP_REG_INTS1 + (regnum * CPCAP_REGISTER_SIZE);
0040     int err, val;
0041 
0042     if (reg < CPCAP_REG_INTS1 || reg > CPCAP_REG_INTS4)
0043         return -EINVAL;
0044 
0045     err = regmap_read(regmap, reg, &val);
0046     if (err)
0047         return err;
0048 
0049     return !!(val & mask);
0050 }
0051 
0052 int cpcap_sense_virq(struct regmap *regmap, int virq)
0053 {
0054     struct regmap_irq_chip_data *d = irq_get_chip_data(virq);
0055     int irq_base = regmap_irq_chip_get_base(d);
0056 
0057     return cpcap_sense_irq(regmap, virq - irq_base);
0058 }
0059 EXPORT_SYMBOL_GPL(cpcap_sense_virq);
0060 
0061 static int cpcap_check_revision(struct cpcap_ddata *cpcap)
0062 {
0063     u16 vendor, rev;
0064     int ret;
0065 
0066     ret = cpcap_get_vendor(&cpcap->spi->dev, cpcap->regmap, &vendor);
0067     if (ret)
0068         return ret;
0069 
0070     ret = cpcap_get_revision(&cpcap->spi->dev, cpcap->regmap, &rev);
0071     if (ret)
0072         return ret;
0073 
0074     dev_info(&cpcap->spi->dev, "CPCAP vendor: %s rev: %i.%i (%x)\n",
0075          vendor == CPCAP_VENDOR_ST ? "ST" : "TI",
0076          CPCAP_REVISION_MAJOR(rev), CPCAP_REVISION_MINOR(rev),
0077          rev);
0078 
0079     if (rev < CPCAP_REVISION_2_1) {
0080         dev_info(&cpcap->spi->dev,
0081              "Please add old CPCAP revision support as needed\n");
0082         return -ENODEV;
0083     }
0084 
0085     return 0;
0086 }
0087 
0088 /*
0089  * First two irq chips are the two private macro interrupt chips, the third
0090  * irq chip is for register banks 1 - 4 and is available for drivers to use.
0091  */
0092 static struct regmap_irq_chip cpcap_irq_chip[CPCAP_NR_IRQ_CHIPS] = {
0093     {
0094         .name = "cpcap-m2",
0095         .num_regs = 1,
0096         .status_base = CPCAP_REG_MI1,
0097         .ack_base = CPCAP_REG_MI1,
0098         .mask_base = CPCAP_REG_MIM1,
0099         .use_ack = true,
0100         .clear_ack = true,
0101     },
0102     {
0103         .name = "cpcap-m2",
0104         .num_regs = 1,
0105         .status_base = CPCAP_REG_MI2,
0106         .ack_base = CPCAP_REG_MI2,
0107         .mask_base = CPCAP_REG_MIM2,
0108         .use_ack = true,
0109         .clear_ack = true,
0110     },
0111     {
0112         .name = "cpcap1-4",
0113         .num_regs = 4,
0114         .status_base = CPCAP_REG_INT1,
0115         .ack_base = CPCAP_REG_INT1,
0116         .mask_base = CPCAP_REG_INTM1,
0117         .use_ack = true,
0118         .clear_ack = true,
0119     },
0120 };
0121 
0122 static void cpcap_init_one_regmap_irq(struct cpcap_ddata *cpcap,
0123                       struct regmap_irq *rirq,
0124                       int irq_base, int irq)
0125 {
0126     unsigned int reg_offset;
0127     unsigned int bit, mask;
0128 
0129     reg_offset = irq - irq_base;
0130     reg_offset /= cpcap->regmap_conf->val_bits;
0131     reg_offset *= cpcap->regmap_conf->reg_stride;
0132 
0133     bit = irq % cpcap->regmap_conf->val_bits;
0134     mask = (1 << bit);
0135 
0136     rirq->reg_offset = reg_offset;
0137     rirq->mask = mask;
0138 }
0139 
0140 static int cpcap_init_irq_chip(struct cpcap_ddata *cpcap, int irq_chip,
0141                    int irq_start, int nr_irqs)
0142 {
0143     struct regmap_irq_chip *chip = &cpcap_irq_chip[irq_chip];
0144     int i, ret;
0145 
0146     for (i = irq_start; i < irq_start + nr_irqs; i++) {
0147         struct regmap_irq *rirq = &cpcap->irqs[i];
0148 
0149         cpcap_init_one_regmap_irq(cpcap, rirq, irq_start, i);
0150     }
0151     chip->irqs = &cpcap->irqs[irq_start];
0152     chip->num_irqs = nr_irqs;
0153     chip->irq_drv_data = cpcap;
0154 
0155     ret = devm_regmap_add_irq_chip(&cpcap->spi->dev, cpcap->regmap,
0156                        cpcap->spi->irq,
0157                        irq_get_trigger_type(cpcap->spi->irq) |
0158                        IRQF_SHARED, -1,
0159                        chip, &cpcap->irqdata[irq_chip]);
0160     if (ret) {
0161         dev_err(&cpcap->spi->dev, "could not add irq chip %i: %i\n",
0162             irq_chip, ret);
0163         return ret;
0164     }
0165 
0166     return 0;
0167 }
0168 
0169 static int cpcap_init_irq(struct cpcap_ddata *cpcap)
0170 {
0171     int ret;
0172 
0173     cpcap->irqs = devm_kzalloc(&cpcap->spi->dev,
0174                    array3_size(sizeof(*cpcap->irqs),
0175                            CPCAP_NR_IRQ_REG_BANKS,
0176                            cpcap->regmap_conf->val_bits),
0177                    GFP_KERNEL);
0178     if (!cpcap->irqs)
0179         return -ENOMEM;
0180 
0181     ret = cpcap_init_irq_chip(cpcap, 0, 0, 16);
0182     if (ret)
0183         return ret;
0184 
0185     ret = cpcap_init_irq_chip(cpcap, 1, 16, 16);
0186     if (ret)
0187         return ret;
0188 
0189     ret = cpcap_init_irq_chip(cpcap, 2, 32, 64);
0190     if (ret)
0191         return ret;
0192 
0193     enable_irq_wake(cpcap->spi->irq);
0194 
0195     return 0;
0196 }
0197 
0198 static const struct of_device_id cpcap_of_match[] = {
0199     { .compatible = "motorola,cpcap", },
0200     { .compatible = "st,6556002", },
0201     {},
0202 };
0203 MODULE_DEVICE_TABLE(of, cpcap_of_match);
0204 
0205 static const struct spi_device_id cpcap_spi_ids[] = {
0206     { .name = "cpcap", },
0207     { .name = "6556002", },
0208     {},
0209 };
0210 MODULE_DEVICE_TABLE(spi, cpcap_spi_ids);
0211 
0212 static const struct regmap_config cpcap_regmap_config = {
0213     .reg_bits = 16,
0214     .reg_stride = 4,
0215     .pad_bits = 0,
0216     .val_bits = 16,
0217     .write_flag_mask = 0x8000,
0218     .max_register = CPCAP_REG_ST_TEST2,
0219     .cache_type = REGCACHE_NONE,
0220     .reg_format_endian = REGMAP_ENDIAN_LITTLE,
0221     .val_format_endian = REGMAP_ENDIAN_LITTLE,
0222 };
0223 
0224 #ifdef CONFIG_PM_SLEEP
0225 static int cpcap_suspend(struct device *dev)
0226 {
0227     struct spi_device *spi = to_spi_device(dev);
0228 
0229     disable_irq(spi->irq);
0230 
0231     return 0;
0232 }
0233 
0234 static int cpcap_resume(struct device *dev)
0235 {
0236     struct spi_device *spi = to_spi_device(dev);
0237 
0238     enable_irq(spi->irq);
0239 
0240     return 0;
0241 }
0242 #endif
0243 
0244 static SIMPLE_DEV_PM_OPS(cpcap_pm, cpcap_suspend, cpcap_resume);
0245 
0246 static const struct mfd_cell cpcap_mfd_devices[] = {
0247     {
0248         .name          = "cpcap_adc",
0249         .of_compatible = "motorola,mapphone-cpcap-adc",
0250     }, {
0251         .name          = "cpcap_battery",
0252         .of_compatible = "motorola,cpcap-battery",
0253     }, {
0254         .name          = "cpcap-charger",
0255         .of_compatible = "motorola,mapphone-cpcap-charger",
0256     }, {
0257         .name          = "cpcap-regulator",
0258         .of_compatible = "motorola,mapphone-cpcap-regulator",
0259     }, {
0260         .name          = "cpcap-rtc",
0261         .of_compatible = "motorola,cpcap-rtc",
0262     }, {
0263         .name          = "cpcap-pwrbutton",
0264         .of_compatible = "motorola,cpcap-pwrbutton",
0265     }, {
0266         .name          = "cpcap-usb-phy",
0267         .of_compatible = "motorola,mapphone-cpcap-usb-phy",
0268     }, {
0269         .name          = "cpcap-led",
0270         .id            = 0,
0271         .of_compatible = "motorola,cpcap-led-red",
0272     }, {
0273         .name          = "cpcap-led",
0274         .id            = 1,
0275         .of_compatible = "motorola,cpcap-led-green",
0276     }, {
0277         .name          = "cpcap-led",
0278         .id            = 2,
0279         .of_compatible = "motorola,cpcap-led-blue",
0280     }, {
0281         .name          = "cpcap-led",
0282         .id            = 3,
0283         .of_compatible = "motorola,cpcap-led-adl",
0284     }, {
0285         .name          = "cpcap-led",
0286         .id            = 4,
0287         .of_compatible = "motorola,cpcap-led-cp",
0288     }, {
0289         .name          = "cpcap-codec",
0290     }
0291 };
0292 
0293 static int cpcap_probe(struct spi_device *spi)
0294 {
0295     const struct of_device_id *match;
0296     struct cpcap_ddata *cpcap;
0297     int ret;
0298 
0299     match = of_match_device(of_match_ptr(cpcap_of_match), &spi->dev);
0300     if (!match)
0301         return -ENODEV;
0302 
0303     cpcap = devm_kzalloc(&spi->dev, sizeof(*cpcap), GFP_KERNEL);
0304     if (!cpcap)
0305         return -ENOMEM;
0306 
0307     cpcap->spi = spi;
0308     spi_set_drvdata(spi, cpcap);
0309 
0310     spi->bits_per_word = 16;
0311     spi->mode = SPI_MODE_0 | SPI_CS_HIGH;
0312 
0313     ret = spi_setup(spi);
0314     if (ret)
0315         return ret;
0316 
0317     cpcap->regmap_conf = &cpcap_regmap_config;
0318     cpcap->regmap = devm_regmap_init_spi(spi, &cpcap_regmap_config);
0319     if (IS_ERR(cpcap->regmap)) {
0320         ret = PTR_ERR(cpcap->regmap);
0321         dev_err(&cpcap->spi->dev, "Failed to initialize regmap: %d\n",
0322             ret);
0323 
0324         return ret;
0325     }
0326 
0327     ret = cpcap_check_revision(cpcap);
0328     if (ret) {
0329         dev_err(&cpcap->spi->dev, "Failed to detect CPCAP: %i\n", ret);
0330         return ret;
0331     }
0332 
0333     ret = cpcap_init_irq(cpcap);
0334     if (ret)
0335         return ret;
0336 
0337     /* Parent SPI controller uses DMA, CPCAP and child devices do not */
0338     spi->dev.coherent_dma_mask = 0;
0339     spi->dev.dma_mask = &spi->dev.coherent_dma_mask;
0340 
0341     return devm_mfd_add_devices(&spi->dev, 0, cpcap_mfd_devices,
0342                     ARRAY_SIZE(cpcap_mfd_devices), NULL, 0, NULL);
0343 }
0344 
0345 static struct spi_driver cpcap_driver = {
0346     .driver = {
0347         .name = "cpcap-core",
0348         .of_match_table = cpcap_of_match,
0349         .pm = &cpcap_pm,
0350     },
0351     .probe = cpcap_probe,
0352     .id_table = cpcap_spi_ids,
0353 };
0354 module_spi_driver(cpcap_driver);
0355 
0356 MODULE_ALIAS("platform:cpcap");
0357 MODULE_DESCRIPTION("CPCAP driver");
0358 MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
0359 MODULE_LICENSE("GPL v2");