root/drivers/mfd/motorola-cpcap.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cpcap_sense_irq
  2. cpcap_sense_virq
  3. cpcap_check_revision
  4. cpcap_init_one_regmap_irq
  5. cpcap_init_irq_chip
  6. cpcap_init_irq
  7. cpcap_probe

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Motorola CPCAP PMIC core driver
   4  *
   5  * Copyright (C) 2016 Tony Lindgren <tony@atomide.com>
   6  */
   7 
   8 #include <linux/device.h>
   9 #include <linux/err.h>
  10 #include <linux/interrupt.h>
  11 #include <linux/irq.h>
  12 #include <linux/kernel.h>
  13 #include <linux/module.h>
  14 #include <linux/of_device.h>
  15 #include <linux/regmap.h>
  16 #include <linux/sysfs.h>
  17 
  18 #include <linux/mfd/core.h>
  19 #include <linux/mfd/motorola-cpcap.h>
  20 #include <linux/spi/spi.h>
  21 
  22 #define CPCAP_NR_IRQ_REG_BANKS  6
  23 #define CPCAP_NR_IRQ_CHIPS      3
  24 #define CPCAP_REGISTER_SIZE     4
  25 #define CPCAP_REGISTER_BITS     16
  26 
  27 struct cpcap_ddata {
  28         struct spi_device *spi;
  29         struct regmap_irq *irqs;
  30         struct regmap_irq_chip_data *irqdata[CPCAP_NR_IRQ_CHIPS];
  31         const struct regmap_config *regmap_conf;
  32         struct regmap *regmap;
  33 };
  34 
  35 static int cpcap_sense_irq(struct regmap *regmap, int irq)
  36 {
  37         int regnum = irq / CPCAP_REGISTER_BITS;
  38         int mask = BIT(irq % CPCAP_REGISTER_BITS);
  39         int reg = CPCAP_REG_INTS1 + (regnum * CPCAP_REGISTER_SIZE);
  40         int err, val;
  41 
  42         if (reg < CPCAP_REG_INTS1 || reg > CPCAP_REG_INTS4)
  43                 return -EINVAL;
  44 
  45         err = regmap_read(regmap, reg, &val);
  46         if (err)
  47                 return err;
  48 
  49         return !!(val & mask);
  50 }
  51 
  52 int cpcap_sense_virq(struct regmap *regmap, int virq)
  53 {
  54         struct regmap_irq_chip_data *d = irq_get_chip_data(virq);
  55         int irq_base = regmap_irq_chip_get_base(d);
  56 
  57         return cpcap_sense_irq(regmap, virq - irq_base);
  58 }
  59 EXPORT_SYMBOL_GPL(cpcap_sense_virq);
  60 
  61 static int cpcap_check_revision(struct cpcap_ddata *cpcap)
  62 {
  63         u16 vendor, rev;
  64         int ret;
  65 
  66         ret = cpcap_get_vendor(&cpcap->spi->dev, cpcap->regmap, &vendor);
  67         if (ret)
  68                 return ret;
  69 
  70         ret = cpcap_get_revision(&cpcap->spi->dev, cpcap->regmap, &rev);
  71         if (ret)
  72                 return ret;
  73 
  74         dev_info(&cpcap->spi->dev, "CPCAP vendor: %s rev: %i.%i (%x)\n",
  75                  vendor == CPCAP_VENDOR_ST ? "ST" : "TI",
  76                  CPCAP_REVISION_MAJOR(rev), CPCAP_REVISION_MINOR(rev),
  77                  rev);
  78 
  79         if (rev < CPCAP_REVISION_2_1) {
  80                 dev_info(&cpcap->spi->dev,
  81                          "Please add old CPCAP revision support as needed\n");
  82                 return -ENODEV;
  83         }
  84 
  85         return 0;
  86 }
  87 
  88 /*
  89  * First two irq chips are the two private macro interrupt chips, the third
  90  * irq chip is for register banks 1 - 4 and is available for drivers to use.
  91  */
  92 static struct regmap_irq_chip cpcap_irq_chip[CPCAP_NR_IRQ_CHIPS] = {
  93         {
  94                 .name = "cpcap-m2",
  95                 .num_regs = 1,
  96                 .status_base = CPCAP_REG_MI1,
  97                 .ack_base = CPCAP_REG_MI1,
  98                 .mask_base = CPCAP_REG_MIM1,
  99                 .use_ack = true,
 100                 .ack_invert = true,
 101         },
 102         {
 103                 .name = "cpcap-m2",
 104                 .num_regs = 1,
 105                 .status_base = CPCAP_REG_MI2,
 106                 .ack_base = CPCAP_REG_MI2,
 107                 .mask_base = CPCAP_REG_MIM2,
 108                 .use_ack = true,
 109                 .ack_invert = true,
 110         },
 111         {
 112                 .name = "cpcap1-4",
 113                 .num_regs = 4,
 114                 .status_base = CPCAP_REG_INT1,
 115                 .ack_base = CPCAP_REG_INT1,
 116                 .mask_base = CPCAP_REG_INTM1,
 117                 .use_ack = true,
 118                 .ack_invert = true,
 119         },
 120 };
 121 
 122 static void cpcap_init_one_regmap_irq(struct cpcap_ddata *cpcap,
 123                                       struct regmap_irq *rirq,
 124                                       int irq_base, int irq)
 125 {
 126         unsigned int reg_offset;
 127         unsigned int bit, mask;
 128 
 129         reg_offset = irq - irq_base;
 130         reg_offset /= cpcap->regmap_conf->val_bits;
 131         reg_offset *= cpcap->regmap_conf->reg_stride;
 132 
 133         bit = irq % cpcap->regmap_conf->val_bits;
 134         mask = (1 << bit);
 135 
 136         rirq->reg_offset = reg_offset;
 137         rirq->mask = mask;
 138 }
 139 
 140 static int cpcap_init_irq_chip(struct cpcap_ddata *cpcap, int irq_chip,
 141                                int irq_start, int nr_irqs)
 142 {
 143         struct regmap_irq_chip *chip = &cpcap_irq_chip[irq_chip];
 144         int i, ret;
 145 
 146         for (i = irq_start; i < irq_start + nr_irqs; i++) {
 147                 struct regmap_irq *rirq = &cpcap->irqs[i];
 148 
 149                 cpcap_init_one_regmap_irq(cpcap, rirq, irq_start, i);
 150         }
 151         chip->irqs = &cpcap->irqs[irq_start];
 152         chip->num_irqs = nr_irqs;
 153         chip->irq_drv_data = cpcap;
 154 
 155         ret = devm_regmap_add_irq_chip(&cpcap->spi->dev, cpcap->regmap,
 156                                        cpcap->spi->irq,
 157                                        irq_get_trigger_type(cpcap->spi->irq) |
 158                                        IRQF_SHARED, -1,
 159                                        chip, &cpcap->irqdata[irq_chip]);
 160         if (ret) {
 161                 dev_err(&cpcap->spi->dev, "could not add irq chip %i: %i\n",
 162                         irq_chip, ret);
 163                 return ret;
 164         }
 165 
 166         return 0;
 167 }
 168 
 169 static int cpcap_init_irq(struct cpcap_ddata *cpcap)
 170 {
 171         int ret;
 172 
 173         cpcap->irqs = devm_kzalloc(&cpcap->spi->dev,
 174                                    array3_size(sizeof(*cpcap->irqs),
 175                                                CPCAP_NR_IRQ_REG_BANKS,
 176                                                cpcap->regmap_conf->val_bits),
 177                                    GFP_KERNEL);
 178         if (!cpcap->irqs)
 179                 return -ENOMEM;
 180 
 181         ret = cpcap_init_irq_chip(cpcap, 0, 0, 16);
 182         if (ret)
 183                 return ret;
 184 
 185         ret = cpcap_init_irq_chip(cpcap, 1, 16, 16);
 186         if (ret)
 187                 return ret;
 188 
 189         ret = cpcap_init_irq_chip(cpcap, 2, 32, 64);
 190         if (ret)
 191                 return ret;
 192 
 193         enable_irq_wake(cpcap->spi->irq);
 194 
 195         return 0;
 196 }
 197 
 198 static const struct of_device_id cpcap_of_match[] = {
 199         { .compatible = "motorola,cpcap", },
 200         { .compatible = "st,6556002", },
 201         {},
 202 };
 203 MODULE_DEVICE_TABLE(of, cpcap_of_match);
 204 
 205 static const struct regmap_config cpcap_regmap_config = {
 206         .reg_bits = 16,
 207         .reg_stride = 4,
 208         .pad_bits = 0,
 209         .val_bits = 16,
 210         .write_flag_mask = 0x8000,
 211         .max_register = CPCAP_REG_ST_TEST2,
 212         .cache_type = REGCACHE_NONE,
 213         .reg_format_endian = REGMAP_ENDIAN_LITTLE,
 214         .val_format_endian = REGMAP_ENDIAN_LITTLE,
 215 };
 216 
 217 static const struct mfd_cell cpcap_mfd_devices[] = {
 218         {
 219                 .name          = "cpcap_adc",
 220                 .of_compatible = "motorola,mapphone-cpcap-adc",
 221         }, {
 222                 .name          = "cpcap_battery",
 223                 .of_compatible = "motorola,cpcap-battery",
 224         }, {
 225                 .name          = "cpcap-charger",
 226                 .of_compatible = "motorola,mapphone-cpcap-charger",
 227         }, {
 228                 .name          = "cpcap-regulator",
 229                 .of_compatible = "motorola,mapphone-cpcap-regulator",
 230         }, {
 231                 .name          = "cpcap-rtc",
 232                 .of_compatible = "motorola,cpcap-rtc",
 233         }, {
 234                 .name          = "cpcap-pwrbutton",
 235                 .of_compatible = "motorola,cpcap-pwrbutton",
 236         }, {
 237                 .name          = "cpcap-usb-phy",
 238                 .of_compatible = "motorola,mapphone-cpcap-usb-phy",
 239         }, {
 240                 .name          = "cpcap-led",
 241                 .id            = 0,
 242                 .of_compatible = "motorola,cpcap-led-red",
 243         }, {
 244                 .name          = "cpcap-led",
 245                 .id            = 1,
 246                 .of_compatible = "motorola,cpcap-led-green",
 247         }, {
 248                 .name          = "cpcap-led",
 249                 .id            = 2,
 250                 .of_compatible = "motorola,cpcap-led-blue",
 251         }, {
 252                 .name          = "cpcap-led",
 253                 .id            = 3,
 254                 .of_compatible = "motorola,cpcap-led-adl",
 255         }, {
 256                 .name          = "cpcap-led",
 257                 .id            = 4,
 258                 .of_compatible = "motorola,cpcap-led-cp",
 259         }, {
 260                 .name          = "cpcap-codec",
 261         }
 262 };
 263 
 264 static int cpcap_probe(struct spi_device *spi)
 265 {
 266         const struct of_device_id *match;
 267         struct cpcap_ddata *cpcap;
 268         int ret;
 269 
 270         match = of_match_device(of_match_ptr(cpcap_of_match), &spi->dev);
 271         if (!match)
 272                 return -ENODEV;
 273 
 274         cpcap = devm_kzalloc(&spi->dev, sizeof(*cpcap), GFP_KERNEL);
 275         if (!cpcap)
 276                 return -ENOMEM;
 277 
 278         cpcap->spi = spi;
 279         spi_set_drvdata(spi, cpcap);
 280 
 281         spi->bits_per_word = 16;
 282         spi->mode = SPI_MODE_0 | SPI_CS_HIGH;
 283 
 284         ret = spi_setup(spi);
 285         if (ret)
 286                 return ret;
 287 
 288         cpcap->regmap_conf = &cpcap_regmap_config;
 289         cpcap->regmap = devm_regmap_init_spi(spi, &cpcap_regmap_config);
 290         if (IS_ERR(cpcap->regmap)) {
 291                 ret = PTR_ERR(cpcap->regmap);
 292                 dev_err(&cpcap->spi->dev, "Failed to initialize regmap: %d\n",
 293                         ret);
 294 
 295                 return ret;
 296         }
 297 
 298         ret = cpcap_check_revision(cpcap);
 299         if (ret) {
 300                 dev_err(&cpcap->spi->dev, "Failed to detect CPCAP: %i\n", ret);
 301                 return ret;
 302         }
 303 
 304         ret = cpcap_init_irq(cpcap);
 305         if (ret)
 306                 return ret;
 307 
 308         return devm_mfd_add_devices(&spi->dev, 0, cpcap_mfd_devices,
 309                                     ARRAY_SIZE(cpcap_mfd_devices), NULL, 0, NULL);
 310 }
 311 
 312 static struct spi_driver cpcap_driver = {
 313         .driver = {
 314                 .name = "cpcap-core",
 315                 .of_match_table = cpcap_of_match,
 316         },
 317         .probe = cpcap_probe,
 318 };
 319 module_spi_driver(cpcap_driver);
 320 
 321 MODULE_ALIAS("platform:cpcap");
 322 MODULE_DESCRIPTION("CPCAP driver");
 323 MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
 324 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */