| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Maxim MAX77620 MFD Driver |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 7 | 8 | * Laxman Dewangan <ldewangan@nvidia.com> |
|---|
| 8 | 9 | * Chaitanya Bandi <bandik@nvidia.com> |
|---|
| 9 | 10 | * Mallikarjun Kasoju <mkasoju@nvidia.com> |
|---|
| 10 | | - * |
|---|
| 11 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 12 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 13 | | - * published by the Free Software Foundation. |
|---|
| 14 | 11 | */ |
|---|
| 15 | 12 | |
|---|
| 16 | 13 | /****************** Teminology used in driver ******************** |
|---|
| .. | .. |
|---|
| 36 | 33 | #include <linux/of_device.h> |
|---|
| 37 | 34 | #include <linux/regmap.h> |
|---|
| 38 | 35 | #include <linux/slab.h> |
|---|
| 36 | + |
|---|
| 37 | +static struct max77620_chip *max77620_scratch; |
|---|
| 39 | 38 | |
|---|
| 40 | 39 | static const struct resource gpio_resources[] = { |
|---|
| 41 | 40 | DEFINE_RES_IRQ(MAX77620_IRQ_TOP_GPIO), |
|---|
| .. | .. |
|---|
| 111 | 110 | }, |
|---|
| 112 | 111 | }; |
|---|
| 113 | 112 | |
|---|
| 113 | +static const struct mfd_cell max77663_children[] = { |
|---|
| 114 | + { .name = "max77620-pinctrl", }, |
|---|
| 115 | + { .name = "max77620-clock", }, |
|---|
| 116 | + { .name = "max77663-pmic", }, |
|---|
| 117 | + { .name = "max77620-watchdog", }, |
|---|
| 118 | + { |
|---|
| 119 | + .name = "max77620-gpio", |
|---|
| 120 | + .resources = gpio_resources, |
|---|
| 121 | + .num_resources = ARRAY_SIZE(gpio_resources), |
|---|
| 122 | + }, { |
|---|
| 123 | + .name = "max77620-rtc", |
|---|
| 124 | + .resources = rtc_resources, |
|---|
| 125 | + .num_resources = ARRAY_SIZE(rtc_resources), |
|---|
| 126 | + }, { |
|---|
| 127 | + .name = "max77663-power", |
|---|
| 128 | + .resources = power_resources, |
|---|
| 129 | + .num_resources = ARRAY_SIZE(power_resources), |
|---|
| 130 | + }, |
|---|
| 131 | +}; |
|---|
| 132 | + |
|---|
| 114 | 133 | static const struct regmap_range max77620_readable_ranges[] = { |
|---|
| 115 | 134 | regmap_reg_range(MAX77620_REG_CNFGGLBL1, MAX77620_REG_DVSSD4), |
|---|
| 116 | 135 | }; |
|---|
| .. | .. |
|---|
| 158 | 177 | .rd_table = &max77620_readable_table, |
|---|
| 159 | 178 | .wr_table = &max77620_writable_table, |
|---|
| 160 | 179 | .volatile_table = &max77620_volatile_table, |
|---|
| 180 | + .use_single_write = true, |
|---|
| 161 | 181 | }; |
|---|
| 162 | 182 | |
|---|
| 163 | 183 | static const struct regmap_config max20024_regmap_config = { |
|---|
| .. | .. |
|---|
| 168 | 188 | .cache_type = REGCACHE_RBTREE, |
|---|
| 169 | 189 | .rd_table = &max20024_readable_table, |
|---|
| 170 | 190 | .wr_table = &max77620_writable_table, |
|---|
| 191 | + .volatile_table = &max77620_volatile_table, |
|---|
| 192 | +}; |
|---|
| 193 | + |
|---|
| 194 | +static const struct regmap_range max77663_readable_ranges[] = { |
|---|
| 195 | + regmap_reg_range(MAX77620_REG_CNFGGLBL1, MAX77620_REG_CID5), |
|---|
| 196 | +}; |
|---|
| 197 | + |
|---|
| 198 | +static const struct regmap_access_table max77663_readable_table = { |
|---|
| 199 | + .yes_ranges = max77663_readable_ranges, |
|---|
| 200 | + .n_yes_ranges = ARRAY_SIZE(max77663_readable_ranges), |
|---|
| 201 | +}; |
|---|
| 202 | + |
|---|
| 203 | +static const struct regmap_range max77663_writable_ranges[] = { |
|---|
| 204 | + regmap_reg_range(MAX77620_REG_CNFGGLBL1, MAX77620_REG_CID5), |
|---|
| 205 | +}; |
|---|
| 206 | + |
|---|
| 207 | +static const struct regmap_access_table max77663_writable_table = { |
|---|
| 208 | + .yes_ranges = max77663_writable_ranges, |
|---|
| 209 | + .n_yes_ranges = ARRAY_SIZE(max77663_writable_ranges), |
|---|
| 210 | +}; |
|---|
| 211 | + |
|---|
| 212 | +static const struct regmap_config max77663_regmap_config = { |
|---|
| 213 | + .name = "power-slave", |
|---|
| 214 | + .reg_bits = 8, |
|---|
| 215 | + .val_bits = 8, |
|---|
| 216 | + .max_register = MAX77620_REG_CID5 + 1, |
|---|
| 217 | + .cache_type = REGCACHE_RBTREE, |
|---|
| 218 | + .rd_table = &max77663_readable_table, |
|---|
| 219 | + .wr_table = &max77663_writable_table, |
|---|
| 171 | 220 | .volatile_table = &max77620_volatile_table, |
|---|
| 172 | 221 | }; |
|---|
| 173 | 222 | |
|---|
| .. | .. |
|---|
| 240 | 289 | case MAX77620: |
|---|
| 241 | 290 | fps_min_period = MAX77620_FPS_PERIOD_MIN_US; |
|---|
| 242 | 291 | break; |
|---|
| 292 | + case MAX77663: |
|---|
| 293 | + fps_min_period = MAX20024_FPS_PERIOD_MIN_US; |
|---|
| 294 | + break; |
|---|
| 243 | 295 | default: |
|---|
| 244 | 296 | return -EINVAL; |
|---|
| 245 | 297 | } |
|---|
| .. | .. |
|---|
| 274 | 326 | case MAX77620: |
|---|
| 275 | 327 | fps_max_period = MAX77620_FPS_PERIOD_MAX_US; |
|---|
| 276 | 328 | break; |
|---|
| 329 | + case MAX77663: |
|---|
| 330 | + fps_max_period = MAX20024_FPS_PERIOD_MAX_US; |
|---|
| 331 | + break; |
|---|
| 277 | 332 | default: |
|---|
| 278 | 333 | return -EINVAL; |
|---|
| 279 | 334 | } |
|---|
| 280 | 335 | |
|---|
| 281 | 336 | for (fps_id = 0; fps_id < MAX77620_FPS_COUNT; fps_id++) { |
|---|
| 282 | 337 | sprintf(fps_name, "fps%d", fps_id); |
|---|
| 283 | | - if (!strcmp(fps_np->name, fps_name)) |
|---|
| 338 | + if (of_node_name_eq(fps_np, fps_name)) |
|---|
| 284 | 339 | break; |
|---|
| 285 | 340 | } |
|---|
| 286 | 341 | |
|---|
| 287 | 342 | if (fps_id == MAX77620_FPS_COUNT) { |
|---|
| 288 | | - dev_err(dev, "FPS node name %s is not valid\n", fps_np->name); |
|---|
| 343 | + dev_err(dev, "FPS node name %pOFn is not valid\n", fps_np); |
|---|
| 289 | 344 | return -EINVAL; |
|---|
| 290 | 345 | } |
|---|
| 291 | 346 | |
|---|
| .. | .. |
|---|
| 362 | 417 | |
|---|
| 363 | 418 | for_each_child_of_node(fps_np, fps_child) { |
|---|
| 364 | 419 | ret = max77620_config_fps(chip, fps_child); |
|---|
| 365 | | - if (ret < 0) |
|---|
| 420 | + if (ret < 0) { |
|---|
| 421 | + of_node_put(fps_child); |
|---|
| 422 | + of_node_put(fps_np); |
|---|
| 366 | 423 | return ret; |
|---|
| 424 | + } |
|---|
| 367 | 425 | } |
|---|
| 426 | + of_node_put(fps_np); |
|---|
| 368 | 427 | |
|---|
| 369 | 428 | config = chip->enable_global_lpm ? MAX77620_ONOFFCNFG2_SLP_LPM_MSK : 0; |
|---|
| 370 | 429 | ret = regmap_update_bits(chip->rmap, MAX77620_REG_ONOFFCNFG2, |
|---|
| .. | .. |
|---|
| 375 | 434 | } |
|---|
| 376 | 435 | |
|---|
| 377 | 436 | skip_fps: |
|---|
| 437 | + if (chip->chip_id == MAX77663) |
|---|
| 438 | + return 0; |
|---|
| 439 | + |
|---|
| 378 | 440 | /* Enable wake on EN0 pin */ |
|---|
| 379 | 441 | ret = regmap_update_bits(chip->rmap, MAX77620_REG_ONOFFCNFG2, |
|---|
| 380 | 442 | MAX77620_ONOFFCNFG2_WK_EN0, |
|---|
| .. | .. |
|---|
| 423 | 485 | return ret; |
|---|
| 424 | 486 | } |
|---|
| 425 | 487 | |
|---|
| 488 | +static void max77620_pm_power_off(void) |
|---|
| 489 | +{ |
|---|
| 490 | + struct max77620_chip *chip = max77620_scratch; |
|---|
| 491 | + |
|---|
| 492 | + regmap_update_bits(chip->rmap, MAX77620_REG_ONOFFCNFG1, |
|---|
| 493 | + MAX77620_ONOFFCNFG1_SFT_RST, |
|---|
| 494 | + MAX77620_ONOFFCNFG1_SFT_RST); |
|---|
| 495 | +} |
|---|
| 496 | + |
|---|
| 426 | 497 | static int max77620_probe(struct i2c_client *client, |
|---|
| 427 | 498 | const struct i2c_device_id *id) |
|---|
| 428 | 499 | { |
|---|
| .. | .. |
|---|
| 430 | 501 | struct max77620_chip *chip; |
|---|
| 431 | 502 | const struct mfd_cell *mfd_cells; |
|---|
| 432 | 503 | int n_mfd_cells; |
|---|
| 504 | + bool pm_off; |
|---|
| 433 | 505 | int ret; |
|---|
| 434 | 506 | |
|---|
| 435 | 507 | chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); |
|---|
| .. | .. |
|---|
| 438 | 510 | |
|---|
| 439 | 511 | i2c_set_clientdata(client, chip); |
|---|
| 440 | 512 | chip->dev = &client->dev; |
|---|
| 441 | | - chip->irq_base = -1; |
|---|
| 442 | 513 | chip->chip_irq = client->irq; |
|---|
| 443 | 514 | chip->chip_id = (enum max77620_chip_id)id->driver_data; |
|---|
| 444 | 515 | |
|---|
| .. | .. |
|---|
| 452 | 523 | mfd_cells = max20024_children; |
|---|
| 453 | 524 | n_mfd_cells = ARRAY_SIZE(max20024_children); |
|---|
| 454 | 525 | rmap_config = &max20024_regmap_config; |
|---|
| 526 | + break; |
|---|
| 527 | + case MAX77663: |
|---|
| 528 | + mfd_cells = max77663_children; |
|---|
| 529 | + n_mfd_cells = ARRAY_SIZE(max77663_children); |
|---|
| 530 | + rmap_config = &max77663_regmap_config; |
|---|
| 455 | 531 | break; |
|---|
| 456 | 532 | default: |
|---|
| 457 | 533 | dev_err(chip->dev, "ChipID is invalid %d\n", chip->chip_id); |
|---|
| .. | .. |
|---|
| 471 | 547 | |
|---|
| 472 | 548 | max77620_top_irq_chip.irq_drv_data = chip; |
|---|
| 473 | 549 | ret = devm_regmap_add_irq_chip(chip->dev, chip->rmap, client->irq, |
|---|
| 474 | | - IRQF_ONESHOT | IRQF_SHARED, |
|---|
| 475 | | - chip->irq_base, &max77620_top_irq_chip, |
|---|
| 550 | + IRQF_ONESHOT | IRQF_SHARED, 0, |
|---|
| 551 | + &max77620_top_irq_chip, |
|---|
| 476 | 552 | &chip->top_irq_data); |
|---|
| 477 | 553 | if (ret < 0) { |
|---|
| 478 | 554 | dev_err(chip->dev, "Failed to add regmap irq: %d\n", ret); |
|---|
| .. | .. |
|---|
| 489 | 565 | if (ret < 0) { |
|---|
| 490 | 566 | dev_err(chip->dev, "Failed to add MFD children: %d\n", ret); |
|---|
| 491 | 567 | return ret; |
|---|
| 568 | + } |
|---|
| 569 | + |
|---|
| 570 | + pm_off = of_device_is_system_power_controller(client->dev.of_node); |
|---|
| 571 | + if (pm_off && !pm_power_off) { |
|---|
| 572 | + max77620_scratch = chip; |
|---|
| 573 | + pm_power_off = max77620_pm_power_off; |
|---|
| 492 | 574 | } |
|---|
| 493 | 575 | |
|---|
| 494 | 576 | return 0; |
|---|
| .. | .. |
|---|
| 546 | 628 | return ret; |
|---|
| 547 | 629 | } |
|---|
| 548 | 630 | |
|---|
| 631 | + if (chip->chip_id == MAX77663) |
|---|
| 632 | + goto out; |
|---|
| 633 | + |
|---|
| 549 | 634 | /* Disable WK_EN0 */ |
|---|
| 550 | 635 | ret = regmap_update_bits(chip->rmap, MAX77620_REG_ONOFFCNFG2, |
|---|
| 551 | 636 | MAX77620_ONOFFCNFG2_WK_EN0, 0); |
|---|
| .. | .. |
|---|
| 581 | 666 | * For MAX20024: No need to configure WKEN0 on resume as |
|---|
| 582 | 667 | * it is configured on Init. |
|---|
| 583 | 668 | */ |
|---|
| 584 | | - if (chip->chip_id == MAX20024) |
|---|
| 669 | + if (chip->chip_id == MAX20024 || chip->chip_id == MAX77663) |
|---|
| 585 | 670 | goto out; |
|---|
| 586 | 671 | |
|---|
| 587 | 672 | /* Enable WK_EN0 */ |
|---|
| .. | .. |
|---|
| 603 | 688 | static const struct i2c_device_id max77620_id[] = { |
|---|
| 604 | 689 | {"max77620", MAX77620}, |
|---|
| 605 | 690 | {"max20024", MAX20024}, |
|---|
| 691 | + {"max77663", MAX77663}, |
|---|
| 606 | 692 | {}, |
|---|
| 607 | 693 | }; |
|---|
| 608 | 694 | |
|---|