| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0+ |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * linux/arch/arm/plat-omap/dmtimer.c |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * OMAP Dual-Mode Timers |
|---|
| 5 | 6 | * |
|---|
| 6 | | - * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ |
|---|
| 7 | + * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ |
|---|
| 7 | 8 | * Tarun Kanti DebBarma <tarun.kanti@ti.com> |
|---|
| 8 | 9 | * Thara Gopinath <thara@ti.com> |
|---|
| 9 | 10 | * |
|---|
| .. | .. |
|---|
| 15 | 16 | * |
|---|
| 16 | 17 | * Copyright (C) 2009 Texas Instruments |
|---|
| 17 | 18 | * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com> |
|---|
| 18 | | - * |
|---|
| 19 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 20 | | - * under the terms of the GNU General Public License as published by the |
|---|
| 21 | | - * Free Software Foundation; either version 2 of the License, or (at your |
|---|
| 22 | | - * option) any later version. |
|---|
| 23 | | - * |
|---|
| 24 | | - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED |
|---|
| 25 | | - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
|---|
| 26 | | - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN |
|---|
| 27 | | - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
|---|
| 28 | | - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
|---|
| 29 | | - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|---|
| 30 | | - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
|---|
| 31 | | - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|---|
| 32 | | - * |
|---|
| 33 | | - * You should have received a copy of the GNU General Public License along |
|---|
| 34 | | - * with this program; if not, write to the Free Software Foundation, Inc., |
|---|
| 35 | | - * 675 Mass Ave, Cambridge, MA 02139, USA. |
|---|
| 36 | 19 | */ |
|---|
| 37 | 20 | |
|---|
| 38 | 21 | #include <linux/clk.h> |
|---|
| 39 | 22 | #include <linux/clk-provider.h> |
|---|
| 23 | +#include <linux/cpu_pm.h> |
|---|
| 40 | 24 | #include <linux/module.h> |
|---|
| 41 | 25 | #include <linux/io.h> |
|---|
| 42 | 26 | #include <linux/device.h> |
|---|
| .. | .. |
|---|
| 94 | 78 | |
|---|
| 95 | 79 | static void omap_timer_restore_context(struct omap_dm_timer *timer) |
|---|
| 96 | 80 | { |
|---|
| 81 | + __omap_dm_timer_write(timer, OMAP_TIMER_OCP_CFG_OFFSET, |
|---|
| 82 | + timer->context.ocp_cfg, 0); |
|---|
| 83 | + |
|---|
| 97 | 84 | omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG, |
|---|
| 98 | 85 | timer->context.twer); |
|---|
| 99 | 86 | omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, |
|---|
| .. | .. |
|---|
| 107 | 94 | writel_relaxed(timer->context.tier, timer->irq_ena); |
|---|
| 108 | 95 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, |
|---|
| 109 | 96 | timer->context.tclr); |
|---|
| 97 | +} |
|---|
| 98 | + |
|---|
| 99 | +static void omap_timer_save_context(struct omap_dm_timer *timer) |
|---|
| 100 | +{ |
|---|
| 101 | + timer->context.ocp_cfg = |
|---|
| 102 | + __omap_dm_timer_read(timer, OMAP_TIMER_OCP_CFG_OFFSET, 0); |
|---|
| 103 | + |
|---|
| 104 | + timer->context.tclr = |
|---|
| 105 | + omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); |
|---|
| 106 | + timer->context.twer = |
|---|
| 107 | + omap_dm_timer_read_reg(timer, OMAP_TIMER_WAKEUP_EN_REG); |
|---|
| 108 | + timer->context.tldr = |
|---|
| 109 | + omap_dm_timer_read_reg(timer, OMAP_TIMER_LOAD_REG); |
|---|
| 110 | + timer->context.tmar = |
|---|
| 111 | + omap_dm_timer_read_reg(timer, OMAP_TIMER_MATCH_REG); |
|---|
| 112 | + timer->context.tier = readl_relaxed(timer->irq_ena); |
|---|
| 113 | + timer->context.tsicr = |
|---|
| 114 | + omap_dm_timer_read_reg(timer, OMAP_TIMER_IF_CTRL_REG); |
|---|
| 115 | +} |
|---|
| 116 | + |
|---|
| 117 | +static int omap_timer_context_notifier(struct notifier_block *nb, |
|---|
| 118 | + unsigned long cmd, void *v) |
|---|
| 119 | +{ |
|---|
| 120 | + struct omap_dm_timer *timer; |
|---|
| 121 | + |
|---|
| 122 | + timer = container_of(nb, struct omap_dm_timer, nb); |
|---|
| 123 | + |
|---|
| 124 | + switch (cmd) { |
|---|
| 125 | + case CPU_CLUSTER_PM_ENTER: |
|---|
| 126 | + if ((timer->capability & OMAP_TIMER_ALWON) || |
|---|
| 127 | + !atomic_read(&timer->enabled)) |
|---|
| 128 | + break; |
|---|
| 129 | + omap_timer_save_context(timer); |
|---|
| 130 | + break; |
|---|
| 131 | + case CPU_CLUSTER_PM_ENTER_FAILED: |
|---|
| 132 | + case CPU_CLUSTER_PM_EXIT: |
|---|
| 133 | + if ((timer->capability & OMAP_TIMER_ALWON) || |
|---|
| 134 | + !atomic_read(&timer->enabled)) |
|---|
| 135 | + break; |
|---|
| 136 | + omap_timer_restore_context(timer); |
|---|
| 137 | + break; |
|---|
| 138 | + } |
|---|
| 139 | + |
|---|
| 140 | + return NOTIFY_OK; |
|---|
| 110 | 141 | } |
|---|
| 111 | 142 | |
|---|
| 112 | 143 | static int omap_dm_timer_reset(struct omap_dm_timer *timer) |
|---|
| .. | .. |
|---|
| 136 | 167 | timer->posted = 0; |
|---|
| 137 | 168 | |
|---|
| 138 | 169 | return 0; |
|---|
| 139 | | -} |
|---|
| 140 | | - |
|---|
| 141 | | -static int omap_dm_timer_of_set_source(struct omap_dm_timer *timer) |
|---|
| 142 | | -{ |
|---|
| 143 | | - int ret; |
|---|
| 144 | | - struct clk *parent; |
|---|
| 145 | | - |
|---|
| 146 | | - /* |
|---|
| 147 | | - * FIXME: OMAP1 devices do not use the clock framework for dmtimers so |
|---|
| 148 | | - * do not call clk_get() for these devices. |
|---|
| 149 | | - */ |
|---|
| 150 | | - if (!timer->fclk) |
|---|
| 151 | | - return -ENODEV; |
|---|
| 152 | | - |
|---|
| 153 | | - parent = clk_get(&timer->pdev->dev, NULL); |
|---|
| 154 | | - if (IS_ERR(parent)) |
|---|
| 155 | | - return -ENODEV; |
|---|
| 156 | | - |
|---|
| 157 | | - /* Bail out if both clocks point to fck */ |
|---|
| 158 | | - if (clk_is_match(parent, timer->fclk)) |
|---|
| 159 | | - return 0; |
|---|
| 160 | | - |
|---|
| 161 | | - ret = clk_set_parent(timer->fclk, parent); |
|---|
| 162 | | - if (ret < 0) |
|---|
| 163 | | - pr_err("%s: failed to set parent\n", __func__); |
|---|
| 164 | | - |
|---|
| 165 | | - clk_put(parent); |
|---|
| 166 | | - |
|---|
| 167 | | - return ret; |
|---|
| 168 | 170 | } |
|---|
| 169 | 171 | |
|---|
| 170 | 172 | static int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) |
|---|
| .. | .. |
|---|
| 225 | 227 | |
|---|
| 226 | 228 | static void omap_dm_timer_enable(struct omap_dm_timer *timer) |
|---|
| 227 | 229 | { |
|---|
| 228 | | - int c; |
|---|
| 229 | | - |
|---|
| 230 | 230 | pm_runtime_get_sync(&timer->pdev->dev); |
|---|
| 231 | | - |
|---|
| 232 | | - if (!(timer->capability & OMAP_TIMER_ALWON)) { |
|---|
| 233 | | - if (timer->get_context_loss_count) { |
|---|
| 234 | | - c = timer->get_context_loss_count(&timer->pdev->dev); |
|---|
| 235 | | - if (c != timer->ctx_loss_count) { |
|---|
| 236 | | - omap_timer_restore_context(timer); |
|---|
| 237 | | - timer->ctx_loss_count = c; |
|---|
| 238 | | - } |
|---|
| 239 | | - } else { |
|---|
| 240 | | - omap_timer_restore_context(timer); |
|---|
| 241 | | - } |
|---|
| 242 | | - } |
|---|
| 243 | 231 | } |
|---|
| 244 | 232 | |
|---|
| 245 | 233 | static void omap_dm_timer_disable(struct omap_dm_timer *timer) |
|---|
| .. | .. |
|---|
| 276 | 264 | __omap_dm_timer_enable_posted(timer); |
|---|
| 277 | 265 | omap_dm_timer_disable(timer); |
|---|
| 278 | 266 | |
|---|
| 279 | | - rc = omap_dm_timer_of_set_source(timer); |
|---|
| 280 | | - if (rc == -ENODEV) |
|---|
| 281 | | - return omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ); |
|---|
| 282 | | - |
|---|
| 283 | | - return rc; |
|---|
| 267 | + return 0; |
|---|
| 284 | 268 | } |
|---|
| 285 | 269 | |
|---|
| 286 | 270 | static inline u32 omap_dm_timer_reserved_systimer(int id) |
|---|
| .. | .. |
|---|
| 508 | 492 | |
|---|
| 509 | 493 | int omap_dm_timer_trigger(struct omap_dm_timer *timer) |
|---|
| 510 | 494 | { |
|---|
| 511 | | - if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) { |
|---|
| 495 | + if (unlikely(!timer || !atomic_read(&timer->enabled))) { |
|---|
| 512 | 496 | pr_err("%s: timer not available or enabled.\n", __func__); |
|---|
| 513 | 497 | return -EINVAL; |
|---|
| 514 | 498 | } |
|---|
| .. | .. |
|---|
| 532 | 516 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); |
|---|
| 533 | 517 | } |
|---|
| 534 | 518 | |
|---|
| 535 | | - /* Save the context */ |
|---|
| 536 | | - timer->context.tclr = l; |
|---|
| 537 | 519 | return 0; |
|---|
| 538 | 520 | } |
|---|
| 539 | 521 | |
|---|
| .. | .. |
|---|
| 549 | 531 | |
|---|
| 550 | 532 | __omap_dm_timer_stop(timer, timer->posted, rate); |
|---|
| 551 | 533 | |
|---|
| 552 | | - /* |
|---|
| 553 | | - * Since the register values are computed and written within |
|---|
| 554 | | - * __omap_dm_timer_stop, we need to use read to retrieve the |
|---|
| 555 | | - * context. |
|---|
| 556 | | - */ |
|---|
| 557 | | - timer->context.tclr = |
|---|
| 558 | | - omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); |
|---|
| 559 | 534 | omap_dm_timer_disable(timer); |
|---|
| 560 | 535 | return 0; |
|---|
| 561 | 536 | } |
|---|
| 562 | 537 | |
|---|
| 563 | | -static int omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload, |
|---|
| 538 | +static int omap_dm_timer_set_load(struct omap_dm_timer *timer, |
|---|
| 564 | 539 | unsigned int load) |
|---|
| 565 | 540 | { |
|---|
| 566 | | - u32 l; |
|---|
| 567 | | - |
|---|
| 568 | 541 | if (unlikely(!timer)) |
|---|
| 569 | 542 | return -EINVAL; |
|---|
| 570 | 543 | |
|---|
| 571 | 544 | omap_dm_timer_enable(timer); |
|---|
| 572 | | - l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); |
|---|
| 573 | | - if (autoreload) |
|---|
| 574 | | - l |= OMAP_TIMER_CTRL_AR; |
|---|
| 575 | | - else |
|---|
| 576 | | - l &= ~OMAP_TIMER_CTRL_AR; |
|---|
| 577 | | - omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); |
|---|
| 578 | 545 | omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load); |
|---|
| 579 | 546 | |
|---|
| 580 | | - omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0); |
|---|
| 581 | | - /* Save the context */ |
|---|
| 582 | | - timer->context.tclr = l; |
|---|
| 583 | | - timer->context.tldr = load; |
|---|
| 584 | 547 | omap_dm_timer_disable(timer); |
|---|
| 585 | 548 | return 0; |
|---|
| 586 | 549 | } |
|---|
| 587 | 550 | |
|---|
| 588 | | -/* Optimized set_load which removes costly spin wait in timer_start */ |
|---|
| 589 | | -int omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload, |
|---|
| 590 | | - unsigned int load) |
|---|
| 591 | | -{ |
|---|
| 592 | | - u32 l; |
|---|
| 593 | | - |
|---|
| 594 | | - if (unlikely(!timer)) |
|---|
| 595 | | - return -EINVAL; |
|---|
| 596 | | - |
|---|
| 597 | | - omap_dm_timer_enable(timer); |
|---|
| 598 | | - |
|---|
| 599 | | - l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); |
|---|
| 600 | | - if (autoreload) { |
|---|
| 601 | | - l |= OMAP_TIMER_CTRL_AR; |
|---|
| 602 | | - omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load); |
|---|
| 603 | | - } else { |
|---|
| 604 | | - l &= ~OMAP_TIMER_CTRL_AR; |
|---|
| 605 | | - } |
|---|
| 606 | | - l |= OMAP_TIMER_CTRL_ST; |
|---|
| 607 | | - |
|---|
| 608 | | - __omap_dm_timer_load_start(timer, l, load, timer->posted); |
|---|
| 609 | | - |
|---|
| 610 | | - /* Save the context */ |
|---|
| 611 | | - timer->context.tclr = l; |
|---|
| 612 | | - timer->context.tldr = load; |
|---|
| 613 | | - timer->context.tcrr = load; |
|---|
| 614 | | - return 0; |
|---|
| 615 | | -} |
|---|
| 616 | 551 | static int omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable, |
|---|
| 617 | 552 | unsigned int match) |
|---|
| 618 | 553 | { |
|---|
| .. | .. |
|---|
| 630 | 565 | omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match); |
|---|
| 631 | 566 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); |
|---|
| 632 | 567 | |
|---|
| 633 | | - /* Save the context */ |
|---|
| 634 | | - timer->context.tclr = l; |
|---|
| 635 | | - timer->context.tmar = match; |
|---|
| 636 | 568 | omap_dm_timer_disable(timer); |
|---|
| 637 | 569 | return 0; |
|---|
| 638 | 570 | } |
|---|
| 639 | 571 | |
|---|
| 640 | 572 | static int omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on, |
|---|
| 641 | | - int toggle, int trigger) |
|---|
| 573 | + int toggle, int trigger, int autoreload) |
|---|
| 642 | 574 | { |
|---|
| 643 | 575 | u32 l; |
|---|
| 644 | 576 | |
|---|
| .. | .. |
|---|
| 648 | 580 | omap_dm_timer_enable(timer); |
|---|
| 649 | 581 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); |
|---|
| 650 | 582 | l &= ~(OMAP_TIMER_CTRL_GPOCFG | OMAP_TIMER_CTRL_SCPWM | |
|---|
| 651 | | - OMAP_TIMER_CTRL_PT | (0x03 << 10)); |
|---|
| 583 | + OMAP_TIMER_CTRL_PT | (0x03 << 10) | OMAP_TIMER_CTRL_AR); |
|---|
| 652 | 584 | if (def_on) |
|---|
| 653 | 585 | l |= OMAP_TIMER_CTRL_SCPWM; |
|---|
| 654 | 586 | if (toggle) |
|---|
| 655 | 587 | l |= OMAP_TIMER_CTRL_PT; |
|---|
| 656 | 588 | l |= trigger << 10; |
|---|
| 589 | + if (autoreload) |
|---|
| 590 | + l |= OMAP_TIMER_CTRL_AR; |
|---|
| 657 | 591 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); |
|---|
| 658 | 592 | |
|---|
| 659 | | - /* Save the context */ |
|---|
| 660 | | - timer->context.tclr = l; |
|---|
| 661 | 593 | omap_dm_timer_disable(timer); |
|---|
| 662 | 594 | return 0; |
|---|
| 595 | +} |
|---|
| 596 | + |
|---|
| 597 | +static int omap_dm_timer_get_pwm_status(struct omap_dm_timer *timer) |
|---|
| 598 | +{ |
|---|
| 599 | + u32 l; |
|---|
| 600 | + |
|---|
| 601 | + if (unlikely(!timer)) |
|---|
| 602 | + return -EINVAL; |
|---|
| 603 | + |
|---|
| 604 | + omap_dm_timer_enable(timer); |
|---|
| 605 | + l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); |
|---|
| 606 | + omap_dm_timer_disable(timer); |
|---|
| 607 | + |
|---|
| 608 | + return l; |
|---|
| 663 | 609 | } |
|---|
| 664 | 610 | |
|---|
| 665 | 611 | static int omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, |
|---|
| .. | .. |
|---|
| 679 | 625 | } |
|---|
| 680 | 626 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); |
|---|
| 681 | 627 | |
|---|
| 682 | | - /* Save the context */ |
|---|
| 683 | | - timer->context.tclr = l; |
|---|
| 684 | 628 | omap_dm_timer_disable(timer); |
|---|
| 685 | 629 | return 0; |
|---|
| 686 | 630 | } |
|---|
| .. | .. |
|---|
| 694 | 638 | omap_dm_timer_enable(timer); |
|---|
| 695 | 639 | __omap_dm_timer_int_enable(timer, value); |
|---|
| 696 | 640 | |
|---|
| 697 | | - /* Save the context */ |
|---|
| 698 | | - timer->context.tier = value; |
|---|
| 699 | | - timer->context.twer = value; |
|---|
| 700 | 641 | omap_dm_timer_disable(timer); |
|---|
| 701 | 642 | return 0; |
|---|
| 702 | 643 | } |
|---|
| .. | .. |
|---|
| 724 | 665 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_WAKEUP_EN_REG) & ~mask; |
|---|
| 725 | 666 | omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG, l); |
|---|
| 726 | 667 | |
|---|
| 727 | | - /* Save the context */ |
|---|
| 728 | | - timer->context.tier &= ~mask; |
|---|
| 729 | | - timer->context.twer &= ~mask; |
|---|
| 730 | 668 | omap_dm_timer_disable(timer); |
|---|
| 731 | 669 | return 0; |
|---|
| 732 | 670 | } |
|---|
| .. | .. |
|---|
| 735 | 673 | { |
|---|
| 736 | 674 | unsigned int l; |
|---|
| 737 | 675 | |
|---|
| 738 | | - if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) { |
|---|
| 676 | + if (unlikely(!timer || !atomic_read(&timer->enabled))) { |
|---|
| 739 | 677 | pr_err("%s: timer not available or enabled.\n", __func__); |
|---|
| 740 | 678 | return 0; |
|---|
| 741 | 679 | } |
|---|
| .. | .. |
|---|
| 747 | 685 | |
|---|
| 748 | 686 | static int omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value) |
|---|
| 749 | 687 | { |
|---|
| 750 | | - if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) |
|---|
| 688 | + if (unlikely(!timer || !atomic_read(&timer->enabled))) |
|---|
| 751 | 689 | return -EINVAL; |
|---|
| 752 | 690 | |
|---|
| 753 | 691 | __omap_dm_timer_write_status(timer, value); |
|---|
| .. | .. |
|---|
| 757 | 695 | |
|---|
| 758 | 696 | static unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer) |
|---|
| 759 | 697 | { |
|---|
| 760 | | - if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) { |
|---|
| 698 | + if (unlikely(!timer || !atomic_read(&timer->enabled))) { |
|---|
| 761 | 699 | pr_err("%s: timer not iavailable or enabled.\n", __func__); |
|---|
| 762 | 700 | return 0; |
|---|
| 763 | 701 | } |
|---|
| .. | .. |
|---|
| 767 | 705 | |
|---|
| 768 | 706 | static int omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value) |
|---|
| 769 | 707 | { |
|---|
| 770 | | - if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) { |
|---|
| 708 | + if (unlikely(!timer || !atomic_read(&timer->enabled))) { |
|---|
| 771 | 709 | pr_err("%s: timer not available or enabled.\n", __func__); |
|---|
| 772 | 710 | return -EINVAL; |
|---|
| 773 | 711 | } |
|---|
| .. | .. |
|---|
| 795 | 733 | return 0; |
|---|
| 796 | 734 | } |
|---|
| 797 | 735 | |
|---|
| 736 | +static int __maybe_unused omap_dm_timer_runtime_suspend(struct device *dev) |
|---|
| 737 | +{ |
|---|
| 738 | + struct omap_dm_timer *timer = dev_get_drvdata(dev); |
|---|
| 739 | + |
|---|
| 740 | + atomic_set(&timer->enabled, 0); |
|---|
| 741 | + |
|---|
| 742 | + if (timer->capability & OMAP_TIMER_ALWON || !timer->func_base) |
|---|
| 743 | + return 0; |
|---|
| 744 | + |
|---|
| 745 | + omap_timer_save_context(timer); |
|---|
| 746 | + |
|---|
| 747 | + return 0; |
|---|
| 748 | +} |
|---|
| 749 | + |
|---|
| 750 | +static int __maybe_unused omap_dm_timer_runtime_resume(struct device *dev) |
|---|
| 751 | +{ |
|---|
| 752 | + struct omap_dm_timer *timer = dev_get_drvdata(dev); |
|---|
| 753 | + |
|---|
| 754 | + if (!(timer->capability & OMAP_TIMER_ALWON) && timer->func_base) |
|---|
| 755 | + omap_timer_restore_context(timer); |
|---|
| 756 | + |
|---|
| 757 | + atomic_set(&timer->enabled, 1); |
|---|
| 758 | + |
|---|
| 759 | + return 0; |
|---|
| 760 | +} |
|---|
| 761 | + |
|---|
| 762 | +static const struct dev_pm_ops omap_dm_timer_pm_ops = { |
|---|
| 763 | + SET_RUNTIME_PM_OPS(omap_dm_timer_runtime_suspend, |
|---|
| 764 | + omap_dm_timer_runtime_resume, NULL) |
|---|
| 765 | +}; |
|---|
| 766 | + |
|---|
| 798 | 767 | static const struct of_device_id omap_timer_match[]; |
|---|
| 799 | 768 | |
|---|
| 800 | 769 | /** |
|---|
| .. | .. |
|---|
| 808 | 777 | { |
|---|
| 809 | 778 | unsigned long flags; |
|---|
| 810 | 779 | struct omap_dm_timer *timer; |
|---|
| 811 | | - struct resource *mem, *irq; |
|---|
| 812 | 780 | struct device *dev = &pdev->dev; |
|---|
| 813 | 781 | const struct dmtimer_platform_data *pdata; |
|---|
| 814 | 782 | int ret; |
|---|
| .. | .. |
|---|
| 824 | 792 | return -ENODEV; |
|---|
| 825 | 793 | } |
|---|
| 826 | 794 | |
|---|
| 827 | | - irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); |
|---|
| 828 | | - if (unlikely(!irq)) { |
|---|
| 829 | | - dev_err(dev, "%s: no IRQ resource.\n", __func__); |
|---|
| 830 | | - return -ENODEV; |
|---|
| 831 | | - } |
|---|
| 832 | | - |
|---|
| 833 | | - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
|---|
| 834 | | - if (unlikely(!mem)) { |
|---|
| 835 | | - dev_err(dev, "%s: no memory resource.\n", __func__); |
|---|
| 836 | | - return -ENODEV; |
|---|
| 837 | | - } |
|---|
| 838 | | - |
|---|
| 839 | 795 | timer = devm_kzalloc(dev, sizeof(*timer), GFP_KERNEL); |
|---|
| 840 | 796 | if (!timer) |
|---|
| 841 | 797 | return -ENOMEM; |
|---|
| 842 | 798 | |
|---|
| 799 | + timer->irq = platform_get_irq(pdev, 0); |
|---|
| 800 | + if (timer->irq < 0) |
|---|
| 801 | + return timer->irq; |
|---|
| 802 | + |
|---|
| 843 | 803 | timer->fclk = ERR_PTR(-ENODEV); |
|---|
| 844 | | - timer->io_base = devm_ioremap_resource(dev, mem); |
|---|
| 804 | + timer->io_base = devm_platform_ioremap_resource(pdev, 0); |
|---|
| 845 | 805 | if (IS_ERR(timer->io_base)) |
|---|
| 846 | 806 | return PTR_ERR(timer->io_base); |
|---|
| 807 | + |
|---|
| 808 | + platform_set_drvdata(pdev, timer); |
|---|
| 847 | 809 | |
|---|
| 848 | 810 | if (dev->of_node) { |
|---|
| 849 | 811 | if (of_find_property(dev->of_node, "ti,timer-alwon", NULL)) |
|---|
| .. | .. |
|---|
| 858 | 820 | timer->id = pdev->id; |
|---|
| 859 | 821 | timer->capability = pdata->timer_capability; |
|---|
| 860 | 822 | timer->reserved = omap_dm_timer_reserved_systimer(timer->id); |
|---|
| 861 | | - timer->get_context_loss_count = pdata->get_context_loss_count; |
|---|
| 823 | + } |
|---|
| 824 | + |
|---|
| 825 | + if (!(timer->capability & OMAP_TIMER_ALWON)) { |
|---|
| 826 | + timer->nb.notifier_call = omap_timer_context_notifier; |
|---|
| 827 | + cpu_pm_register_notifier(&timer->nb); |
|---|
| 862 | 828 | } |
|---|
| 863 | 829 | |
|---|
| 864 | 830 | if (pdata) |
|---|
| 865 | 831 | timer->errata = pdata->timer_errata; |
|---|
| 866 | 832 | |
|---|
| 867 | | - timer->irq = irq->start; |
|---|
| 868 | 833 | timer->pdev = pdev; |
|---|
| 869 | 834 | |
|---|
| 870 | 835 | pm_runtime_enable(dev); |
|---|
| .. | .. |
|---|
| 913 | 878 | list_for_each_entry(timer, &omap_timer_list, node) |
|---|
| 914 | 879 | if (!strcmp(dev_name(&timer->pdev->dev), |
|---|
| 915 | 880 | dev_name(&pdev->dev))) { |
|---|
| 881 | + if (!(timer->capability & OMAP_TIMER_ALWON)) |
|---|
| 882 | + cpu_pm_unregister_notifier(&timer->nb); |
|---|
| 916 | 883 | list_del(&timer->node); |
|---|
| 917 | 884 | ret = 0; |
|---|
| 918 | 885 | break; |
|---|
| .. | .. |
|---|
| 924 | 891 | return ret; |
|---|
| 925 | 892 | } |
|---|
| 926 | 893 | |
|---|
| 927 | | -const static struct omap_dm_timer_ops dmtimer_ops = { |
|---|
| 894 | +static const struct omap_dm_timer_ops dmtimer_ops = { |
|---|
| 928 | 895 | .request_by_node = omap_dm_timer_request_by_node, |
|---|
| 929 | 896 | .request_specific = omap_dm_timer_request_specific, |
|---|
| 930 | 897 | .request = omap_dm_timer_request, |
|---|
| .. | .. |
|---|
| 941 | 908 | .set_load = omap_dm_timer_set_load, |
|---|
| 942 | 909 | .set_match = omap_dm_timer_set_match, |
|---|
| 943 | 910 | .set_pwm = omap_dm_timer_set_pwm, |
|---|
| 911 | + .get_pwm_status = omap_dm_timer_get_pwm_status, |
|---|
| 944 | 912 | .set_prescaler = omap_dm_timer_set_prescaler, |
|---|
| 945 | 913 | .read_counter = omap_dm_timer_read_counter, |
|---|
| 946 | 914 | .write_counter = omap_dm_timer_write_counter, |
|---|
| .. | .. |
|---|
| 991 | 959 | .driver = { |
|---|
| 992 | 960 | .name = "omap_timer", |
|---|
| 993 | 961 | .of_match_table = of_match_ptr(omap_timer_match), |
|---|
| 962 | + .pm = &omap_dm_timer_pm_ops, |
|---|
| 994 | 963 | }, |
|---|
| 995 | 964 | }; |
|---|
| 996 | 965 | |
|---|
| 997 | | -early_platform_init("earlytimer", &omap_dm_timer_driver); |
|---|
| 998 | 966 | module_platform_driver(omap_dm_timer_driver); |
|---|
| 999 | 967 | |
|---|
| 1000 | 968 | MODULE_DESCRIPTION("OMAP Dual-Mode Timer Driver"); |
|---|
| 1001 | 969 | MODULE_LICENSE("GPL"); |
|---|
| 1002 | | -MODULE_ALIAS("platform:" DRIVER_NAME); |
|---|
| 1003 | 970 | MODULE_AUTHOR("Texas Instruments Inc"); |
|---|