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