| 1 | |
| 2 | |
| 3 | * Copyright 2020 Google Inc
|
| 4 | * Copyright 2025 Linaro Ltd.
|
| 5 | *
|
| 6 | * Core driver for Maxim MAX77759 companion PMIC for USB Type-C
|
| 7 |
|
| 8 |
|
| 9 | #include <linux/array_size.h>
|
| 10 | #include <linux/bitfield.h>
|
| 11 | #include <linux/bits.h>
|
| 12 | #include <linux/cleanup.h>
|
| 13 | #include <linux/completion.h>
|
| 14 | #include <linux/dev_printk.h>
|
| 15 | #include <linux/device.h>
|
| 16 | #include <linux/err.h>
|
| 17 | #include <linux/i2c.h>
|
| 18 | #include <linux/init.h>
|
| 19 | #include <linux/interrupt.h>
|
| 20 | #include <linux/irq.h>
|
| 21 | #include <linux/jiffies.h>
|
| 22 | #include <linux/mfd/core.h>
|
| 23 | #include <linux/mfd/max77759.h>
|
| 24 | #include <linux/module.h>
|
| 25 | #include <linux/mutex.h>
|
| 26 | #include <linux/of.h>
|
| 27 | #include <linux/overflow.h>
|
| 28 | #include <linux/regmap.h>
|
| 29 |
|
| 30 |
|
| 31 | enum {
|
| 32 | MAX77759_CHIP_ID = 59,
|
| 33 | };
|
| 34 |
|
| 35 | enum max77759_i2c_subdev_id {
|
| 36 | |
| 37 | * These are arbitrary and simply used to match struct
|
| 38 | * max77759_i2c_subdev entries to the regmap pointers in struct
|
| 39 | * max77759 during probe().
|
| 40 |
|
| 41 | MAX77759_I2C_SUBDEV_ID_MAXQ,
|
| 42 | MAX77759_I2C_SUBDEV_ID_CHARGER,
|
| 43 | };
|
| 44 |
|
| 45 | struct max77759_i2c_subdev {
|
| 46 | enum max77759_i2c_subdev_id id;
|
| 47 | const struct regmap_config *cfg;
|
| 48 | u16 i2c_address;
|
| 49 | };
|
| 50 |
|
| 51 | static const struct regmap_range max77759_top_registers[] = {
|
| 52 | regmap_reg_range(0x00, 0x02),
|
| 53 | regmap_reg_range(0x22, 0x24),
|
| 54 | regmap_reg_range(0x26, 0x26),
|
| 55 | regmap_reg_range(0x40, 0x40),
|
| 56 | regmap_reg_range(0x50, 0x51),
|
| 57 | };
|
| 58 |
|
| 59 | static const struct regmap_range max77759_top_ro_registers[] = {
|
| 60 | regmap_reg_range(0x00, 0x02),
|
| 61 | regmap_reg_range(0x22, 0x22),
|
| 62 | };
|
| 63 |
|
| 64 | static const struct regmap_range max77759_top_volatile_registers[] = {
|
| 65 | regmap_reg_range(0x22, 0x22),
|
| 66 | regmap_reg_range(0x24, 0x24),
|
| 67 | };
|
| 68 |
|
| 69 | static const struct regmap_access_table max77759_top_wr_table = {
|
| 70 | .yes_ranges = max77759_top_registers,
|
| 71 | .n_yes_ranges = ARRAY_SIZE(max77759_top_registers),
|
| 72 | .no_ranges = max77759_top_ro_registers,
|
| 73 | .n_no_ranges = ARRAY_SIZE(max77759_top_ro_registers),
|
| 74 | };
|
| 75 |
|
| 76 | static const struct regmap_access_table max77759_top_rd_table = {
|
| 77 | .yes_ranges = max77759_top_registers,
|
| 78 | .n_yes_ranges = ARRAY_SIZE(max77759_top_registers),
|
| 79 | };
|
| 80 |
|
| 81 | static const struct regmap_access_table max77759_top_volatile_table = {
|
| 82 | .yes_ranges = max77759_top_volatile_registers,
|
| 83 | .n_yes_ranges = ARRAY_SIZE(max77759_top_volatile_registers),
|
| 84 | };
|
| 85 |
|
| 86 | static const struct regmap_config max77759_regmap_config_top = {
|
| 87 | .name = "top",
|
| 88 | .reg_bits = 8,
|
| 89 | .val_bits = 8,
|
| 90 | .max_register = MAX77759_PMIC_REG_CONTROL_FG,
|
| 91 | .wr_table = &max77759_top_wr_table,
|
| 92 | .rd_table = &max77759_top_rd_table,
|
| 93 | .volatile_table = &max77759_top_volatile_table,
|
| 94 | .num_reg_defaults_raw = MAX77759_PMIC_REG_CONTROL_FG + 1,
|
| 95 | .cache_type = REGCACHE_FLAT,
|
| 96 | };
|
| 97 |
|
| 98 | static const struct regmap_range max77759_maxq_registers[] = {
|
| 99 | regmap_reg_range(0x60, 0x73),
|
| 100 | regmap_reg_range(0x81, 0xa1),
|
| 101 | regmap_reg_range(0xb1, 0xd1),
|
| 102 | regmap_reg_range(0xe0, 0xe0),
|
| 103 | };
|
| 104 |
|
| 105 | static const struct regmap_range max77759_maxq_ro_registers[] = {
|
| 106 | regmap_reg_range(0x60, 0x63),
|
| 107 | regmap_reg_range(0x68, 0x6f),
|
| 108 | regmap_reg_range(0xb1, 0xd1),
|
| 109 | };
|
| 110 |
|
| 111 | static const struct regmap_range max77759_maxq_volatile_registers[] = {
|
| 112 | regmap_reg_range(0x64, 0x6f),
|
| 113 | regmap_reg_range(0xb1, 0xd1),
|
| 114 | regmap_reg_range(0xe0, 0xe0),
|
| 115 | };
|
| 116 |
|
| 117 | static const struct regmap_access_table max77759_maxq_wr_table = {
|
| 118 | .yes_ranges = max77759_maxq_registers,
|
| 119 | .n_yes_ranges = ARRAY_SIZE(max77759_maxq_registers),
|
| 120 | .no_ranges = max77759_maxq_ro_registers,
|
| 121 | .n_no_ranges = ARRAY_SIZE(max77759_maxq_ro_registers),
|
| 122 | };
|
| 123 |
|
| 124 | static const struct regmap_access_table max77759_maxq_rd_table = {
|
| 125 | .yes_ranges = max77759_maxq_registers,
|
| 126 | .n_yes_ranges = ARRAY_SIZE(max77759_maxq_registers),
|
| 127 | };
|
| 128 |
|
| 129 | static const struct regmap_access_table max77759_maxq_volatile_table = {
|
| 130 | .yes_ranges = max77759_maxq_volatile_registers,
|
| 131 | .n_yes_ranges = ARRAY_SIZE(max77759_maxq_volatile_registers),
|
| 132 | };
|
| 133 |
|
| 134 | static const struct regmap_config max77759_regmap_config_maxq = {
|
| 135 | .name = "maxq",
|
| 136 | .reg_bits = 8,
|
| 137 | .val_bits = 8,
|
| 138 | .max_register = MAX77759_MAXQ_REG_UIC_SWRST,
|
| 139 | .wr_table = &max77759_maxq_wr_table,
|
| 140 | .rd_table = &max77759_maxq_rd_table,
|
| 141 | .volatile_table = &max77759_maxq_volatile_table,
|
| 142 | .num_reg_defaults_raw = MAX77759_MAXQ_REG_UIC_SWRST + 1,
|
| 143 | .cache_type = REGCACHE_FLAT,
|
| 144 | };
|
| 145 |
|
| 146 | static const struct regmap_range max77759_charger_registers[] = {
|
| 147 | regmap_reg_range(0xb0, 0xcc),
|
| 148 | };
|
| 149 |
|
| 150 | static const struct regmap_range max77759_charger_ro_registers[] = {
|
| 151 | regmap_reg_range(0xb4, 0xb8),
|
| 152 | };
|
| 153 |
|
| 154 | static const struct regmap_range max77759_charger_volatile_registers[] = {
|
| 155 | regmap_reg_range(0xb0, 0xb1),
|
| 156 | regmap_reg_range(0xb4, 0xb8),
|
| 157 | };
|
| 158 |
|
| 159 | static const struct regmap_access_table max77759_charger_wr_table = {
|
| 160 | .yes_ranges = max77759_charger_registers,
|
| 161 | .n_yes_ranges = ARRAY_SIZE(max77759_charger_registers),
|
| 162 | .no_ranges = max77759_charger_ro_registers,
|
| 163 | .n_no_ranges = ARRAY_SIZE(max77759_charger_ro_registers),
|
| 164 | };
|
| 165 |
|
| 166 | static const struct regmap_access_table max77759_charger_rd_table = {
|
| 167 | .yes_ranges = max77759_charger_registers,
|
| 168 | .n_yes_ranges = ARRAY_SIZE(max77759_charger_registers),
|
| 169 | };
|
| 170 |
|
| 171 | static const struct regmap_access_table max77759_charger_volatile_table = {
|
| 172 | .yes_ranges = max77759_charger_volatile_registers,
|
| 173 | .n_yes_ranges = ARRAY_SIZE(max77759_charger_volatile_registers),
|
| 174 | };
|
| 175 |
|
| 176 | static const struct regmap_config max77759_regmap_config_charger = {
|
| 177 | .name = "charger",
|
| 178 | .reg_bits = 8,
|
| 179 | .val_bits = 8,
|
| 180 | .max_register = MAX77759_CHGR_REG_CHG_CNFG_19,
|
| 181 | .wr_table = &max77759_charger_wr_table,
|
| 182 | .rd_table = &max77759_charger_rd_table,
|
| 183 | .volatile_table = &max77759_charger_volatile_table,
|
| 184 | .num_reg_defaults_raw = MAX77759_CHGR_REG_CHG_CNFG_19 + 1,
|
| 185 | .cache_type = REGCACHE_FLAT,
|
| 186 | };
|
| 187 |
|
| 188 | |
| 189 | * Interrupts - with the following interrupt hierarchy:
|
| 190 | * pmic IRQs (INTSRC)
|
| 191 | * - MAXQ_INT: MaxQ IRQs
|
| 192 | * - UIC_INT1
|
| 193 | * - APCmdResI
|
| 194 | * - SysMsgI
|
| 195 | * - GPIOxI
|
| 196 | * - TOPSYS_INT: topsys
|
| 197 | * - TOPSYS_INT
|
| 198 | * - TSHDN_INT
|
| 199 | * - SYSOVLO_INT
|
| 200 | * - SYSUVLO_INT
|
| 201 | * - FSHIP_NOT_RD
|
| 202 | * - CHGR_INT: charger
|
| 203 | * - INT1
|
| 204 | * - AICL
|
| 205 | * - CHGIN
|
| 206 | * - WCIN
|
| 207 | * - CHG
|
| 208 | * - BAT
|
| 209 | * - INLIM
|
| 210 | * - THM2
|
| 211 | * - BYP
|
| 212 | * - INT2
|
| 213 | * - INSEL
|
| 214 | * - SYS_UVLO1
|
| 215 | * - SYS_UVLO2
|
| 216 | * - BAT_OILO
|
| 217 | * - CHG_STA_CC
|
| 218 | * - CHG_STA_CV
|
| 219 | * - CHG_STA_TO
|
| 220 | * - CHG_STA_DONE
|
| 221 |
|
| 222 | enum {
|
| 223 | MAX77759_INT_MAXQ,
|
| 224 | MAX77759_INT_TOPSYS,
|
| 225 | MAX77759_INT_CHGR,
|
| 226 | };
|
| 227 |
|
| 228 | enum {
|
| 229 | MAX77759_TOPSYS_INT_TSHDN,
|
| 230 | MAX77759_TOPSYS_INT_SYSOVLO,
|
| 231 | MAX77759_TOPSYS_INT_SYSUVLO,
|
| 232 | MAX77759_TOPSYS_INT_FSHIP_NOT_RD,
|
| 233 | };
|
| 234 |
|
| 235 | enum {
|
| 236 | MAX77759_MAXQ_INT_APCMDRESI,
|
| 237 | MAX77759_MAXQ_INT_SYSMSGI,
|
| 238 | MAX77759_MAXQ_INT_GPIO,
|
| 239 | MAX77759_MAXQ_INT_UIC1,
|
| 240 | MAX77759_MAXQ_INT_UIC2,
|
| 241 | MAX77759_MAXQ_INT_UIC3,
|
| 242 | MAX77759_MAXQ_INT_UIC4,
|
| 243 | };
|
| 244 |
|
| 245 | enum {
|
| 246 | MAX77759_CHGR_INT1_AICL,
|
| 247 | MAX77759_CHGR_INT1_CHGIN,
|
| 248 | MAX77759_CHGR_INT1_WCIN,
|
| 249 | MAX77759_CHGR_INT1_CHG,
|
| 250 | MAX77759_CHGR_INT1_BAT,
|
| 251 | MAX77759_CHGR_INT1_INLIM,
|
| 252 | MAX77759_CHGR_INT1_THM2,
|
| 253 | MAX77759_CHGR_INT1_BYP,
|
| 254 | MAX77759_CHGR_INT2_INSEL,
|
| 255 | MAX77759_CHGR_INT2_SYS_UVLO1,
|
| 256 | MAX77759_CHGR_INT2_SYS_UVLO2,
|
| 257 | MAX77759_CHGR_INT2_BAT_OILO,
|
| 258 | MAX77759_CHGR_INT2_CHG_STA_CC,
|
| 259 | MAX77759_CHGR_INT2_CHG_STA_CV,
|
| 260 | MAX77759_CHGR_INT2_CHG_STA_TO,
|
| 261 | MAX77759_CHGR_INT2_CHG_STA_DONE,
|
| 262 | };
|
| 263 |
|
| 264 | static const struct regmap_irq max77759_pmic_irqs[] = {
|
| 265 | REGMAP_IRQ_REG(MAX77759_INT_MAXQ, 0, MAX77759_PMIC_REG_INTSRC_MAXQ),
|
| 266 | REGMAP_IRQ_REG(MAX77759_INT_TOPSYS, 0, MAX77759_PMIC_REG_INTSRC_TOPSYS),
|
| 267 | REGMAP_IRQ_REG(MAX77759_INT_CHGR, 0, MAX77759_PMIC_REG_INTSRC_CHGR),
|
| 268 | };
|
| 269 |
|
| 270 | static const struct regmap_irq max77759_maxq_irqs[] = {
|
| 271 | REGMAP_IRQ_REG(MAX77759_MAXQ_INT_APCMDRESI, 0, MAX77759_MAXQ_REG_UIC_INT1_APCMDRESI),
|
| 272 | REGMAP_IRQ_REG(MAX77759_MAXQ_INT_SYSMSGI, 0, MAX77759_MAXQ_REG_UIC_INT1_SYSMSGI),
|
| 273 | REGMAP_IRQ_REG(MAX77759_MAXQ_INT_GPIO, 0, GENMASK(1, 0)),
|
| 274 | REGMAP_IRQ_REG(MAX77759_MAXQ_INT_UIC1, 0, GENMASK(5, 2)),
|
| 275 | REGMAP_IRQ_REG(MAX77759_MAXQ_INT_UIC2, 1, GENMASK(7, 0)),
|
| 276 | REGMAP_IRQ_REG(MAX77759_MAXQ_INT_UIC3, 2, GENMASK(7, 0)),
|
| 277 | REGMAP_IRQ_REG(MAX77759_MAXQ_INT_UIC4, 3, GENMASK(7, 0)),
|
| 278 | };
|
| 279 |
|
| 280 | static const struct regmap_irq max77759_topsys_irqs[] = {
|
| 281 | REGMAP_IRQ_REG(MAX77759_TOPSYS_INT_TSHDN, 0, MAX77759_PMIC_REG_TOPSYS_INT_TSHDN),
|
| 282 | REGMAP_IRQ_REG(MAX77759_TOPSYS_INT_SYSOVLO, 0, MAX77759_PMIC_REG_TOPSYS_INT_SYSOVLO),
|
| 283 | REGMAP_IRQ_REG(MAX77759_TOPSYS_INT_SYSUVLO, 0, MAX77759_PMIC_REG_TOPSYS_INT_SYSUVLO),
|
| 284 | REGMAP_IRQ_REG(MAX77759_TOPSYS_INT_FSHIP_NOT_RD, 0, MAX77759_PMIC_REG_TOPSYS_INT_FSHIP),
|
| 285 | };
|
| 286 |
|
| 287 | static const struct regmap_irq max77759_chgr_irqs[] = {
|
| 288 | REGMAP_IRQ_REG(MAX77759_CHGR_INT1_AICL, 0, MAX77759_CHGR_REG_CHG_INT_AICL),
|
| 289 | REGMAP_IRQ_REG(MAX77759_CHGR_INT1_CHGIN, 0, MAX77759_CHGR_REG_CHG_INT_CHGIN),
|
| 290 | REGMAP_IRQ_REG(MAX77759_CHGR_INT1_WCIN, 0, MAX77759_CHGR_REG_CHG_INT_WCIN),
|
| 291 | REGMAP_IRQ_REG(MAX77759_CHGR_INT1_CHG, 0, MAX77759_CHGR_REG_CHG_INT_CHG),
|
| 292 | REGMAP_IRQ_REG(MAX77759_CHGR_INT1_BAT, 0, MAX77759_CHGR_REG_CHG_INT_BAT),
|
| 293 | REGMAP_IRQ_REG(MAX77759_CHGR_INT1_INLIM, 0, MAX77759_CHGR_REG_CHG_INT_INLIM),
|
| 294 | REGMAP_IRQ_REG(MAX77759_CHGR_INT1_THM2, 0, MAX77759_CHGR_REG_CHG_INT_THM2),
|
| 295 | REGMAP_IRQ_REG(MAX77759_CHGR_INT1_BYP, 0, MAX77759_CHGR_REG_CHG_INT_BYP),
|
| 296 | REGMAP_IRQ_REG(MAX77759_CHGR_INT2_INSEL, 1, MAX77759_CHGR_REG_CHG_INT2_INSEL),
|
| 297 | REGMAP_IRQ_REG(MAX77759_CHGR_INT2_SYS_UVLO1, 1, MAX77759_CHGR_REG_CHG_INT2_SYS_UVLO1),
|
| 298 | REGMAP_IRQ_REG(MAX77759_CHGR_INT2_SYS_UVLO2, 1, MAX77759_CHGR_REG_CHG_INT2_SYS_UVLO2),
|
| 299 | REGMAP_IRQ_REG(MAX77759_CHGR_INT2_BAT_OILO, 1, MAX77759_CHGR_REG_CHG_INT2_BAT_OILO),
|
| 300 | REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_CC, 1, MAX77759_CHGR_REG_CHG_INT2_CHG_STA_CC),
|
| 301 | REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_CV, 1, MAX77759_CHGR_REG_CHG_INT2_CHG_STA_CV),
|
| 302 | REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_TO, 1, MAX77759_CHGR_REG_CHG_INT2_CHG_STA_TO),
|
| 303 | REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_DONE, 1, MAX77759_CHGR_REG_CHG_INT2_CHG_STA_DONE),
|
| 304 | };
|
| 305 |
|
| 306 | static const struct regmap_irq_chip max77759_pmic_irq_chip = {
|
| 307 | .name = "max77759-pmic",
|
| 308 |
|
| 309 | .status_base = MAX77759_PMIC_REG_INTSRC,
|
| 310 | .mask_base = MAX77759_PMIC_REG_INTSRCMASK,
|
| 311 | .num_regs = 1,
|
| 312 | .irqs = max77759_pmic_irqs,
|
| 313 | .num_irqs = ARRAY_SIZE(max77759_pmic_irqs),
|
| 314 | };
|
| 315 |
|
| 316 | |
| 317 | * We can let regmap-irq auto-ack the topsys interrupt bits as required, but
|
| 318 | * for all others the individual drivers need to know which interrupt bit
|
| 319 | * exactly is set inside their interrupt handlers, and therefore we can not set
|
| 320 | * .ack_base for those.
|
| 321 |
|
| 322 | static const struct regmap_irq_chip max77759_maxq_irq_chip = {
|
| 323 | .name = "max77759-maxq",
|
| 324 | .domain_suffix = "MAXQ",
|
| 325 | .status_base = MAX77759_MAXQ_REG_UIC_INT1,
|
| 326 | .mask_base = MAX77759_MAXQ_REG_UIC_INT1_M,
|
| 327 | .num_regs = 4,
|
| 328 | .irqs = max77759_maxq_irqs,
|
| 329 | .num_irqs = ARRAY_SIZE(max77759_maxq_irqs),
|
| 330 | };
|
| 331 |
|
| 332 | static const struct regmap_irq_chip max77759_topsys_irq_chip = {
|
| 333 | .name = "max77759-topsys",
|
| 334 | .domain_suffix = "TOPSYS",
|
| 335 | .status_base = MAX77759_PMIC_REG_TOPSYS_INT,
|
| 336 | .mask_base = MAX77759_PMIC_REG_TOPSYS_INT_MASK,
|
| 337 | .ack_base = MAX77759_PMIC_REG_TOPSYS_INT,
|
| 338 | .num_regs = 1,
|
| 339 | .irqs = max77759_topsys_irqs,
|
| 340 | .num_irqs = ARRAY_SIZE(max77759_topsys_irqs),
|
| 341 | };
|
| 342 |
|
| 343 | static const struct regmap_irq_chip max77759_chgr_irq_chip = {
|
| 344 | .name = "max77759-chgr",
|
| 345 | .domain_suffix = "CHGR",
|
| 346 | .status_base = MAX77759_CHGR_REG_CHG_INT,
|
| 347 | .mask_base = MAX77759_CHGR_REG_CHG_INT_MASK,
|
| 348 | .ack_base = MAX77759_CHGR_REG_CHG_INT,
|
| 349 | .num_regs = 2,
|
| 350 | .irqs = max77759_chgr_irqs,
|
| 351 | .num_irqs = ARRAY_SIZE(max77759_chgr_irqs),
|
| 352 | };
|
| 353 |
|
| 354 | static const struct max77759_i2c_subdev max77759_i2c_subdevs[] = {
|
| 355 | {
|
| 356 | .id = MAX77759_I2C_SUBDEV_ID_MAXQ,
|
| 357 | .cfg = &max77759_regmap_config_maxq,
|
| 358 |
|
| 359 | },
|
| 360 | {
|
| 361 | .id = MAX77759_I2C_SUBDEV_ID_CHARGER,
|
| 362 | .cfg = &max77759_regmap_config_charger,
|
| 363 | .i2c_address = 0x69,
|
| 364 | },
|
| 365 | };
|
| 366 |
|
| 367 | static const struct resource max77759_gpio_resources[] = {
|
| 368 | DEFINE_RES_IRQ_NAMED(MAX77759_MAXQ_INT_GPIO, "GPI"),
|
| 369 | };
|
| 370 |
|
| 371 | static const struct resource max77759_charger_resources[] = {
|
| 372 | DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_AICL, "AICL"),
|
| 373 | DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_CHGIN, "CHGIN"),
|
| 374 | DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_WCIN, "WCIN"),
|
| 375 | DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_CHG, "CHG"),
|
| 376 | DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_BAT, "BAT"),
|
| 377 | DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_INLIM, "INLIM"),
|
| 378 | DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_THM2, "THM2"),
|
| 379 | DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_BYP, "BYP"),
|
| 380 | DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_INSEL, "INSEL"),
|
| 381 | DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_SYS_UVLO1, "SYS_UVLO1"),
|
| 382 | DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_SYS_UVLO2, "SYS_UVLO2"),
|
| 383 | DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_BAT_OILO, "BAT_OILO"),
|
| 384 | DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_CHG_STA_CC, "CHG_STA_CC"),
|
| 385 | DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_CHG_STA_CV, "CHG_STA_CV"),
|
| 386 | DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_CHG_STA_TO, "CHG_STA_TO"),
|
| 387 | DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_CHG_STA_DONE, "CHG_STA_DONE"),
|
| 388 | };
|
| 389 |
|
| 390 | static const struct mfd_cell max77759_cells[] = {
|
| 391 | MFD_CELL_OF("max77759-nvmem", NULL, NULL, 0, 0,
|
| 392 | "maxim,max77759-nvmem"),
|
| 393 | };
|
| 394 |
|
| 395 | static const struct mfd_cell max77759_maxq_cells[] = {
|
| 396 | MFD_CELL_OF("max77759-gpio", max77759_gpio_resources, NULL, 0, 0,
|
| 397 | "maxim,max77759-gpio"),
|
| 398 | };
|
| 399 |
|
| 400 | static const struct mfd_cell max77759_charger_cells[] = {
|
| 401 | MFD_CELL_RES("max77759-charger", max77759_charger_resources),
|
| 402 | };
|
| 403 |
|
| 404 | int max77759_maxq_command(struct max77759 *max77759,
|
| 405 | const struct max77759_maxq_command *cmd,
|
| 406 | struct max77759_maxq_response *rsp)
|
| 407 | {
|
| 408 | DEFINE_FLEX(struct max77759_maxq_response, _rsp, rsp, length, 1);
|
| 409 | struct device *dev = regmap_get_device(max77759->regmap_maxq);
|
| 410 | static const unsigned int timeout_ms = 200;
|
| 411 | int ret;
|
| 412 |
|
| 413 | if (cmd->length > MAX77759_MAXQ_OPCODE_MAXLENGTH)
|
| 414 | return -EINVAL;
|
| 415 |
|
| 416 | |
| 417 | * As a convenience for API users when issuing simple commands, rsp is
|
| 418 | * allowed to be NULL. In that case we need a temporary here to write
|
| 419 | * the response to, as we need to verify that the command was indeed
|
| 420 | * completed correctly.
|
| 421 |
|
| 422 | if (!rsp)
|
| 423 | rsp = _rsp;
|
| 424 |
|
| 425 | if (!rsp->length || rsp->length > MAX77759_MAXQ_OPCODE_MAXLENGTH)
|
| 426 | return -EINVAL;
|
| 427 |
|
| 428 | guard(mutex)(&max77759->maxq_lock);
|
| 429 |
|
| 430 | reinit_completion(&max77759->cmd_done);
|
| 431 |
|
| 432 | |
| 433 | * MaxQ latches the message when the DATAOUT32 register is written. If
|
| 434 | * cmd->length is shorter we still need to write 0 to it.
|
| 435 |
|
| 436 | ret = regmap_bulk_write(max77759->regmap_maxq,
|
| 437 | MAX77759_MAXQ_REG_AP_DATAOUT0, cmd->cmd,
|
| 438 | cmd->length);
|
| 439 | if (!ret && cmd->length < MAX77759_MAXQ_OPCODE_MAXLENGTH)
|
| 440 | ret = regmap_write(max77759->regmap_maxq,
|
| 441 | MAX77759_MAXQ_REG_AP_DATAOUT32, 0);
|
| 442 | if (ret) {
|
| 443 | dev_err(dev, "writing command failed: %d\n", ret);
|
| 444 | return ret;
|
| 445 | }
|
| 446 |
|
| 447 |
|
| 448 | if (!wait_for_completion_timeout(&max77759->cmd_done,
|
| 449 | msecs_to_jiffies(timeout_ms))) {
|
| 450 | dev_err(dev, "timed out waiting for response\n");
|
| 451 | return -ETIMEDOUT;
|
| 452 | }
|
| 453 |
|
| 454 | ret = regmap_bulk_read(max77759->regmap_maxq,
|
| 455 | MAX77759_MAXQ_REG_AP_DATAIN0,
|
| 456 | rsp->rsp, rsp->length);
|
| 457 | if (ret) {
|
| 458 | dev_err(dev, "reading response failed: %d\n", ret);
|
| 459 | return ret;
|
| 460 | }
|
| 461 |
|
| 462 | |
| 463 | * As per the protocol, the first byte of the reply will match the
|
| 464 | * request.
|
| 465 |
|
| 466 | if (cmd->cmd[0] != rsp->rsp[0]) {
|
| 467 | dev_err(dev, "unexpected opcode response for %#.2x: %*ph\n",
|
| 468 | cmd->cmd[0], (int)rsp->length, rsp->rsp);
|
| 469 | return -EIO;
|
| 470 | }
|
| 471 |
|
| 472 | return 0;
|
| 473 | }
|
| 474 | EXPORT_SYMBOL_GPL(max77759_maxq_command);
|
| 475 |
|
| 476 | static irqreturn_t apcmdres_irq_handler(int irq, void *irq_data)
|
| 477 | {
|
| 478 | struct max77759 *max77759 = irq_data;
|
| 479 |
|
| 480 | regmap_write(max77759->regmap_maxq, MAX77759_MAXQ_REG_UIC_INT1,
|
| 481 | MAX77759_MAXQ_REG_UIC_INT1_APCMDRESI);
|
| 482 |
|
| 483 | complete(&max77759->cmd_done);
|
| 484 |
|
| 485 | return IRQ_HANDLED;
|
| 486 | }
|
| 487 |
|
| 488 | static int max77759_create_i2c_subdev(struct i2c_client *client,
|
| 489 | struct max77759 *max77759,
|
| 490 | const struct max77759_i2c_subdev *sd)
|
| 491 | {
|
| 492 | struct i2c_client *sub;
|
| 493 | struct regmap *regmap;
|
| 494 | int ret;
|
| 495 |
|
| 496 | |
| 497 | * If 'sd' has an I2C address, 'sub' will be assigned a new 'dummy'
|
| 498 | * device, otherwise use it as-is.
|
| 499 |
|
| 500 | sub = client;
|
| 501 | if (sd->i2c_address) {
|
| 502 | sub = devm_i2c_new_dummy_device(&client->dev,
|
| 503 | client->adapter,
|
| 504 | sd->i2c_address);
|
| 505 |
|
| 506 | if (IS_ERR(sub))
|
| 507 | return dev_err_probe(&client->dev, PTR_ERR(sub),
|
| 508 | "failed to claim I2C device %s\n",
|
| 509 | sd->cfg->name);
|
| 510 | }
|
| 511 |
|
| 512 | regmap = devm_regmap_init_i2c(sub, sd->cfg);
|
| 513 | if (IS_ERR(regmap))
|
| 514 | return dev_err_probe(&sub->dev, PTR_ERR(regmap),
|
| 515 | "regmap init for '%s' failed\n",
|
| 516 | sd->cfg->name);
|
| 517 |
|
| 518 | ret = regmap_attach_dev(&client->dev, regmap, sd->cfg);
|
| 519 | if (ret)
|
| 520 | return dev_err_probe(&client->dev, ret,
|
| 521 | "regmap attach of '%s' failed\n",
|
| 522 | sd->cfg->name);
|
| 523 |
|
| 524 | if (sd->id == MAX77759_I2C_SUBDEV_ID_MAXQ)
|
| 525 | max77759->regmap_maxq = regmap;
|
| 526 | else if (sd->id == MAX77759_I2C_SUBDEV_ID_CHARGER)
|
| 527 | max77759->regmap_charger = regmap;
|
| 528 |
|
| 529 | return 0;
|
| 530 | }
|
| 531 |
|
| 532 | static int max77759_add_chained_irq_chip(struct device *dev,
|
| 533 | struct regmap *regmap,
|
| 534 | int pirq,
|
| 535 | struct regmap_irq_chip_data *parent,
|
| 536 | const struct regmap_irq_chip *chip,
|
| 537 | struct regmap_irq_chip_data **data)
|
| 538 | {
|
| 539 | int irq, ret;
|
| 540 |
|
| 541 | irq = regmap_irq_get_virq(parent, pirq);
|
| 542 | if (irq < 0)
|
| 543 | return dev_err_probe(dev, irq,
|
| 544 | "failed to get parent vIRQ(%d) for chip %s\n",
|
| 545 | pirq, chip->name);
|
| 546 |
|
| 547 | ret = devm_regmap_add_irq_chip(dev, regmap, irq,
|
| 548 | IRQF_ONESHOT | IRQF_SHARED, 0, chip,
|
| 549 | data);
|
| 550 | if (ret)
|
| 551 | return dev_err_probe(dev, ret, "failed to add %s IRQ chip\n",
|
| 552 | chip->name);
|
| 553 |
|
| 554 | return 0;
|
| 555 | }
|
| 556 |
|
| 557 | static int max77759_add_chained_maxq(struct i2c_client *client,
|
| 558 | struct max77759 *max77759,
|
| 559 | struct regmap_irq_chip_data *parent)
|
| 560 | {
|
| 561 | struct regmap_irq_chip_data *irq_chip_data;
|
| 562 | int apcmdres_irq;
|
| 563 | int ret;
|
| 564 |
|
| 565 | ret = max77759_add_chained_irq_chip(&client->dev,
|
| 566 | max77759->regmap_maxq,
|
| 567 | MAX77759_INT_MAXQ,
|
| 568 | parent,
|
| 569 | &max77759_maxq_irq_chip,
|
| 570 | &irq_chip_data);
|
| 571 | if (ret)
|
| 572 | return ret;
|
| 573 |
|
| 574 | init_completion(&max77759->cmd_done);
|
| 575 | apcmdres_irq = regmap_irq_get_virq(irq_chip_data,
|
| 576 | MAX77759_MAXQ_INT_APCMDRESI);
|
| 577 |
|
| 578 | ret = devm_request_threaded_irq(&client->dev, apcmdres_irq,
|
| 579 | NULL, apcmdres_irq_handler,
|
| 580 | IRQF_ONESHOT | IRQF_SHARED,
|
| 581 | dev_name(&client->dev), max77759);
|
| 582 | if (ret)
|
| 583 | return dev_err_probe(&client->dev, ret,
|
| 584 | "MAX77759_MAXQ_INT_APCMDRESI failed\n");
|
| 585 |
|
| 586 | ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_AUTO,
|
| 587 | max77759_maxq_cells,
|
| 588 | ARRAY_SIZE(max77759_maxq_cells),
|
| 589 | NULL, 0,
|
| 590 | regmap_irq_get_domain(irq_chip_data));
|
| 591 | if (ret)
|
| 592 | return dev_err_probe(&client->dev, ret,
|
| 593 | "failed to add child devices (MaxQ)\n");
|
| 594 |
|
| 595 | return 0;
|
| 596 | }
|
| 597 |
|
| 598 | static int max77759_add_chained_topsys(struct i2c_client *client,
|
| 599 | struct max77759 *max77759,
|
| 600 | struct regmap_irq_chip_data *parent)
|
| 601 | {
|
| 602 | struct regmap_irq_chip_data *irq_chip_data;
|
| 603 | int ret;
|
| 604 |
|
| 605 | ret = max77759_add_chained_irq_chip(&client->dev,
|
| 606 | max77759->regmap_top,
|
| 607 | MAX77759_INT_TOPSYS,
|
| 608 | parent,
|
| 609 | &max77759_topsys_irq_chip,
|
| 610 | &irq_chip_data);
|
| 611 | if (ret)
|
| 612 | return ret;
|
| 613 |
|
| 614 | return 0;
|
| 615 | }
|
| 616 |
|
| 617 | static int max77759_add_chained_charger(struct i2c_client *client,
|
| 618 | struct max77759 *max77759,
|
| 619 | struct regmap_irq_chip_data *parent)
|
| 620 | {
|
| 621 | struct regmap_irq_chip_data *irq_chip_data;
|
| 622 | int ret;
|
| 623 |
|
| 624 | ret = max77759_add_chained_irq_chip(&client->dev,
|
| 625 | max77759->regmap_charger,
|
| 626 | MAX77759_INT_CHGR,
|
| 627 | parent,
|
| 628 | &max77759_chgr_irq_chip,
|
| 629 | &irq_chip_data);
|
| 630 | if (ret)
|
| 631 | return ret;
|
| 632 |
|
| 633 | ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_AUTO,
|
| 634 | max77759_charger_cells,
|
| 635 | ARRAY_SIZE(max77759_charger_cells),
|
| 636 | NULL, 0,
|
| 637 | regmap_irq_get_domain(irq_chip_data));
|
| 638 | if (ret)
|
| 639 | return dev_err_probe(&client->dev, ret,
|
| 640 | "failed to add child devices (charger)\n");
|
| 641 |
|
| 642 | return 0;
|
| 643 | }
|
| 644 |
|
| 645 | static int max77759_probe(struct i2c_client *client)
|
| 646 | {
|
| 647 | struct regmap_irq_chip_data *irq_chip_data_pmic;
|
| 648 | struct max77759 *max77759;
|
| 649 | unsigned int pmic_id;
|
| 650 | int ret;
|
| 651 |
|
| 652 | max77759 = devm_kzalloc(&client->dev, sizeof(*max77759), GFP_KERNEL);
|
| 653 | if (!max77759)
|
| 654 | return -ENOMEM;
|
| 655 |
|
| 656 | i2c_set_clientdata(client, max77759);
|
| 657 |
|
| 658 | max77759->regmap_top = devm_regmap_init_i2c(client,
|
| 659 | &max77759_regmap_config_top);
|
| 660 | if (IS_ERR(max77759->regmap_top))
|
| 661 | return dev_err_probe(&client->dev, PTR_ERR(max77759->regmap_top),
|
| 662 | "regmap init for '%s' failed\n",
|
| 663 | max77759_regmap_config_top.name);
|
| 664 |
|
| 665 | ret = regmap_read(max77759->regmap_top,
|
| 666 | MAX77759_PMIC_REG_PMIC_ID, &pmic_id);
|
| 667 | if (ret)
|
| 668 | return dev_err_probe(&client->dev, ret,
|
| 669 | "unable to read device ID\n");
|
| 670 |
|
| 671 | if (pmic_id != MAX77759_CHIP_ID)
|
| 672 | return dev_err_probe(&client->dev, -ENODEV,
|
| 673 | "unsupported device ID %#.2x (%d)\n",
|
| 674 | pmic_id, pmic_id);
|
| 675 |
|
| 676 | ret = devm_mutex_init(&client->dev, &max77759->maxq_lock);
|
| 677 | if (ret)
|
| 678 | return ret;
|
| 679 |
|
| 680 | for (int i = 0; i < ARRAY_SIZE(max77759_i2c_subdevs); i++) {
|
| 681 | ret = max77759_create_i2c_subdev(client, max77759,
|
| 682 | &max77759_i2c_subdevs[i]);
|
| 683 | if (ret)
|
| 684 | return ret;
|
| 685 | }
|
| 686 |
|
| 687 | ret = devm_regmap_add_irq_chip(&client->dev, max77759->regmap_top,
|
| 688 | client->irq, IRQF_ONESHOT | IRQF_SHARED, 0,
|
| 689 | &max77759_pmic_irq_chip,
|
| 690 | &irq_chip_data_pmic);
|
| 691 | if (ret)
|
| 692 | return dev_err_probe(&client->dev, ret,
|
| 693 | "failed to add IRQ chip '%s'\n",
|
| 694 | max77759_pmic_irq_chip.name);
|
| 695 |
|
| 696 | ret = max77759_add_chained_maxq(client, max77759, irq_chip_data_pmic);
|
| 697 | if (ret)
|
| 698 | return ret;
|
| 699 |
|
| 700 | ret = max77759_add_chained_topsys(client, max77759, irq_chip_data_pmic);
|
| 701 | if (ret)
|
| 702 | return ret;
|
| 703 |
|
| 704 | ret = max77759_add_chained_charger(client, max77759, irq_chip_data_pmic);
|
| 705 | if (ret)
|
| 706 | return ret;
|
| 707 |
|
| 708 | return devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_AUTO,
|
| 709 | max77759_cells, ARRAY_SIZE(max77759_cells),
|
| 710 | NULL, 0,
|
| 711 | regmap_irq_get_domain(irq_chip_data_pmic));
|
| 712 | }
|
| 713 |
|
| 714 | static const struct i2c_device_id max77759_i2c_id[] = {
|
| 715 | { "max77759" },
|
| 716 | { }
|
| 717 | };
|
| 718 | MODULE_DEVICE_TABLE(i2c, max77759_i2c_id);
|
| 719 |
|
| 720 | static const struct of_device_id max77759_of_id[] = {
|
| 721 | { .compatible = "maxim,max77759", },
|
| 722 | { }
|
| 723 | };
|
| 724 | MODULE_DEVICE_TABLE(of, max77759_of_id);
|
| 725 |
|
| 726 | static struct i2c_driver max77759_i2c_driver = {
|
| 727 | .driver = {
|
| 728 | .name = "max77759",
|
| 729 | .of_match_table = max77759_of_id,
|
| 730 | },
|
| 731 | .probe = max77759_probe,
|
| 732 | .id_table = max77759_i2c_id,
|
| 733 | };
|
| 734 | module_i2c_driver(max77759_i2c_driver);
|
| 735 |
|
| 736 | MODULE_AUTHOR("André Draszik <[email protected]>");
|
| 737 | MODULE_DESCRIPTION("Maxim MAX77759 core driver");
|
| 738 | MODULE_LICENSE("GPL");
|