.. | .. |
---|
| 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 | |
---|