1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Core support for ATC260x PMICs 4 * 5 * Copyright (C) 2019 Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> 6 * Copyright (C) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com> 7 */ 8 9 #include <linux/interrupt.h> 10 #include <linux/mfd/atc260x/core.h> 11 #include <linux/mfd/core.h> 12 #include <linux/module.h> 13 #include <linux/of.h> 14 #include <linux/regmap.h> 15 16 #define ATC260X_CHIP_REV_MAX 31 17 18 struct atc260x_init_regs { 19 unsigned int cmu_devrst; 20 unsigned int cmu_devrst_ints; 21 unsigned int ints_msk; 22 unsigned int pad_en; 23 unsigned int pad_en_extirq; 24 }; 25 26 static void regmap_lock_mutex(void *__mutex) 27 { 28 struct mutex *mutex = __mutex; 29 30 /* 31 * Using regmap within an atomic context (e.g. accessing a PMIC when 32 * powering system down) is normally allowed only if the regmap type 33 * is MMIO and the regcache type is either REGCACHE_NONE or 34 * REGCACHE_FLAT. For slow buses like I2C and SPI, the regmap is 35 * internally protected by a mutex which is acquired non-atomically. 36 * 37 * Let's improve this by using a customized locking scheme inspired 38 * from I2C atomic transfer. See i2c_in_atomic_xfer_mode() for a 39 * starting point. 40 */ 41 if (system_state > SYSTEM_RUNNING && irqs_disabled()) 42 mutex_trylock(mutex); 43 else 44 mutex_lock(mutex); 45 } 46 47 static void regmap_unlock_mutex(void *__mutex) 48 { 49 struct mutex *mutex = __mutex; 50 51 mutex_unlock(mutex); 52 } 53 54 static const struct regmap_config atc2603c_regmap_config = { 55 .reg_bits = 8, 56 .val_bits = 16, 57 .max_register = ATC2603C_SADDR, 58 .cache_type = REGCACHE_NONE, 59 }; 60 61 static const struct regmap_config atc2609a_regmap_config = { 62 .reg_bits = 8, 63 .val_bits = 16, 64 .max_register = ATC2609A_SADDR, 65 .cache_type = REGCACHE_NONE, 66 }; 67 68 static const struct regmap_irq atc2603c_regmap_irqs[] = { 69 REGMAP_IRQ_REG(ATC2603C_IRQ_AUDIO, 0, ATC2603C_INTS_MSK_AUDIO), 70 REGMAP_IRQ_REG(ATC2603C_IRQ_OV, 0, ATC2603C_INTS_MSK_OV), 71 REGMAP_IRQ_REG(ATC2603C_IRQ_OC, 0, ATC2603C_INTS_MSK_OC), 72 REGMAP_IRQ_REG(ATC2603C_IRQ_OT, 0, ATC2603C_INTS_MSK_OT), 73 REGMAP_IRQ_REG(ATC2603C_IRQ_UV, 0, ATC2603C_INTS_MSK_UV), 74 REGMAP_IRQ_REG(ATC2603C_IRQ_ALARM, 0, ATC2603C_INTS_MSK_ALARM), 75 REGMAP_IRQ_REG(ATC2603C_IRQ_ONOFF, 0, ATC2603C_INTS_MSK_ONOFF), 76 REGMAP_IRQ_REG(ATC2603C_IRQ_SGPIO, 0, ATC2603C_INTS_MSK_SGPIO), 77 REGMAP_IRQ_REG(ATC2603C_IRQ_IR, 0, ATC2603C_INTS_MSK_IR), 78 REGMAP_IRQ_REG(ATC2603C_IRQ_REMCON, 0, ATC2603C_INTS_MSK_REMCON), 79 REGMAP_IRQ_REG(ATC2603C_IRQ_POWER_IN, 0, ATC2603C_INTS_MSK_POWERIN), 80 }; 81 82 static const struct regmap_irq atc2609a_regmap_irqs[] = { 83 REGMAP_IRQ_REG(ATC2609A_IRQ_AUDIO, 0, ATC2609A_INTS_MSK_AUDIO), 84 REGMAP_IRQ_REG(ATC2609A_IRQ_OV, 0, ATC2609A_INTS_MSK_OV), 85 REGMAP_IRQ_REG(ATC2609A_IRQ_OC, 0, ATC2609A_INTS_MSK_OC), 86 REGMAP_IRQ_REG(ATC2609A_IRQ_OT, 0, ATC2609A_INTS_MSK_OT), 87 REGMAP_IRQ_REG(ATC2609A_IRQ_UV, 0, ATC2609A_INTS_MSK_UV), 88 REGMAP_IRQ_REG(ATC2609A_IRQ_ALARM, 0, ATC2609A_INTS_MSK_ALARM), 89 REGMAP_IRQ_REG(ATC2609A_IRQ_ONOFF, 0, ATC2609A_INTS_MSK_ONOFF), 90 REGMAP_IRQ_REG(ATC2609A_IRQ_WKUP, 0, ATC2609A_INTS_MSK_WKUP), 91 REGMAP_IRQ_REG(ATC2609A_IRQ_IR, 0, ATC2609A_INTS_MSK_IR), 92 REGMAP_IRQ_REG(ATC2609A_IRQ_REMCON, 0, ATC2609A_INTS_MSK_REMCON), 93 REGMAP_IRQ_REG(ATC2609A_IRQ_POWER_IN, 0, ATC2609A_INTS_MSK_POWERIN), 94 }; 95 96 static const struct regmap_irq_chip atc2603c_regmap_irq_chip = { 97 .name = "atc2603c", 98 .irqs = atc2603c_regmap_irqs, 99 .num_irqs = ARRAY_SIZE(atc2603c_regmap_irqs), 100 .num_regs = 1, 101 .status_base = ATC2603C_INTS_PD, 102 .unmask_base = ATC2603C_INTS_MSK, 103 }; 104 105 static const struct regmap_irq_chip atc2609a_regmap_irq_chip = { 106 .name = "atc2609a", 107 .irqs = atc2609a_regmap_irqs, 108 .num_irqs = ARRAY_SIZE(atc2609a_regmap_irqs), 109 .num_regs = 1, 110 .status_base = ATC2609A_INTS_PD, 111 .unmask_base = ATC2609A_INTS_MSK, 112 }; 113 114 static const struct resource atc2603c_onkey_resources[] = { 115 DEFINE_RES_IRQ(ATC2603C_IRQ_ONOFF), 116 }; 117 118 static const struct resource atc2609a_onkey_resources[] = { 119 DEFINE_RES_IRQ(ATC2609A_IRQ_ONOFF), 120 }; 121 122 static const struct mfd_cell atc2603c_mfd_cells[] = { 123 { .name = "atc260x-regulator" }, 124 { .name = "atc260x-pwrc" }, 125 { 126 .name = "atc260x-onkey", 127 .num_resources = ARRAY_SIZE(atc2603c_onkey_resources), 128 .resources = atc2603c_onkey_resources, 129 }, 130 }; 131 132 static const struct mfd_cell atc2609a_mfd_cells[] = { 133 { .name = "atc260x-regulator" }, 134 { .name = "atc260x-pwrc" }, 135 { 136 .name = "atc260x-onkey", 137 .num_resources = ARRAY_SIZE(atc2609a_onkey_resources), 138 .resources = atc2609a_onkey_resources, 139 }, 140 }; 141 142 static const struct atc260x_init_regs atc2603c_init_regs = { 143 .cmu_devrst = ATC2603C_CMU_DEVRST, 144 .cmu_devrst_ints = ATC2603C_CMU_DEVRST_INTS, 145 .ints_msk = ATC2603C_INTS_MSK, 146 .pad_en = ATC2603C_PAD_EN, 147 .pad_en_extirq = ATC2603C_PAD_EN_EXTIRQ, 148 }; 149 150 static const struct atc260x_init_regs atc2609a_init_regs = { 151 .cmu_devrst = ATC2609A_CMU_DEVRST, 152 .cmu_devrst_ints = ATC2609A_CMU_DEVRST_INTS, 153 .ints_msk = ATC2609A_INTS_MSK, 154 .pad_en = ATC2609A_PAD_EN, 155 .pad_en_extirq = ATC2609A_PAD_EN_EXTIRQ, 156 }; 157 158 static void atc260x_cmu_reset(struct atc260x *atc260x) 159 { 160 const struct atc260x_init_regs *regs = atc260x->init_regs; 161 162 /* Assert reset */ 163 regmap_update_bits(atc260x->regmap, regs->cmu_devrst, 164 regs->cmu_devrst_ints, ~regs->cmu_devrst_ints); 165 166 /* De-assert reset */ 167 regmap_update_bits(atc260x->regmap, regs->cmu_devrst, 168 regs->cmu_devrst_ints, regs->cmu_devrst_ints); 169 } 170 171 static void atc260x_dev_init(struct atc260x *atc260x) 172 { 173 const struct atc260x_init_regs *regs = atc260x->init_regs; 174 175 /* Initialize interrupt block */ 176 atc260x_cmu_reset(atc260x); 177 178 /* Disable all interrupt sources */ 179 regmap_write(atc260x->regmap, regs->ints_msk, 0); 180 181 /* Enable EXTIRQ pad */ 182 regmap_update_bits(atc260x->regmap, regs->pad_en, 183 regs->pad_en_extirq, regs->pad_en_extirq); 184 } 185 186 /** 187 * atc260x_match_device(): Setup ATC260x variant related fields 188 * 189 * @atc260x: ATC260x device to setup (.dev field must be set) 190 * @regmap_cfg: regmap config associated with this ATC260x device 191 * 192 * This lets the ATC260x core configure the MFD cells and register maps 193 * for later use. 194 */ 195 int atc260x_match_device(struct atc260x *atc260x, struct regmap_config *regmap_cfg) 196 { 197 struct device *dev = atc260x->dev; 198 const void *of_data; 199 200 of_data = of_device_get_match_data(dev); 201 if (!of_data) 202 return -ENODEV; 203 204 atc260x->ic_type = (unsigned long)of_data; 205 206 switch (atc260x->ic_type) { 207 case ATC2603C: 208 *regmap_cfg = atc2603c_regmap_config; 209 atc260x->regmap_irq_chip = &atc2603c_regmap_irq_chip; 210 atc260x->cells = atc2603c_mfd_cells; 211 atc260x->nr_cells = ARRAY_SIZE(atc2603c_mfd_cells); 212 atc260x->type_name = "atc2603c"; 213 atc260x->rev_reg = ATC2603C_CHIP_VER; 214 atc260x->init_regs = &atc2603c_init_regs; 215 break; 216 case ATC2609A: 217 *regmap_cfg = atc2609a_regmap_config; 218 atc260x->regmap_irq_chip = &atc2609a_regmap_irq_chip; 219 atc260x->cells = atc2609a_mfd_cells; 220 atc260x->nr_cells = ARRAY_SIZE(atc2609a_mfd_cells); 221 atc260x->type_name = "atc2609a"; 222 atc260x->rev_reg = ATC2609A_CHIP_VER; 223 atc260x->init_regs = &atc2609a_init_regs; 224 break; 225 default: 226 dev_err(dev, "Unsupported ATC260x device type: %u\n", 227 atc260x->ic_type); 228 return -EINVAL; 229 } 230 231 atc260x->regmap_mutex = devm_kzalloc(dev, sizeof(*atc260x->regmap_mutex), 232 GFP_KERNEL); 233 if (!atc260x->regmap_mutex) 234 return -ENOMEM; 235 236 mutex_init(atc260x->regmap_mutex); 237 238 regmap_cfg->lock = regmap_lock_mutex; 239 regmap_cfg->unlock = regmap_unlock_mutex; 240 regmap_cfg->lock_arg = atc260x->regmap_mutex; 241 242 return 0; 243 } 244 EXPORT_SYMBOL_GPL(atc260x_match_device); 245 246 /** 247 * atc260x_device_probe(): Probe a configured ATC260x device 248 * 249 * @atc260x: ATC260x device to probe (must be configured) 250 * 251 * This function lets the ATC260x core register the ATC260x MFD devices 252 * and IRQCHIP. The ATC260x device passed in must be fully configured 253 * with atc260x_match_device, its IRQ set, and regmap created. 254 */ 255 int atc260x_device_probe(struct atc260x *atc260x) 256 { 257 struct device *dev = atc260x->dev; 258 unsigned int chip_rev; 259 int ret; 260 261 if (!atc260x->irq) { 262 dev_err(dev, "No interrupt support\n"); 263 return -EINVAL; 264 } 265 266 /* Initialize the hardware */ 267 atc260x_dev_init(atc260x); 268 269 ret = regmap_read(atc260x->regmap, atc260x->rev_reg, &chip_rev); 270 if (ret) { 271 dev_err(dev, "Failed to get chip revision\n"); 272 return ret; 273 } 274 275 if (chip_rev > ATC260X_CHIP_REV_MAX) { 276 dev_err(dev, "Unknown chip revision: %u\n", chip_rev); 277 return -EINVAL; 278 } 279 280 atc260x->ic_ver = __ffs(chip_rev + 1U); 281 282 dev_info(dev, "Detected chip type %s rev.%c\n", 283 atc260x->type_name, 'A' + atc260x->ic_ver); 284 285 ret = devm_regmap_add_irq_chip(dev, atc260x->regmap, atc260x->irq, IRQF_ONESHOT, 286 -1, atc260x->regmap_irq_chip, &atc260x->irq_data); 287 if (ret) { 288 dev_err(dev, "Failed to add IRQ chip: %d\n", ret); 289 return ret; 290 } 291 292 ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 293 atc260x->cells, atc260x->nr_cells, NULL, 0, 294 regmap_irq_get_domain(atc260x->irq_data)); 295 if (ret) { 296 dev_err(dev, "Failed to add child devices: %d\n", ret); 297 regmap_del_irq_chip(atc260x->irq, atc260x->irq_data); 298 } 299 300 return ret; 301 } 302 EXPORT_SYMBOL_GPL(atc260x_device_probe); 303 304 MODULE_DESCRIPTION("ATC260x PMICs Core support"); 305 MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>"); 306 MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>"); 307 MODULE_LICENSE("GPL"); 308