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