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 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_check_revision(struct cpcap_ddata *cpcap) 36 { 37 u16 vendor, rev; 38 int ret; 39 40 ret = cpcap_get_vendor(&cpcap->spi->dev, cpcap->regmap, &vendor); 41 if (ret) 42 return ret; 43 44 ret = cpcap_get_revision(&cpcap->spi->dev, cpcap->regmap, &rev); 45 if (ret) 46 return ret; 47 48 dev_info(&cpcap->spi->dev, "CPCAP vendor: %s rev: %i.%i (%x)\n", 49 vendor == CPCAP_VENDOR_ST ? "ST" : "TI", 50 CPCAP_REVISION_MAJOR(rev), CPCAP_REVISION_MINOR(rev), 51 rev); 52 53 if (rev < CPCAP_REVISION_2_1) { 54 dev_info(&cpcap->spi->dev, 55 "Please add old CPCAP revision support as needed\n"); 56 return -ENODEV; 57 } 58 59 return 0; 60 } 61 62 /* 63 * First two irq chips are the two private macro interrupt chips, the third 64 * irq chip is for register banks 1 - 4 and is available for drivers to use. 65 */ 66 static struct regmap_irq_chip cpcap_irq_chip[CPCAP_NR_IRQ_CHIPS] = { 67 { 68 .name = "cpcap-m2", 69 .num_regs = 1, 70 .status_base = CPCAP_REG_MI1, 71 .ack_base = CPCAP_REG_MI1, 72 .mask_base = CPCAP_REG_MIM1, 73 .use_ack = true, 74 }, 75 { 76 .name = "cpcap-m2", 77 .num_regs = 1, 78 .status_base = CPCAP_REG_MI2, 79 .ack_base = CPCAP_REG_MI2, 80 .mask_base = CPCAP_REG_MIM2, 81 .use_ack = true, 82 }, 83 { 84 .name = "cpcap1-4", 85 .num_regs = 4, 86 .status_base = CPCAP_REG_INT1, 87 .ack_base = CPCAP_REG_INT1, 88 .mask_base = CPCAP_REG_INTM1, 89 .type_base = CPCAP_REG_INTS1, 90 .use_ack = true, 91 }, 92 }; 93 94 static void cpcap_init_one_regmap_irq(struct cpcap_ddata *cpcap, 95 struct regmap_irq *rirq, 96 int irq_base, int irq) 97 { 98 unsigned int reg_offset; 99 unsigned int bit, mask; 100 101 reg_offset = irq - irq_base; 102 reg_offset /= cpcap->regmap_conf->val_bits; 103 reg_offset *= cpcap->regmap_conf->reg_stride; 104 105 bit = irq % cpcap->regmap_conf->val_bits; 106 mask = (1 << bit); 107 108 rirq->reg_offset = reg_offset; 109 rirq->mask = mask; 110 } 111 112 static int cpcap_init_irq_chip(struct cpcap_ddata *cpcap, int irq_chip, 113 int irq_start, int nr_irqs) 114 { 115 struct regmap_irq_chip *chip = &cpcap_irq_chip[irq_chip]; 116 int i, ret; 117 118 for (i = irq_start; i < irq_start + nr_irqs; i++) { 119 struct regmap_irq *rirq = &cpcap->irqs[i]; 120 121 cpcap_init_one_regmap_irq(cpcap, rirq, irq_start, i); 122 } 123 chip->irqs = &cpcap->irqs[irq_start]; 124 chip->num_irqs = nr_irqs; 125 chip->irq_drv_data = cpcap; 126 127 ret = devm_regmap_add_irq_chip(&cpcap->spi->dev, cpcap->regmap, 128 cpcap->spi->irq, 129 IRQF_TRIGGER_RISING | 130 IRQF_SHARED, -1, 131 chip, &cpcap->irqdata[irq_chip]); 132 if (ret) { 133 dev_err(&cpcap->spi->dev, "could not add irq chip %i: %i\n", 134 irq_chip, ret); 135 return ret; 136 } 137 138 return 0; 139 } 140 141 static int cpcap_init_irq(struct cpcap_ddata *cpcap) 142 { 143 int ret; 144 145 cpcap->irqs = devm_kzalloc(&cpcap->spi->dev, 146 sizeof(*cpcap->irqs) * 147 CPCAP_NR_IRQ_REG_BANKS * 148 cpcap->regmap_conf->val_bits, 149 GFP_KERNEL); 150 if (!cpcap->irqs) 151 return -ENOMEM; 152 153 ret = cpcap_init_irq_chip(cpcap, 0, 0, 16); 154 if (ret) 155 return ret; 156 157 ret = cpcap_init_irq_chip(cpcap, 1, 16, 16); 158 if (ret) 159 return ret; 160 161 ret = cpcap_init_irq_chip(cpcap, 2, 32, 64); 162 if (ret) 163 return ret; 164 165 enable_irq_wake(cpcap->spi->irq); 166 167 return 0; 168 } 169 170 static const struct of_device_id cpcap_of_match[] = { 171 { .compatible = "motorola,cpcap", }, 172 { .compatible = "st,6556002", }, 173 {}, 174 }; 175 MODULE_DEVICE_TABLE(of, cpcap_of_match); 176 177 static const struct regmap_config cpcap_regmap_config = { 178 .reg_bits = 16, 179 .reg_stride = 4, 180 .pad_bits = 0, 181 .val_bits = 16, 182 .write_flag_mask = 0x8000, 183 .max_register = CPCAP_REG_ST_TEST2, 184 .cache_type = REGCACHE_NONE, 185 .reg_format_endian = REGMAP_ENDIAN_LITTLE, 186 .val_format_endian = REGMAP_ENDIAN_LITTLE, 187 }; 188 189 static int cpcap_probe(struct spi_device *spi) 190 { 191 const struct of_device_id *match; 192 struct cpcap_ddata *cpcap; 193 int ret; 194 195 match = of_match_device(of_match_ptr(cpcap_of_match), &spi->dev); 196 if (!match) 197 return -ENODEV; 198 199 cpcap = devm_kzalloc(&spi->dev, sizeof(*cpcap), GFP_KERNEL); 200 if (!cpcap) 201 return -ENOMEM; 202 203 cpcap->spi = spi; 204 spi_set_drvdata(spi, cpcap); 205 206 spi->bits_per_word = 16; 207 spi->mode = SPI_MODE_0 | SPI_CS_HIGH; 208 209 ret = spi_setup(spi); 210 if (ret) 211 return ret; 212 213 cpcap->regmap_conf = &cpcap_regmap_config; 214 cpcap->regmap = devm_regmap_init_spi(spi, &cpcap_regmap_config); 215 if (IS_ERR(cpcap->regmap)) { 216 ret = PTR_ERR(cpcap->regmap); 217 dev_err(&cpcap->spi->dev, "Failed to initialize regmap: %d\n", 218 ret); 219 220 return ret; 221 } 222 223 ret = cpcap_check_revision(cpcap); 224 if (ret) { 225 dev_err(&cpcap->spi->dev, "Failed to detect CPCAP: %i\n", ret); 226 return ret; 227 } 228 229 ret = cpcap_init_irq(cpcap); 230 if (ret) 231 return ret; 232 233 return of_platform_populate(spi->dev.of_node, NULL, NULL, 234 &cpcap->spi->dev); 235 } 236 237 static int cpcap_remove(struct spi_device *pdev) 238 { 239 struct cpcap_ddata *cpcap = spi_get_drvdata(pdev); 240 241 of_platform_depopulate(&cpcap->spi->dev); 242 243 return 0; 244 } 245 246 static struct spi_driver cpcap_driver = { 247 .driver = { 248 .name = "cpcap-core", 249 .of_match_table = cpcap_of_match, 250 }, 251 .probe = cpcap_probe, 252 .remove = cpcap_remove, 253 }; 254 module_spi_driver(cpcap_driver); 255 256 MODULE_ALIAS("platform:cpcap"); 257 MODULE_DESCRIPTION("CPCAP driver"); 258 MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>"); 259 MODULE_LICENSE("GPL v2"); 260