diff options
Diffstat (limited to 'arch/arm/mach-omap2/omap_hwmod.c')
| -rw-r--r-- | arch/arm/mach-omap2/omap_hwmod.c | 336 |
1 files changed, 294 insertions, 42 deletions
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index 37afbd173c2c..6af64bbd9e1d 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c | |||
| @@ -139,18 +139,20 @@ | |||
| 139 | #include <linux/slab.h> | 139 | #include <linux/slab.h> |
| 140 | #include <linux/bootmem.h> | 140 | #include <linux/bootmem.h> |
| 141 | 141 | ||
| 142 | #include "common.h" | ||
| 143 | #include <plat/cpu.h> | ||
| 144 | #include "clockdomain.h" | ||
| 145 | #include "powerdomain.h" | ||
| 146 | #include <plat/clock.h> | 142 | #include <plat/clock.h> |
| 147 | #include <plat/omap_hwmod.h> | 143 | #include <plat/omap_hwmod.h> |
| 148 | #include <plat/prcm.h> | 144 | #include <plat/prcm.h> |
| 149 | 145 | ||
| 146 | #include "soc.h" | ||
| 147 | #include "common.h" | ||
| 148 | #include "clockdomain.h" | ||
| 149 | #include "powerdomain.h" | ||
| 150 | #include "cm2xxx_3xxx.h" | 150 | #include "cm2xxx_3xxx.h" |
| 151 | #include "cminst44xx.h" | 151 | #include "cminst44xx.h" |
| 152 | #include "cm33xx.h" | ||
| 152 | #include "prm2xxx_3xxx.h" | 153 | #include "prm2xxx_3xxx.h" |
| 153 | #include "prm44xx.h" | 154 | #include "prm44xx.h" |
| 155 | #include "prm33xx.h" | ||
| 154 | #include "prminst44xx.h" | 156 | #include "prminst44xx.h" |
| 155 | #include "mux.h" | 157 | #include "mux.h" |
| 156 | #include "pm.h" | 158 | #include "pm.h" |
| @@ -677,16 +679,25 @@ static int _init_main_clk(struct omap_hwmod *oh) | |||
| 677 | if (!oh->main_clk) | 679 | if (!oh->main_clk) |
| 678 | return 0; | 680 | return 0; |
| 679 | 681 | ||
| 680 | oh->_clk = omap_clk_get_by_name(oh->main_clk); | 682 | oh->_clk = clk_get(NULL, oh->main_clk); |
| 681 | if (!oh->_clk) { | 683 | if (IS_ERR(oh->_clk)) { |
| 682 | pr_warning("omap_hwmod: %s: cannot clk_get main_clk %s\n", | 684 | pr_warning("omap_hwmod: %s: cannot clk_get main_clk %s\n", |
| 683 | oh->name, oh->main_clk); | 685 | oh->name, oh->main_clk); |
| 684 | return -EINVAL; | 686 | return -EINVAL; |
| 685 | } | 687 | } |
| 688 | /* | ||
| 689 | * HACK: This needs a re-visit once clk_prepare() is implemented | ||
| 690 | * to do something meaningful. Today its just a no-op. | ||
| 691 | * If clk_prepare() is used at some point to do things like | ||
| 692 | * voltage scaling etc, then this would have to be moved to | ||
| 693 | * some point where subsystems like i2c and pmic become | ||
| 694 | * available. | ||
| 695 | */ | ||
| 696 | clk_prepare(oh->_clk); | ||
| 686 | 697 | ||
| 687 | if (!oh->_clk->clkdm) | 698 | if (!oh->_clk->clkdm) |
| 688 | pr_warning("omap_hwmod: %s: missing clockdomain for %s.\n", | 699 | pr_debug("omap_hwmod: %s: missing clockdomain for %s.\n", |
| 689 | oh->main_clk, oh->_clk->name); | 700 | oh->name, oh->main_clk); |
| 690 | 701 | ||
| 691 | return ret; | 702 | return ret; |
| 692 | } | 703 | } |
| @@ -713,13 +724,22 @@ static int _init_interface_clks(struct omap_hwmod *oh) | |||
| 713 | if (!os->clk) | 724 | if (!os->clk) |
| 714 | continue; | 725 | continue; |
| 715 | 726 | ||
| 716 | c = omap_clk_get_by_name(os->clk); | 727 | c = clk_get(NULL, os->clk); |
| 717 | if (!c) { | 728 | if (IS_ERR(c)) { |
| 718 | pr_warning("omap_hwmod: %s: cannot clk_get interface_clk %s\n", | 729 | pr_warning("omap_hwmod: %s: cannot clk_get interface_clk %s\n", |
| 719 | oh->name, os->clk); | 730 | oh->name, os->clk); |
| 720 | ret = -EINVAL; | 731 | ret = -EINVAL; |
| 721 | } | 732 | } |
| 722 | os->_clk = c; | 733 | os->_clk = c; |
| 734 | /* | ||
| 735 | * HACK: This needs a re-visit once clk_prepare() is implemented | ||
| 736 | * to do something meaningful. Today its just a no-op. | ||
| 737 | * If clk_prepare() is used at some point to do things like | ||
| 738 | * voltage scaling etc, then this would have to be moved to | ||
| 739 | * some point where subsystems like i2c and pmic become | ||
| 740 | * available. | ||
| 741 | */ | ||
| 742 | clk_prepare(os->_clk); | ||
| 723 | } | 743 | } |
| 724 | 744 | ||
| 725 | return ret; | 745 | return ret; |
| @@ -740,13 +760,22 @@ static int _init_opt_clks(struct omap_hwmod *oh) | |||
| 740 | int ret = 0; | 760 | int ret = 0; |
| 741 | 761 | ||
| 742 | for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++) { | 762 | for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++) { |
| 743 | c = omap_clk_get_by_name(oc->clk); | 763 | c = clk_get(NULL, oc->clk); |
| 744 | if (!c) { | 764 | if (IS_ERR(c)) { |
| 745 | pr_warning("omap_hwmod: %s: cannot clk_get opt_clk %s\n", | 765 | pr_warning("omap_hwmod: %s: cannot clk_get opt_clk %s\n", |
| 746 | oh->name, oc->clk); | 766 | oh->name, oc->clk); |
| 747 | ret = -EINVAL; | 767 | ret = -EINVAL; |
| 748 | } | 768 | } |
| 749 | oc->_clk = c; | 769 | oc->_clk = c; |
| 770 | /* | ||
| 771 | * HACK: This needs a re-visit once clk_prepare() is implemented | ||
| 772 | * to do something meaningful. Today its just a no-op. | ||
| 773 | * If clk_prepare() is used at some point to do things like | ||
| 774 | * voltage scaling etc, then this would have to be moved to | ||
| 775 | * some point where subsystems like i2c and pmic become | ||
| 776 | * available. | ||
| 777 | */ | ||
| 778 | clk_prepare(oc->_clk); | ||
| 750 | } | 779 | } |
| 751 | 780 | ||
| 752 | return ret; | 781 | return ret; |
| @@ -825,7 +854,7 @@ static void _enable_optional_clocks(struct omap_hwmod *oh) | |||
| 825 | for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++) | 854 | for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++) |
| 826 | if (oc->_clk) { | 855 | if (oc->_clk) { |
| 827 | pr_debug("omap_hwmod: enable %s:%s\n", oc->role, | 856 | pr_debug("omap_hwmod: enable %s:%s\n", oc->role, |
| 828 | oc->_clk->name); | 857 | __clk_get_name(oc->_clk)); |
| 829 | clk_enable(oc->_clk); | 858 | clk_enable(oc->_clk); |
| 830 | } | 859 | } |
| 831 | } | 860 | } |
| @@ -840,7 +869,7 @@ static void _disable_optional_clocks(struct omap_hwmod *oh) | |||
| 840 | for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++) | 869 | for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++) |
| 841 | if (oc->_clk) { | 870 | if (oc->_clk) { |
| 842 | pr_debug("omap_hwmod: disable %s:%s\n", oc->role, | 871 | pr_debug("omap_hwmod: disable %s:%s\n", oc->role, |
| 843 | oc->_clk->name); | 872 | __clk_get_name(oc->_clk)); |
| 844 | clk_disable(oc->_clk); | 873 | clk_disable(oc->_clk); |
| 845 | } | 874 | } |
| 846 | } | 875 | } |
| @@ -868,6 +897,26 @@ static void _omap4_enable_module(struct omap_hwmod *oh) | |||
| 868 | } | 897 | } |
| 869 | 898 | ||
| 870 | /** | 899 | /** |
| 900 | * _am33xx_enable_module - enable CLKCTRL modulemode on AM33XX | ||
| 901 | * @oh: struct omap_hwmod * | ||
| 902 | * | ||
| 903 | * Enables the PRCM module mode related to the hwmod @oh. | ||
| 904 | * No return value. | ||
| 905 | */ | ||
| 906 | static void _am33xx_enable_module(struct omap_hwmod *oh) | ||
| 907 | { | ||
| 908 | if (!oh->clkdm || !oh->prcm.omap4.modulemode) | ||
| 909 | return; | ||
| 910 | |||
| 911 | pr_debug("omap_hwmod: %s: %s: %d\n", | ||
| 912 | oh->name, __func__, oh->prcm.omap4.modulemode); | ||
| 913 | |||
| 914 | am33xx_cm_module_enable(oh->prcm.omap4.modulemode, oh->clkdm->cm_inst, | ||
| 915 | oh->clkdm->clkdm_offs, | ||
| 916 | oh->prcm.omap4.clkctrl_offs); | ||
| 917 | } | ||
| 918 | |||
| 919 | /** | ||
| 871 | * _omap4_wait_target_disable - wait for a module to be disabled on OMAP4 | 920 | * _omap4_wait_target_disable - wait for a module to be disabled on OMAP4 |
| 872 | * @oh: struct omap_hwmod * | 921 | * @oh: struct omap_hwmod * |
| 873 | * | 922 | * |
| @@ -878,10 +927,10 @@ static void _omap4_enable_module(struct omap_hwmod *oh) | |||
| 878 | */ | 927 | */ |
| 879 | static int _omap4_wait_target_disable(struct omap_hwmod *oh) | 928 | static int _omap4_wait_target_disable(struct omap_hwmod *oh) |
| 880 | { | 929 | { |
| 881 | if (!oh || !oh->clkdm) | 930 | if (!oh) |
| 882 | return -EINVAL; | 931 | return -EINVAL; |
| 883 | 932 | ||
| 884 | if (oh->_int_flags & _HWMOD_NO_MPU_PORT) | 933 | if (oh->_int_flags & _HWMOD_NO_MPU_PORT || !oh->clkdm) |
| 885 | return 0; | 934 | return 0; |
| 886 | 935 | ||
| 887 | if (oh->flags & HWMOD_NO_IDLEST) | 936 | if (oh->flags & HWMOD_NO_IDLEST) |
| @@ -894,6 +943,31 @@ static int _omap4_wait_target_disable(struct omap_hwmod *oh) | |||
| 894 | } | 943 | } |
| 895 | 944 | ||
| 896 | /** | 945 | /** |
| 946 | * _am33xx_wait_target_disable - wait for a module to be disabled on AM33XX | ||
| 947 | * @oh: struct omap_hwmod * | ||
| 948 | * | ||
| 949 | * Wait for a module @oh to enter slave idle. Returns 0 if the module | ||
| 950 | * does not have an IDLEST bit or if the module successfully enters | ||
| 951 | * slave idle; otherwise, pass along the return value of the | ||
| 952 | * appropriate *_cm*_wait_module_idle() function. | ||
| 953 | */ | ||
| 954 | static int _am33xx_wait_target_disable(struct omap_hwmod *oh) | ||
| 955 | { | ||
| 956 | if (!oh) | ||
| 957 | return -EINVAL; | ||
| 958 | |||
| 959 | if (oh->_int_flags & _HWMOD_NO_MPU_PORT) | ||
| 960 | return 0; | ||
| 961 | |||
| 962 | if (oh->flags & HWMOD_NO_IDLEST) | ||
| 963 | return 0; | ||
| 964 | |||
| 965 | return am33xx_cm_wait_module_idle(oh->clkdm->cm_inst, | ||
| 966 | oh->clkdm->clkdm_offs, | ||
| 967 | oh->prcm.omap4.clkctrl_offs); | ||
| 968 | } | ||
| 969 | |||
| 970 | /** | ||
| 897 | * _count_mpu_irqs - count the number of MPU IRQ lines associated with @oh | 971 | * _count_mpu_irqs - count the number of MPU IRQ lines associated with @oh |
| 898 | * @oh: struct omap_hwmod *oh | 972 | * @oh: struct omap_hwmod *oh |
| 899 | * | 973 | * |
| @@ -1380,8 +1454,10 @@ static struct omap_hwmod *_lookup(const char *name) | |||
| 1380 | */ | 1454 | */ |
| 1381 | static int _init_clkdm(struct omap_hwmod *oh) | 1455 | static int _init_clkdm(struct omap_hwmod *oh) |
| 1382 | { | 1456 | { |
| 1383 | if (!oh->clkdm_name) | 1457 | if (!oh->clkdm_name) { |
| 1458 | pr_debug("omap_hwmod: %s: missing clockdomain\n", oh->name); | ||
| 1384 | return 0; | 1459 | return 0; |
| 1460 | } | ||
| 1385 | 1461 | ||
| 1386 | oh->clkdm = clkdm_lookup(oh->clkdm_name); | 1462 | oh->clkdm = clkdm_lookup(oh->clkdm_name); |
| 1387 | if (!oh->clkdm) { | 1463 | if (!oh->clkdm) { |
| @@ -1438,8 +1514,8 @@ static int _init_clocks(struct omap_hwmod *oh, void *data) | |||
| 1438 | * Return the bit position of the reset line that match the | 1514 | * Return the bit position of the reset line that match the |
| 1439 | * input name. Return -ENOENT if not found. | 1515 | * input name. Return -ENOENT if not found. |
| 1440 | */ | 1516 | */ |
| 1441 | static u8 _lookup_hardreset(struct omap_hwmod *oh, const char *name, | 1517 | static int _lookup_hardreset(struct omap_hwmod *oh, const char *name, |
| 1442 | struct omap_hwmod_rst_info *ohri) | 1518 | struct omap_hwmod_rst_info *ohri) |
| 1443 | { | 1519 | { |
| 1444 | int i; | 1520 | int i; |
| 1445 | 1521 | ||
| @@ -1475,7 +1551,7 @@ static u8 _lookup_hardreset(struct omap_hwmod *oh, const char *name, | |||
| 1475 | static int _assert_hardreset(struct omap_hwmod *oh, const char *name) | 1551 | static int _assert_hardreset(struct omap_hwmod *oh, const char *name) |
| 1476 | { | 1552 | { |
| 1477 | struct omap_hwmod_rst_info ohri; | 1553 | struct omap_hwmod_rst_info ohri; |
| 1478 | u8 ret = -EINVAL; | 1554 | int ret = -EINVAL; |
| 1479 | 1555 | ||
| 1480 | if (!oh) | 1556 | if (!oh) |
| 1481 | return -EINVAL; | 1557 | return -EINVAL; |
| @@ -1484,7 +1560,7 @@ static int _assert_hardreset(struct omap_hwmod *oh, const char *name) | |||
| 1484 | return -ENOSYS; | 1560 | return -ENOSYS; |
| 1485 | 1561 | ||
| 1486 | ret = _lookup_hardreset(oh, name, &ohri); | 1562 | ret = _lookup_hardreset(oh, name, &ohri); |
| 1487 | if (IS_ERR_VALUE(ret)) | 1563 | if (ret < 0) |
| 1488 | return ret; | 1564 | return ret; |
| 1489 | 1565 | ||
| 1490 | ret = soc_ops.assert_hardreset(oh, &ohri); | 1566 | ret = soc_ops.assert_hardreset(oh, &ohri); |
| @@ -1509,6 +1585,7 @@ static int _deassert_hardreset(struct omap_hwmod *oh, const char *name) | |||
| 1509 | { | 1585 | { |
| 1510 | struct omap_hwmod_rst_info ohri; | 1586 | struct omap_hwmod_rst_info ohri; |
| 1511 | int ret = -EINVAL; | 1587 | int ret = -EINVAL; |
| 1588 | int hwsup = 0; | ||
| 1512 | 1589 | ||
| 1513 | if (!oh) | 1590 | if (!oh) |
| 1514 | return -EINVAL; | 1591 | return -EINVAL; |
| @@ -1520,10 +1597,46 @@ static int _deassert_hardreset(struct omap_hwmod *oh, const char *name) | |||
| 1520 | if (IS_ERR_VALUE(ret)) | 1597 | if (IS_ERR_VALUE(ret)) |
| 1521 | return ret; | 1598 | return ret; |
| 1522 | 1599 | ||
| 1600 | if (oh->clkdm) { | ||
| 1601 | /* | ||
| 1602 | * A clockdomain must be in SW_SUP otherwise reset | ||
| 1603 | * might not be completed. The clockdomain can be set | ||
| 1604 | * in HW_AUTO only when the module become ready. | ||
| 1605 | */ | ||
| 1606 | hwsup = clkdm_in_hwsup(oh->clkdm); | ||
| 1607 | ret = clkdm_hwmod_enable(oh->clkdm, oh); | ||
| 1608 | if (ret) { | ||
| 1609 | WARN(1, "omap_hwmod: %s: could not enable clockdomain %s: %d\n", | ||
| 1610 | oh->name, oh->clkdm->name, ret); | ||
| 1611 | return ret; | ||
| 1612 | } | ||
| 1613 | } | ||
| 1614 | |||
| 1615 | _enable_clocks(oh); | ||
| 1616 | if (soc_ops.enable_module) | ||
| 1617 | soc_ops.enable_module(oh); | ||
| 1618 | |||
| 1523 | ret = soc_ops.deassert_hardreset(oh, &ohri); | 1619 | ret = soc_ops.deassert_hardreset(oh, &ohri); |
| 1620 | |||
| 1621 | if (soc_ops.disable_module) | ||
| 1622 | soc_ops.disable_module(oh); | ||
| 1623 | _disable_clocks(oh); | ||
| 1624 | |||
| 1524 | if (ret == -EBUSY) | 1625 | if (ret == -EBUSY) |
| 1525 | pr_warning("omap_hwmod: %s: failed to hardreset\n", oh->name); | 1626 | pr_warning("omap_hwmod: %s: failed to hardreset\n", oh->name); |
| 1526 | 1627 | ||
| 1628 | if (!ret) { | ||
| 1629 | /* | ||
| 1630 | * Set the clockdomain to HW_AUTO, assuming that the | ||
| 1631 | * previous state was HW_AUTO. | ||
| 1632 | */ | ||
| 1633 | if (oh->clkdm && hwsup) | ||
| 1634 | clkdm_allow_idle(oh->clkdm); | ||
| 1635 | } else { | ||
| 1636 | if (oh->clkdm) | ||
| 1637 | clkdm_hwmod_disable(oh->clkdm, oh); | ||
| 1638 | } | ||
| 1639 | |||
| 1527 | return ret; | 1640 | return ret; |
| 1528 | } | 1641 | } |
| 1529 | 1642 | ||
| @@ -1542,7 +1655,7 @@ static int _deassert_hardreset(struct omap_hwmod *oh, const char *name) | |||
| 1542 | static int _read_hardreset(struct omap_hwmod *oh, const char *name) | 1655 | static int _read_hardreset(struct omap_hwmod *oh, const char *name) |
| 1543 | { | 1656 | { |
| 1544 | struct omap_hwmod_rst_info ohri; | 1657 | struct omap_hwmod_rst_info ohri; |
| 1545 | u8 ret = -EINVAL; | 1658 | int ret = -EINVAL; |
| 1546 | 1659 | ||
| 1547 | if (!oh) | 1660 | if (!oh) |
| 1548 | return -EINVAL; | 1661 | return -EINVAL; |
| @@ -1551,32 +1664,35 @@ static int _read_hardreset(struct omap_hwmod *oh, const char *name) | |||
| 1551 | return -ENOSYS; | 1664 | return -ENOSYS; |
| 1552 | 1665 | ||
| 1553 | ret = _lookup_hardreset(oh, name, &ohri); | 1666 | ret = _lookup_hardreset(oh, name, &ohri); |
| 1554 | if (IS_ERR_VALUE(ret)) | 1667 | if (ret < 0) |
| 1555 | return ret; | 1668 | return ret; |
| 1556 | 1669 | ||
| 1557 | return soc_ops.is_hardreset_asserted(oh, &ohri); | 1670 | return soc_ops.is_hardreset_asserted(oh, &ohri); |
| 1558 | } | 1671 | } |
| 1559 | 1672 | ||
| 1560 | /** | 1673 | /** |
| 1561 | * _are_any_hardreset_lines_asserted - return true if part of @oh is hard-reset | 1674 | * _are_all_hardreset_lines_asserted - return true if the @oh is hard-reset |
| 1562 | * @oh: struct omap_hwmod * | 1675 | * @oh: struct omap_hwmod * |
| 1563 | * | 1676 | * |
| 1564 | * If any hardreset line associated with @oh is asserted, then return true. | 1677 | * If all hardreset lines associated with @oh are asserted, then return true. |
| 1565 | * Otherwise, if @oh has no hardreset lines associated with it, or if | 1678 | * Otherwise, if part of @oh is out hardreset or if no hardreset lines |
| 1566 | * no hardreset lines associated with @oh are asserted, then return false. | 1679 | * associated with @oh are asserted, then return false. |
| 1567 | * This function is used to avoid executing some parts of the IP block | 1680 | * This function is used to avoid executing some parts of the IP block |
| 1568 | * enable/disable sequence if a hardreset line is set. | 1681 | * enable/disable sequence if its hardreset line is set. |
| 1569 | */ | 1682 | */ |
| 1570 | static bool _are_any_hardreset_lines_asserted(struct omap_hwmod *oh) | 1683 | static bool _are_all_hardreset_lines_asserted(struct omap_hwmod *oh) |
| 1571 | { | 1684 | { |
| 1572 | int i; | 1685 | int i, rst_cnt = 0; |
| 1573 | 1686 | ||
| 1574 | if (oh->rst_lines_cnt == 0) | 1687 | if (oh->rst_lines_cnt == 0) |
| 1575 | return false; | 1688 | return false; |
| 1576 | 1689 | ||
| 1577 | for (i = 0; i < oh->rst_lines_cnt; i++) | 1690 | for (i = 0; i < oh->rst_lines_cnt; i++) |
| 1578 | if (_read_hardreset(oh, oh->rst_lines[i].name) > 0) | 1691 | if (_read_hardreset(oh, oh->rst_lines[i].name) > 0) |
| 1579 | return true; | 1692 | rst_cnt++; |
| 1693 | |||
| 1694 | if (oh->rst_lines_cnt == rst_cnt) | ||
| 1695 | return true; | ||
| 1580 | 1696 | ||
| 1581 | return false; | 1697 | return false; |
| 1582 | } | 1698 | } |
| @@ -1595,6 +1711,13 @@ static int _omap4_disable_module(struct omap_hwmod *oh) | |||
| 1595 | if (!oh->clkdm || !oh->prcm.omap4.modulemode) | 1711 | if (!oh->clkdm || !oh->prcm.omap4.modulemode) |
| 1596 | return -EINVAL; | 1712 | return -EINVAL; |
| 1597 | 1713 | ||
| 1714 | /* | ||
| 1715 | * Since integration code might still be doing something, only | ||
| 1716 | * disable if all lines are under hardreset. | ||
| 1717 | */ | ||
| 1718 | if (!_are_all_hardreset_lines_asserted(oh)) | ||
| 1719 | return 0; | ||
| 1720 | |||
| 1598 | pr_debug("omap_hwmod: %s: %s\n", oh->name, __func__); | 1721 | pr_debug("omap_hwmod: %s: %s\n", oh->name, __func__); |
| 1599 | 1722 | ||
| 1600 | omap4_cminst_module_disable(oh->clkdm->prcm_partition, | 1723 | omap4_cminst_module_disable(oh->clkdm->prcm_partition, |
| @@ -1602,10 +1725,37 @@ static int _omap4_disable_module(struct omap_hwmod *oh) | |||
| 1602 | oh->clkdm->clkdm_offs, | 1725 | oh->clkdm->clkdm_offs, |
| 1603 | oh->prcm.omap4.clkctrl_offs); | 1726 | oh->prcm.omap4.clkctrl_offs); |
| 1604 | 1727 | ||
| 1605 | if (_are_any_hardreset_lines_asserted(oh)) | 1728 | v = _omap4_wait_target_disable(oh); |
| 1729 | if (v) | ||
| 1730 | pr_warn("omap_hwmod: %s: _wait_target_disable failed\n", | ||
| 1731 | oh->name); | ||
| 1732 | |||
| 1733 | return 0; | ||
| 1734 | } | ||
| 1735 | |||
| 1736 | /** | ||
| 1737 | * _am33xx_disable_module - enable CLKCTRL modulemode on AM33XX | ||
| 1738 | * @oh: struct omap_hwmod * | ||
| 1739 | * | ||
| 1740 | * Disable the PRCM module mode related to the hwmod @oh. | ||
| 1741 | * Return EINVAL if the modulemode is not supported and 0 in case of success. | ||
| 1742 | */ | ||
| 1743 | static int _am33xx_disable_module(struct omap_hwmod *oh) | ||
| 1744 | { | ||
| 1745 | int v; | ||
| 1746 | |||
| 1747 | if (!oh->clkdm || !oh->prcm.omap4.modulemode) | ||
| 1748 | return -EINVAL; | ||
| 1749 | |||
| 1750 | pr_debug("omap_hwmod: %s: %s\n", oh->name, __func__); | ||
| 1751 | |||
| 1752 | am33xx_cm_module_disable(oh->clkdm->cm_inst, oh->clkdm->clkdm_offs, | ||
| 1753 | oh->prcm.omap4.clkctrl_offs); | ||
| 1754 | |||
| 1755 | if (_are_all_hardreset_lines_asserted(oh)) | ||
| 1606 | return 0; | 1756 | return 0; |
| 1607 | 1757 | ||
| 1608 | v = _omap4_wait_target_disable(oh); | 1758 | v = _am33xx_wait_target_disable(oh); |
| 1609 | if (v) | 1759 | if (v) |
| 1610 | pr_warn("omap_hwmod: %s: _wait_target_disable failed\n", | 1760 | pr_warn("omap_hwmod: %s: _wait_target_disable failed\n", |
| 1611 | oh->name); | 1761 | oh->name); |
| @@ -1641,8 +1791,8 @@ static int _ocp_softreset(struct omap_hwmod *oh) | |||
| 1641 | 1791 | ||
| 1642 | /* clocks must be on for this operation */ | 1792 | /* clocks must be on for this operation */ |
| 1643 | if (oh->_state != _HWMOD_STATE_ENABLED) { | 1793 | if (oh->_state != _HWMOD_STATE_ENABLED) { |
| 1644 | pr_warning("omap_hwmod: %s: reset can only be entered from " | 1794 | pr_warn("omap_hwmod: %s: reset can only be entered from enabled state\n", |
| 1645 | "enabled state\n", oh->name); | 1795 | oh->name); |
| 1646 | return -EINVAL; | 1796 | return -EINVAL; |
| 1647 | } | 1797 | } |
| 1648 | 1798 | ||
| @@ -1830,7 +1980,7 @@ static int _enable(struct omap_hwmod *oh) | |||
| 1830 | } | 1980 | } |
| 1831 | 1981 | ||
| 1832 | /* | 1982 | /* |
| 1833 | * If an IP block contains HW reset lines and any of them are | 1983 | * If an IP block contains HW reset lines and all of them are |
| 1834 | * asserted, we let integration code associated with that | 1984 | * asserted, we let integration code associated with that |
| 1835 | * block handle the enable. We've received very little | 1985 | * block handle the enable. We've received very little |
| 1836 | * information on what those driver authors need, and until | 1986 | * information on what those driver authors need, and until |
| @@ -1838,7 +1988,7 @@ static int _enable(struct omap_hwmod *oh) | |||
| 1838 | * posted to the public lists, this is probably the best we | 1988 | * posted to the public lists, this is probably the best we |
| 1839 | * can do. | 1989 | * can do. |
| 1840 | */ | 1990 | */ |
| 1841 | if (_are_any_hardreset_lines_asserted(oh)) | 1991 | if (_are_all_hardreset_lines_asserted(oh)) |
| 1842 | return 0; | 1992 | return 0; |
| 1843 | 1993 | ||
| 1844 | /* Mux pins for device runtime if populated */ | 1994 | /* Mux pins for device runtime if populated */ |
| @@ -1857,7 +2007,8 @@ static int _enable(struct omap_hwmod *oh) | |||
| 1857 | * completely the module. The clockdomain can be set | 2007 | * completely the module. The clockdomain can be set |
| 1858 | * in HW_AUTO only when the module become ready. | 2008 | * in HW_AUTO only when the module become ready. |
| 1859 | */ | 2009 | */ |
| 1860 | hwsup = clkdm_in_hwsup(oh->clkdm); | 2010 | hwsup = clkdm_in_hwsup(oh->clkdm) && |
| 2011 | !clkdm_missing_idle_reporting(oh->clkdm); | ||
| 1861 | r = clkdm_hwmod_enable(oh->clkdm, oh); | 2012 | r = clkdm_hwmod_enable(oh->clkdm, oh); |
| 1862 | if (r) { | 2013 | if (r) { |
| 1863 | WARN(1, "omap_hwmod: %s: could not enable clockdomain %s: %d\n", | 2014 | WARN(1, "omap_hwmod: %s: could not enable clockdomain %s: %d\n", |
| @@ -1919,7 +2070,7 @@ static int _idle(struct omap_hwmod *oh) | |||
| 1919 | return -EINVAL; | 2070 | return -EINVAL; |
| 1920 | } | 2071 | } |
| 1921 | 2072 | ||
| 1922 | if (_are_any_hardreset_lines_asserted(oh)) | 2073 | if (_are_all_hardreset_lines_asserted(oh)) |
| 1923 | return 0; | 2074 | return 0; |
| 1924 | 2075 | ||
| 1925 | if (oh->class->sysc) | 2076 | if (oh->class->sysc) |
| @@ -2007,7 +2158,7 @@ static int _shutdown(struct omap_hwmod *oh) | |||
| 2007 | return -EINVAL; | 2158 | return -EINVAL; |
| 2008 | } | 2159 | } |
| 2009 | 2160 | ||
| 2010 | if (_are_any_hardreset_lines_asserted(oh)) | 2161 | if (_are_all_hardreset_lines_asserted(oh)) |
| 2011 | return 0; | 2162 | return 0; |
| 2012 | 2163 | ||
| 2013 | pr_debug("omap_hwmod: %s: disabling\n", oh->name); | 2164 | pr_debug("omap_hwmod: %s: disabling\n", oh->name); |
| @@ -2531,10 +2682,10 @@ static int _omap2_wait_target_ready(struct omap_hwmod *oh) | |||
| 2531 | */ | 2682 | */ |
| 2532 | static int _omap4_wait_target_ready(struct omap_hwmod *oh) | 2683 | static int _omap4_wait_target_ready(struct omap_hwmod *oh) |
| 2533 | { | 2684 | { |
| 2534 | if (!oh || !oh->clkdm) | 2685 | if (!oh) |
| 2535 | return -EINVAL; | 2686 | return -EINVAL; |
| 2536 | 2687 | ||
| 2537 | if (oh->flags & HWMOD_NO_IDLEST) | 2688 | if (oh->flags & HWMOD_NO_IDLEST || !oh->clkdm) |
| 2538 | return 0; | 2689 | return 0; |
| 2539 | 2690 | ||
| 2540 | if (!_find_mpu_rt_port(oh)) | 2691 | if (!_find_mpu_rt_port(oh)) |
| @@ -2549,6 +2700,33 @@ static int _omap4_wait_target_ready(struct omap_hwmod *oh) | |||
| 2549 | } | 2700 | } |
| 2550 | 2701 | ||
| 2551 | /** | 2702 | /** |
| 2703 | * _am33xx_wait_target_ready - wait for a module to leave slave idle | ||
| 2704 | * @oh: struct omap_hwmod * | ||
| 2705 | * | ||
| 2706 | * Wait for a module @oh to leave slave idle. Returns 0 if the module | ||
| 2707 | * does not have an IDLEST bit or if the module successfully leaves | ||
| 2708 | * slave idle; otherwise, pass along the return value of the | ||
| 2709 | * appropriate *_cm*_wait_module_ready() function. | ||
| 2710 | */ | ||
| 2711 | static int _am33xx_wait_target_ready(struct omap_hwmod *oh) | ||
| 2712 | { | ||
| 2713 | if (!oh || !oh->clkdm) | ||
| 2714 | return -EINVAL; | ||
| 2715 | |||
| 2716 | if (oh->flags & HWMOD_NO_IDLEST) | ||
| 2717 | return 0; | ||
| 2718 | |||
| 2719 | if (!_find_mpu_rt_port(oh)) | ||
| 2720 | return 0; | ||
| 2721 | |||
| 2722 | /* XXX check module SIDLEMODE, hardreset status */ | ||
| 2723 | |||
| 2724 | return am33xx_cm_wait_module_ready(oh->clkdm->cm_inst, | ||
| 2725 | oh->clkdm->clkdm_offs, | ||
| 2726 | oh->prcm.omap4.clkctrl_offs); | ||
| 2727 | } | ||
| 2728 | |||
| 2729 | /** | ||
| 2552 | * _omap2_assert_hardreset - call OMAP2 PRM hardreset fn with hwmod args | 2730 | * _omap2_assert_hardreset - call OMAP2 PRM hardreset fn with hwmod args |
| 2553 | * @oh: struct omap_hwmod * to assert hardreset | 2731 | * @oh: struct omap_hwmod * to assert hardreset |
| 2554 | * @ohri: hardreset line data | 2732 | * @ohri: hardreset line data |
| @@ -2679,6 +2857,72 @@ static int _omap4_is_hardreset_asserted(struct omap_hwmod *oh, | |||
| 2679 | oh->prcm.omap4.rstctrl_offs); | 2857 | oh->prcm.omap4.rstctrl_offs); |
| 2680 | } | 2858 | } |
| 2681 | 2859 | ||
| 2860 | /** | ||
| 2861 | * _am33xx_assert_hardreset - call AM33XX PRM hardreset fn with hwmod args | ||
| 2862 | * @oh: struct omap_hwmod * to assert hardreset | ||
| 2863 | * @ohri: hardreset line data | ||
| 2864 | * | ||
| 2865 | * Call am33xx_prminst_assert_hardreset() with parameters extracted | ||
| 2866 | * from the hwmod @oh and the hardreset line data @ohri. Only | ||
| 2867 | * intended for use as an soc_ops function pointer. Passes along the | ||
| 2868 | * return value from am33xx_prminst_assert_hardreset(). XXX This | ||
| 2869 | * function is scheduled for removal when the PRM code is moved into | ||
| 2870 | * drivers/. | ||
| 2871 | */ | ||
| 2872 | static int _am33xx_assert_hardreset(struct omap_hwmod *oh, | ||
| 2873 | struct omap_hwmod_rst_info *ohri) | ||
| 2874 | |||
| 2875 | { | ||
| 2876 | return am33xx_prm_assert_hardreset(ohri->rst_shift, | ||
| 2877 | oh->clkdm->pwrdm.ptr->prcm_offs, | ||
| 2878 | oh->prcm.omap4.rstctrl_offs); | ||
| 2879 | } | ||
| 2880 | |||
| 2881 | /** | ||
| 2882 | * _am33xx_deassert_hardreset - call AM33XX PRM hardreset fn with hwmod args | ||
| 2883 | * @oh: struct omap_hwmod * to deassert hardreset | ||
| 2884 | * @ohri: hardreset line data | ||
| 2885 | * | ||
| 2886 | * Call am33xx_prminst_deassert_hardreset() with parameters extracted | ||
| 2887 | * from the hwmod @oh and the hardreset line data @ohri. Only | ||
| 2888 | * intended for use as an soc_ops function pointer. Passes along the | ||
| 2889 | * return value from am33xx_prminst_deassert_hardreset(). XXX This | ||
| 2890 | * function is scheduled for removal when the PRM code is moved into | ||
| 2891 | * drivers/. | ||
| 2892 | */ | ||
| 2893 | static int _am33xx_deassert_hardreset(struct omap_hwmod *oh, | ||
| 2894 | struct omap_hwmod_rst_info *ohri) | ||
| 2895 | { | ||
| 2896 | if (ohri->st_shift) | ||
| 2897 | pr_err("omap_hwmod: %s: %s: hwmod data error: OMAP4 does not support st_shift\n", | ||
| 2898 | oh->name, ohri->name); | ||
| 2899 | |||
| 2900 | return am33xx_prm_deassert_hardreset(ohri->rst_shift, | ||
| 2901 | oh->clkdm->pwrdm.ptr->prcm_offs, | ||
| 2902 | oh->prcm.omap4.rstctrl_offs, | ||
| 2903 | oh->prcm.omap4.rstst_offs); | ||
| 2904 | } | ||
| 2905 | |||
| 2906 | /** | ||
| 2907 | * _am33xx_is_hardreset_asserted - call AM33XX PRM hardreset fn with hwmod args | ||
| 2908 | * @oh: struct omap_hwmod * to test hardreset | ||
| 2909 | * @ohri: hardreset line data | ||
| 2910 | * | ||
| 2911 | * Call am33xx_prminst_is_hardreset_asserted() with parameters | ||
| 2912 | * extracted from the hwmod @oh and the hardreset line data @ohri. | ||
| 2913 | * Only intended for use as an soc_ops function pointer. Passes along | ||
| 2914 | * the return value from am33xx_prminst_is_hardreset_asserted(). XXX | ||
| 2915 | * This function is scheduled for removal when the PRM code is moved | ||
| 2916 | * into drivers/. | ||
| 2917 | */ | ||
| 2918 | static int _am33xx_is_hardreset_asserted(struct omap_hwmod *oh, | ||
| 2919 | struct omap_hwmod_rst_info *ohri) | ||
| 2920 | { | ||
| 2921 | return am33xx_prm_is_hardreset_asserted(ohri->rst_shift, | ||
| 2922 | oh->clkdm->pwrdm.ptr->prcm_offs, | ||
| 2923 | oh->prcm.omap4.rstctrl_offs); | ||
| 2924 | } | ||
| 2925 | |||
| 2682 | /* Public functions */ | 2926 | /* Public functions */ |
| 2683 | 2927 | ||
| 2684 | u32 omap_hwmod_read(struct omap_hwmod *oh, u16 reg_offs) | 2928 | u32 omap_hwmod_read(struct omap_hwmod *oh, u16 reg_offs) |
| @@ -3678,6 +3922,14 @@ void __init omap_hwmod_init(void) | |||
| 3678 | soc_ops.deassert_hardreset = _omap4_deassert_hardreset; | 3922 | soc_ops.deassert_hardreset = _omap4_deassert_hardreset; |
| 3679 | soc_ops.is_hardreset_asserted = _omap4_is_hardreset_asserted; | 3923 | soc_ops.is_hardreset_asserted = _omap4_is_hardreset_asserted; |
| 3680 | soc_ops.init_clkdm = _init_clkdm; | 3924 | soc_ops.init_clkdm = _init_clkdm; |
| 3925 | } else if (soc_is_am33xx()) { | ||
| 3926 | soc_ops.enable_module = _am33xx_enable_module; | ||
| 3927 | soc_ops.disable_module = _am33xx_disable_module; | ||
| 3928 | soc_ops.wait_target_ready = _am33xx_wait_target_ready; | ||
| 3929 | soc_ops.assert_hardreset = _am33xx_assert_hardreset; | ||
| 3930 | soc_ops.deassert_hardreset = _am33xx_deassert_hardreset; | ||
| 3931 | soc_ops.is_hardreset_asserted = _am33xx_is_hardreset_asserted; | ||
| 3932 | soc_ops.init_clkdm = _init_clkdm; | ||
| 3681 | } else { | 3933 | } else { |
| 3682 | WARN(1, "omap_hwmod: unknown SoC type\n"); | 3934 | WARN(1, "omap_hwmod: unknown SoC type\n"); |
| 3683 | } | 3935 | } |
