.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * omap_hwmod implementation for OMAP2/3/4 |
---|
3 | 4 | * |
---|
.. | .. |
---|
9 | 10 | * Created in collaboration with (alphabetical order): Thara Gopinath, |
---|
10 | 11 | * Tony Lindgren, Rajendra Nayak, Vikram Pandita, Sakari Poussa, Anand |
---|
11 | 12 | * Sawant, Santosh Shilimkar, Richard Woodruff |
---|
12 | | - * |
---|
13 | | - * This program is free software; you can redistribute it and/or modify |
---|
14 | | - * it under the terms of the GNU General Public License version 2 as |
---|
15 | | - * published by the Free Software Foundation. |
---|
16 | 13 | * |
---|
17 | 14 | * Introduction |
---|
18 | 15 | * ------------ |
---|
.. | .. |
---|
141 | 138 | #include <linux/cpu.h> |
---|
142 | 139 | #include <linux/of.h> |
---|
143 | 140 | #include <linux/of_address.h> |
---|
144 | | -#include <linux/bootmem.h> |
---|
| 141 | +#include <linux/memblock.h> |
---|
145 | 142 | |
---|
146 | 143 | #include <linux/platform_data/ti-sysc.h> |
---|
147 | 144 | |
---|
.. | .. |
---|
155 | 152 | #include "soc.h" |
---|
156 | 153 | #include "common.h" |
---|
157 | 154 | #include "clockdomain.h" |
---|
| 155 | +#include "hdq1w.h" |
---|
| 156 | +#include "mmc.h" |
---|
158 | 157 | #include "powerdomain.h" |
---|
159 | 158 | #include "cm2xxx.h" |
---|
160 | 159 | #include "cm3xxx.h" |
---|
.. | .. |
---|
165 | 164 | #include "prm33xx.h" |
---|
166 | 165 | #include "prminst44xx.h" |
---|
167 | 166 | #include "pm.h" |
---|
| 167 | +#include "wd_timer.h" |
---|
168 | 168 | |
---|
169 | 169 | /* Name of the OMAP hwmod for the MPU */ |
---|
170 | 170 | #define MPU_INITIATOR_NAME "mpu" |
---|
.. | .. |
---|
188 | 188 | |
---|
189 | 189 | /** |
---|
190 | 190 | * struct clkctrl_provider - clkctrl provider mapping data |
---|
191 | | - * @addr: base address for the provider |
---|
192 | | - * @size: size of the provider address space |
---|
193 | | - * @offset: offset of the provider from PRCM instance base |
---|
| 191 | + * @num_addrs: number of base address ranges for the provider |
---|
| 192 | + * @addr: base address(es) for the provider |
---|
| 193 | + * @size: size(s) of the provider address space(s) |
---|
194 | 194 | * @node: device node associated with the provider |
---|
195 | 195 | * @link: list link |
---|
196 | 196 | */ |
---|
197 | 197 | struct clkctrl_provider { |
---|
198 | | - u32 addr; |
---|
199 | | - u32 size; |
---|
200 | | - u16 offset; |
---|
| 198 | + int num_addrs; |
---|
| 199 | + u32 *addr; |
---|
| 200 | + u32 *size; |
---|
201 | 201 | struct device_node *node; |
---|
202 | 202 | struct list_head link; |
---|
203 | 203 | }; |
---|
204 | 204 | |
---|
205 | 205 | static LIST_HEAD(clkctrl_providers); |
---|
| 206 | + |
---|
| 207 | +/** |
---|
| 208 | + * struct omap_hwmod_reset - IP specific reset functions |
---|
| 209 | + * @match: string to match against the module name |
---|
| 210 | + * @len: number of characters to match |
---|
| 211 | + * @reset: IP specific reset function |
---|
| 212 | + * |
---|
| 213 | + * Used only in cases where struct omap_hwmod is dynamically allocated. |
---|
| 214 | + */ |
---|
| 215 | +struct omap_hwmod_reset { |
---|
| 216 | + const char *match; |
---|
| 217 | + int len; |
---|
| 218 | + int (*reset)(struct omap_hwmod *oh); |
---|
| 219 | +}; |
---|
206 | 220 | |
---|
207 | 221 | /** |
---|
208 | 222 | * struct omap_hwmod_soc_ops - fn ptrs for some SoC-specific operations |
---|
.. | .. |
---|
235 | 249 | |
---|
236 | 250 | /* omap_hwmod_list contains all registered struct omap_hwmods */ |
---|
237 | 251 | static LIST_HEAD(omap_hwmod_list); |
---|
| 252 | +static DEFINE_MUTEX(list_lock); |
---|
238 | 253 | |
---|
239 | 254 | /* mpu_oh: used to add/remove MPU initiator from sleepdep list */ |
---|
240 | 255 | static struct omap_hwmod *mpu_oh; |
---|
.. | .. |
---|
608 | 623 | return 0; |
---|
609 | 624 | } |
---|
610 | 625 | |
---|
611 | | -/** |
---|
612 | | - * _disable_wakeup: clear OCP_SYSCONFIG.ENAWAKEUP bit in the hardware |
---|
613 | | - * @oh: struct omap_hwmod * |
---|
614 | | - * |
---|
615 | | - * Prevent the hardware module @oh to send wakeups. Returns -EINVAL |
---|
616 | | - * upon error or 0 upon success. |
---|
617 | | - */ |
---|
618 | | -static int _disable_wakeup(struct omap_hwmod *oh, u32 *v) |
---|
619 | | -{ |
---|
620 | | - if (!oh->class->sysc || |
---|
621 | | - !((oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP) || |
---|
622 | | - (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP) || |
---|
623 | | - (oh->class->sysc->idlemodes & MSTANDBY_SMART_WKUP))) |
---|
624 | | - return -EINVAL; |
---|
625 | | - |
---|
626 | | - if (!oh->class->sysc->sysc_fields) { |
---|
627 | | - WARN(1, "omap_hwmod: %s: offset struct for sysconfig not provided in class\n", oh->name); |
---|
628 | | - return -EINVAL; |
---|
629 | | - } |
---|
630 | | - |
---|
631 | | - if (oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP) |
---|
632 | | - *v &= ~(0x1 << oh->class->sysc->sysc_fields->enwkup_shift); |
---|
633 | | - |
---|
634 | | - if (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP) |
---|
635 | | - _set_slave_idlemode(oh, HWMOD_IDLEMODE_SMART, v); |
---|
636 | | - if (oh->class->sysc->idlemodes & MSTANDBY_SMART_WKUP) |
---|
637 | | - _set_master_standbymode(oh, HWMOD_IDLEMODE_SMART, v); |
---|
638 | | - |
---|
639 | | - /* XXX test pwrdm_get_wken for this hwmod's subsystem */ |
---|
640 | | - |
---|
641 | | - return 0; |
---|
642 | | -} |
---|
643 | | - |
---|
644 | 626 | static struct clockdomain *_get_clkdm(struct omap_hwmod *oh) |
---|
645 | 627 | { |
---|
646 | 628 | struct clk_hw_omap *clk; |
---|
.. | .. |
---|
648 | 630 | if (oh->clkdm) { |
---|
649 | 631 | return oh->clkdm; |
---|
650 | 632 | } else if (oh->_clk) { |
---|
651 | | - if (__clk_get_flags(oh->_clk) & CLK_IS_BASIC) |
---|
| 633 | + if (!omap2_clk_is_hw_omap(__clk_get_hw(oh->_clk))) |
---|
652 | 634 | return NULL; |
---|
653 | 635 | clk = to_clk_hw_omap(__clk_get_hw(oh->_clk)); |
---|
654 | | - return clk->clkdm; |
---|
| 636 | + return clk->clkdm; |
---|
655 | 637 | } |
---|
656 | 638 | return NULL; |
---|
657 | 639 | } |
---|
.. | .. |
---|
724 | 706 | const __be32 *addrp; |
---|
725 | 707 | struct clkctrl_provider *provider; |
---|
726 | 708 | u64 size; |
---|
| 709 | + int i; |
---|
727 | 710 | |
---|
728 | | - provider = memblock_virt_alloc(sizeof(*provider), 0); |
---|
| 711 | + provider = memblock_alloc(sizeof(*provider), SMP_CACHE_BYTES); |
---|
729 | 712 | if (!provider) |
---|
730 | 713 | return -ENOMEM; |
---|
731 | 714 | |
---|
732 | | - addrp = of_get_address(np, 0, &size, NULL); |
---|
733 | | - provider->addr = (u32)of_translate_address(np, addrp); |
---|
734 | | - addrp = of_get_address(np->parent, 0, NULL, NULL); |
---|
735 | | - provider->offset = provider->addr - |
---|
736 | | - (u32)of_translate_address(np->parent, addrp); |
---|
737 | | - provider->addr &= ~0xff; |
---|
738 | | - provider->size = size | 0xff; |
---|
739 | 715 | provider->node = np; |
---|
740 | 716 | |
---|
741 | | - pr_debug("%s: %s: %x...%x [+%x]\n", __func__, np->parent->name, |
---|
742 | | - provider->addr, provider->addr + provider->size, |
---|
743 | | - provider->offset); |
---|
| 717 | + provider->num_addrs = |
---|
| 718 | + of_property_count_elems_of_size(np, "reg", sizeof(u32)) / 2; |
---|
| 719 | + |
---|
| 720 | + provider->addr = |
---|
| 721 | + memblock_alloc(sizeof(void *) * provider->num_addrs, |
---|
| 722 | + SMP_CACHE_BYTES); |
---|
| 723 | + if (!provider->addr) |
---|
| 724 | + return -ENOMEM; |
---|
| 725 | + |
---|
| 726 | + provider->size = |
---|
| 727 | + memblock_alloc(sizeof(u32) * provider->num_addrs, |
---|
| 728 | + SMP_CACHE_BYTES); |
---|
| 729 | + if (!provider->size) |
---|
| 730 | + return -ENOMEM; |
---|
| 731 | + |
---|
| 732 | + for (i = 0; i < provider->num_addrs; i++) { |
---|
| 733 | + addrp = of_get_address(np, i, &size, NULL); |
---|
| 734 | + provider->addr[i] = (u32)of_translate_address(np, addrp); |
---|
| 735 | + provider->size[i] = size; |
---|
| 736 | + pr_debug("%s: %pOF: %x...%x\n", __func__, np, provider->addr[i], |
---|
| 737 | + provider->addr[i] + provider->size[i]); |
---|
| 738 | + } |
---|
744 | 739 | |
---|
745 | 740 | list_add(&provider->link, &clkctrl_providers); |
---|
746 | 741 | |
---|
.. | .. |
---|
789 | 784 | pr_debug("%s: %s: addr=%x\n", __func__, oh->name, addr); |
---|
790 | 785 | |
---|
791 | 786 | list_for_each_entry(provider, &clkctrl_providers, link) { |
---|
792 | | - if (provider->addr <= addr && |
---|
793 | | - provider->addr + provider->size >= addr) { |
---|
794 | | - struct of_phandle_args clkspec; |
---|
| 787 | + int i; |
---|
795 | 788 | |
---|
796 | | - clkspec.np = provider->node; |
---|
797 | | - clkspec.args_count = 2; |
---|
798 | | - clkspec.args[0] = addr - provider->addr - |
---|
799 | | - provider->offset; |
---|
800 | | - clkspec.args[1] = 0; |
---|
| 789 | + for (i = 0; i < provider->num_addrs; i++) { |
---|
| 790 | + if (provider->addr[i] <= addr && |
---|
| 791 | + provider->addr[i] + provider->size[i] > addr) { |
---|
| 792 | + struct of_phandle_args clkspec; |
---|
801 | 793 | |
---|
802 | | - clk = of_clk_get_from_provider(&clkspec); |
---|
| 794 | + clkspec.np = provider->node; |
---|
| 795 | + clkspec.args_count = 2; |
---|
| 796 | + clkspec.args[0] = addr - provider->addr[0]; |
---|
| 797 | + clkspec.args[1] = 0; |
---|
803 | 798 | |
---|
804 | | - pr_debug("%s: %s got %p (offset=%x, provider=%s)\n", |
---|
805 | | - __func__, oh->name, clk, clkspec.args[0], |
---|
806 | | - provider->node->parent->name); |
---|
| 799 | + clk = of_clk_get_from_provider(&clkspec); |
---|
807 | 800 | |
---|
808 | | - return clk; |
---|
| 801 | + pr_debug("%s: %s got %p (offset=%x, provider=%pOF)\n", |
---|
| 802 | + __func__, oh->name, clk, |
---|
| 803 | + clkspec.args[0], provider->node); |
---|
| 804 | + |
---|
| 805 | + return clk; |
---|
| 806 | + } |
---|
809 | 807 | } |
---|
810 | 808 | } |
---|
811 | 809 | |
---|
.. | .. |
---|
988 | 986 | clk_enable(oh->_clk); |
---|
989 | 987 | |
---|
990 | 988 | list_for_each_entry(os, &oh->slave_ports, node) { |
---|
991 | | - if (os->_clk && (os->flags & OCPIF_SWSUP_IDLE)) |
---|
| 989 | + if (os->_clk && (os->flags & OCPIF_SWSUP_IDLE)) { |
---|
| 990 | + omap2_clk_deny_idle(os->_clk); |
---|
992 | 991 | clk_enable(os->_clk); |
---|
| 992 | + } |
---|
993 | 993 | } |
---|
994 | 994 | |
---|
995 | 995 | /* The opt clocks are controlled by the device driver. */ |
---|
.. | .. |
---|
1041 | 1041 | clk_disable(oh->_clk); |
---|
1042 | 1042 | |
---|
1043 | 1043 | list_for_each_entry(os, &oh->slave_ports, node) { |
---|
1044 | | - if (os->_clk && (os->flags & OCPIF_SWSUP_IDLE)) |
---|
| 1044 | + if (os->_clk && (os->flags & OCPIF_SWSUP_IDLE)) { |
---|
1045 | 1045 | clk_disable(os->_clk); |
---|
| 1046 | + omap2_clk_allow_idle(os->_clk); |
---|
| 1047 | + } |
---|
1046 | 1048 | } |
---|
1047 | 1049 | |
---|
1048 | 1050 | if (oh->flags & HWMOD_OPT_CLKS_NEEDED) |
---|
.. | .. |
---|
1853 | 1855 | } |
---|
1854 | 1856 | |
---|
1855 | 1857 | /** |
---|
1856 | | - * _enable_preprogram - Pre-program an IP block during the _enable() process |
---|
1857 | | - * @oh: struct omap_hwmod * |
---|
1858 | | - * |
---|
1859 | | - * Some IP blocks (such as AESS) require some additional programming |
---|
1860 | | - * after enable before they can enter idle. If a function pointer to |
---|
1861 | | - * do so is present in the hwmod data, then call it and pass along the |
---|
1862 | | - * return value; otherwise, return 0. |
---|
1863 | | - */ |
---|
1864 | | -static int _enable_preprogram(struct omap_hwmod *oh) |
---|
1865 | | -{ |
---|
1866 | | - if (!oh->class->enable_preprogram) |
---|
1867 | | - return 0; |
---|
1868 | | - |
---|
1869 | | - return oh->class->enable_preprogram(oh); |
---|
1870 | | -} |
---|
1871 | | - |
---|
1872 | | -/** |
---|
1873 | 1858 | * _enable - enable an omap_hwmod |
---|
1874 | 1859 | * @oh: struct omap_hwmod * |
---|
1875 | 1860 | * |
---|
.. | .. |
---|
1952 | 1937 | _update_sysc_cache(oh); |
---|
1953 | 1938 | _enable_sysc(oh); |
---|
1954 | 1939 | } |
---|
1955 | | - r = _enable_preprogram(oh); |
---|
1956 | 1940 | } else { |
---|
1957 | 1941 | if (soc_ops.disable_module) |
---|
1958 | 1942 | soc_ops.disable_module(oh); |
---|
.. | .. |
---|
2109 | 2093 | if (res) |
---|
2110 | 2094 | continue; |
---|
2111 | 2095 | if (!strcmp(p, oh->name)) { |
---|
2112 | | - pr_debug("omap_hwmod: dt %s[%i] uses hwmod %s\n", |
---|
2113 | | - np->name, i, oh->name); |
---|
| 2096 | + pr_debug("omap_hwmod: dt %pOFn[%i] uses hwmod %s\n", |
---|
| 2097 | + np, i, oh->name); |
---|
2114 | 2098 | return i; |
---|
2115 | 2099 | } |
---|
2116 | 2100 | } |
---|
.. | .. |
---|
2243 | 2227 | return -ENOENT; |
---|
2244 | 2228 | |
---|
2245 | 2229 | if (nr_addr != 1 || nr_size != 1) { |
---|
2246 | | - pr_err("%s: invalid range for %s->%s\n", __func__, |
---|
2247 | | - oh->name, np->name); |
---|
| 2230 | + pr_err("%s: invalid range for %s->%pOFn\n", __func__, |
---|
| 2231 | + oh->name, np); |
---|
2248 | 2232 | return -EINVAL; |
---|
2249 | 2233 | } |
---|
2250 | 2234 | |
---|
.. | .. |
---|
2252 | 2236 | base = of_translate_address(np, ranges++); |
---|
2253 | 2237 | size = be32_to_cpup(ranges); |
---|
2254 | 2238 | |
---|
2255 | | - pr_debug("omap_hwmod: %s %s at 0x%llx size 0x%llx\n", |
---|
2256 | | - oh ? oh->name : "", np->name, base, size); |
---|
| 2239 | + pr_debug("omap_hwmod: %s %pOFn at 0x%llx size 0x%llx\n", |
---|
| 2240 | + oh->name, np, base, size); |
---|
2257 | 2241 | |
---|
2258 | 2242 | if (oh && oh->mpu_rt_idx) { |
---|
2259 | 2243 | omap_hwmod_fix_mpu_rt_idx(oh, np, res); |
---|
.. | .. |
---|
2331 | 2315 | return 0; |
---|
2332 | 2316 | } |
---|
2333 | 2317 | |
---|
| 2318 | +static void __init parse_module_flags(struct omap_hwmod *oh, |
---|
| 2319 | + struct device_node *np) |
---|
| 2320 | +{ |
---|
| 2321 | + if (of_find_property(np, "ti,no-reset-on-init", NULL)) |
---|
| 2322 | + oh->flags |= HWMOD_INIT_NO_RESET; |
---|
| 2323 | + if (of_find_property(np, "ti,no-idle-on-init", NULL)) |
---|
| 2324 | + oh->flags |= HWMOD_INIT_NO_IDLE; |
---|
| 2325 | + if (of_find_property(np, "ti,no-idle", NULL)) |
---|
| 2326 | + oh->flags |= HWMOD_NO_IDLE; |
---|
| 2327 | +} |
---|
| 2328 | + |
---|
2334 | 2329 | /** |
---|
2335 | 2330 | * _init - initialize internal data for the hwmod @oh |
---|
2336 | 2331 | * @oh: struct omap_hwmod * |
---|
.. | .. |
---|
2361 | 2356 | if (r) |
---|
2362 | 2357 | pr_debug("omap_hwmod: %s missing dt data\n", oh->name); |
---|
2363 | 2358 | else if (np && index) |
---|
2364 | | - pr_warn("omap_hwmod: %s using broken dt data from %s\n", |
---|
2365 | | - oh->name, np->name); |
---|
| 2359 | + pr_warn("omap_hwmod: %s using broken dt data from %pOFn\n", |
---|
| 2360 | + oh->name, np); |
---|
2366 | 2361 | |
---|
2367 | 2362 | r = _init_mpu_rt_base(oh, NULL, index, np); |
---|
2368 | 2363 | if (r < 0) { |
---|
.. | .. |
---|
2378 | 2373 | } |
---|
2379 | 2374 | |
---|
2380 | 2375 | if (np) { |
---|
2381 | | - if (of_find_property(np, "ti,no-reset-on-init", NULL)) |
---|
2382 | | - oh->flags |= HWMOD_INIT_NO_RESET; |
---|
2383 | | - if (of_find_property(np, "ti,no-idle-on-init", NULL)) |
---|
2384 | | - oh->flags |= HWMOD_INIT_NO_IDLE; |
---|
2385 | | - if (of_find_property(np, "ti,no-idle", NULL)) |
---|
2386 | | - oh->flags |= HWMOD_NO_IDLE; |
---|
| 2376 | + struct device_node *child; |
---|
| 2377 | + |
---|
| 2378 | + parse_module_flags(oh, np); |
---|
| 2379 | + child = of_get_next_child(np, NULL); |
---|
| 2380 | + if (child) |
---|
| 2381 | + parse_module_flags(oh, child); |
---|
2387 | 2382 | } |
---|
2388 | 2383 | |
---|
2389 | 2384 | oh->_state = _HWMOD_STATE_INITIALIZED; |
---|
.. | .. |
---|
2411 | 2406 | continue; |
---|
2412 | 2407 | |
---|
2413 | 2408 | if (os->flags & OCPIF_SWSUP_IDLE) { |
---|
2414 | | - /* XXX omap_iclk_deny_idle(c); */ |
---|
| 2409 | + /* |
---|
| 2410 | + * we might have multiple users of one iclk with |
---|
| 2411 | + * different requirements, disable autoidle when |
---|
| 2412 | + * the module is enabled, e.g. dss iclk |
---|
| 2413 | + */ |
---|
2415 | 2414 | } else { |
---|
2416 | | - /* XXX omap_iclk_allow_idle(c); */ |
---|
| 2415 | + /* we are enabling autoidle afterwards anyways */ |
---|
2417 | 2416 | clk_enable(os->_clk); |
---|
2418 | 2417 | } |
---|
2419 | 2418 | } |
---|
.. | .. |
---|
2591 | 2590 | * that the copy process would be relatively complex due to the large number |
---|
2592 | 2591 | * of substructures. |
---|
2593 | 2592 | */ |
---|
2594 | | -static int __init _register(struct omap_hwmod *oh) |
---|
| 2593 | +static int _register(struct omap_hwmod *oh) |
---|
2595 | 2594 | { |
---|
2596 | 2595 | if (!oh || !oh->name || !oh->class || !oh->class->name || |
---|
2597 | 2596 | (oh->_state != _HWMOD_STATE_UNKNOWN)) |
---|
.. | .. |
---|
2630 | 2629 | * locking in this code. Changes to this assumption will require |
---|
2631 | 2630 | * additional locking. Returns 0. |
---|
2632 | 2631 | */ |
---|
2633 | | -static int __init _add_link(struct omap_hwmod_ocp_if *oi) |
---|
| 2632 | +static int _add_link(struct omap_hwmod_ocp_if *oi) |
---|
2634 | 2633 | { |
---|
2635 | 2634 | pr_debug("omap_hwmod: %s -> %s: adding link\n", oi->master->name, |
---|
2636 | 2635 | oi->slave->name); |
---|
.. | .. |
---|
3151 | 3150 | /** |
---|
3152 | 3151 | * omap_hwmod_init_regbits - init sysconfig specific register bits |
---|
3153 | 3152 | * @dev: struct device |
---|
| 3153 | + * @oh: module |
---|
3154 | 3154 | * @data: module data |
---|
3155 | 3155 | * @sysc_fields: new sysc configuration |
---|
3156 | 3156 | */ |
---|
3157 | | -static int omap_hwmod_init_regbits(struct device *dev, |
---|
| 3157 | +static int omap_hwmod_init_regbits(struct device *dev, struct omap_hwmod *oh, |
---|
3158 | 3158 | const struct ti_sysc_module_data *data, |
---|
3159 | 3159 | struct sysc_regbits **sysc_fields) |
---|
3160 | 3160 | { |
---|
3161 | | - *sysc_fields = NULL; |
---|
3162 | | - |
---|
3163 | 3161 | switch (data->cap->type) { |
---|
3164 | 3162 | case TI_SYSC_OMAP2: |
---|
3165 | 3163 | case TI_SYSC_OMAP2_TIMER: |
---|
.. | .. |
---|
3194 | 3192 | *sysc_fields = &omap_hwmod_sysc_type_usb_host_fs; |
---|
3195 | 3193 | break; |
---|
3196 | 3194 | default: |
---|
| 3195 | + *sysc_fields = NULL; |
---|
| 3196 | + if (!oh->class->sysc->sysc_fields) |
---|
| 3197 | + return 0; |
---|
| 3198 | + |
---|
| 3199 | + dev_err(dev, "sysc_fields not found\n"); |
---|
| 3200 | + |
---|
3197 | 3201 | return -EINVAL; |
---|
3198 | 3202 | } |
---|
3199 | 3203 | |
---|
.. | .. |
---|
3208 | 3212 | * @sysc_offs: sysc register offset |
---|
3209 | 3213 | * @syss_offs: syss register offset |
---|
3210 | 3214 | */ |
---|
3211 | | -int omap_hwmod_init_reg_offs(struct device *dev, |
---|
3212 | | - const struct ti_sysc_module_data *data, |
---|
3213 | | - s32 *rev_offs, s32 *sysc_offs, s32 *syss_offs) |
---|
| 3215 | +static int omap_hwmod_init_reg_offs(struct device *dev, |
---|
| 3216 | + const struct ti_sysc_module_data *data, |
---|
| 3217 | + s32 *rev_offs, s32 *sysc_offs, |
---|
| 3218 | + s32 *syss_offs) |
---|
3214 | 3219 | { |
---|
3215 | 3220 | *rev_offs = -ENODEV; |
---|
3216 | 3221 | *sysc_offs = 0; |
---|
.. | .. |
---|
3234 | 3239 | * @data: module data |
---|
3235 | 3240 | * @sysc_flags: module configuration |
---|
3236 | 3241 | */ |
---|
3237 | | -int omap_hwmod_init_sysc_flags(struct device *dev, |
---|
3238 | | - const struct ti_sysc_module_data *data, |
---|
3239 | | - u32 *sysc_flags) |
---|
| 3242 | +static int omap_hwmod_init_sysc_flags(struct device *dev, |
---|
| 3243 | + const struct ti_sysc_module_data *data, |
---|
| 3244 | + u32 *sysc_flags) |
---|
3240 | 3245 | { |
---|
3241 | 3246 | *sysc_flags = 0; |
---|
3242 | 3247 | |
---|
.. | .. |
---|
3308 | 3313 | * @data: module data |
---|
3309 | 3314 | * @idlemodes: module supported idle modes |
---|
3310 | 3315 | */ |
---|
3311 | | -int omap_hwmod_init_idlemodes(struct device *dev, |
---|
3312 | | - const struct ti_sysc_module_data *data, |
---|
3313 | | - u32 *idlemodes) |
---|
| 3316 | +static int omap_hwmod_init_idlemodes(struct device *dev, |
---|
| 3317 | + const struct ti_sysc_module_data *data, |
---|
| 3318 | + u32 *idlemodes) |
---|
3314 | 3319 | { |
---|
3315 | 3320 | *idlemodes = 0; |
---|
3316 | 3321 | |
---|
.. | .. |
---|
3358 | 3363 | if (!oh->class->sysc) |
---|
3359 | 3364 | return -ENODEV; |
---|
3360 | 3365 | |
---|
3361 | | - if (sysc_fields != oh->class->sysc->sysc_fields) |
---|
3362 | | - dev_warn(dev, "sysc_fields %p != %p\n", sysc_fields, |
---|
3363 | | - oh->class->sysc->sysc_fields); |
---|
| 3366 | + if (oh->class->sysc->sysc_fields && |
---|
| 3367 | + sysc_fields != oh->class->sysc->sysc_fields) |
---|
| 3368 | + dev_warn(dev, "sysc_fields mismatch\n"); |
---|
3364 | 3369 | |
---|
3365 | 3370 | if (rev_offs != oh->class->sysc->rev_offs) |
---|
3366 | 3371 | dev_warn(dev, "rev_offs %08x != %08x\n", rev_offs, |
---|
.. | .. |
---|
3393 | 3398 | * @dev: struct device |
---|
3394 | 3399 | * @oh: module |
---|
3395 | 3400 | * @sysc_fields: sysc register bits |
---|
| 3401 | + * @clockdomain: clockdomain |
---|
3396 | 3402 | * @rev_offs: revision register offset |
---|
3397 | 3403 | * @sysc_offs: sysconfig register offset |
---|
3398 | 3404 | * @syss_offs: sysstatus register offset |
---|
.. | .. |
---|
3401 | 3407 | * |
---|
3402 | 3408 | * Note that the allocations here cannot use devm as ti-sysc can rebind. |
---|
3403 | 3409 | */ |
---|
3404 | | -int omap_hwmod_allocate_module(struct device *dev, struct omap_hwmod *oh, |
---|
3405 | | - const struct ti_sysc_module_data *data, |
---|
3406 | | - struct sysc_regbits *sysc_fields, |
---|
3407 | | - s32 rev_offs, s32 sysc_offs, s32 syss_offs, |
---|
3408 | | - u32 sysc_flags, u32 idlemodes) |
---|
| 3410 | +static int omap_hwmod_allocate_module(struct device *dev, struct omap_hwmod *oh, |
---|
| 3411 | + const struct ti_sysc_module_data *data, |
---|
| 3412 | + struct sysc_regbits *sysc_fields, |
---|
| 3413 | + struct clockdomain *clkdm, |
---|
| 3414 | + s32 rev_offs, s32 sysc_offs, |
---|
| 3415 | + s32 syss_offs, u32 sysc_flags, |
---|
| 3416 | + u32 idlemodes) |
---|
3409 | 3417 | { |
---|
3410 | 3418 | struct omap_hwmod_class_sysconfig *sysc; |
---|
3411 | | - struct omap_hwmod_class *class; |
---|
| 3419 | + struct omap_hwmod_class *class = NULL; |
---|
| 3420 | + struct omap_hwmod_ocp_if *oi = NULL; |
---|
3412 | 3421 | void __iomem *regs = NULL; |
---|
3413 | 3422 | unsigned long flags; |
---|
3414 | 3423 | |
---|
.. | .. |
---|
3428 | 3437 | regs = ioremap(data->module_pa, |
---|
3429 | 3438 | data->module_size); |
---|
3430 | 3439 | if (!regs) |
---|
3431 | | - return -ENOMEM; |
---|
| 3440 | + goto out_free_sysc; |
---|
3432 | 3441 | } |
---|
3433 | 3442 | |
---|
3434 | 3443 | /* |
---|
3435 | | - * We need new oh->class as the other devices in the same class |
---|
| 3444 | + * We may need a new oh->class as the other devices in the same class |
---|
3436 | 3445 | * may not yet have ioremapped their registers. |
---|
3437 | 3446 | */ |
---|
3438 | | - class = kmemdup(oh->class, sizeof(*oh->class), GFP_KERNEL); |
---|
3439 | | - if (!class) |
---|
3440 | | - return -ENOMEM; |
---|
| 3447 | + if (oh->class->name && strcmp(oh->class->name, data->name)) { |
---|
| 3448 | + class = kmemdup(oh->class, sizeof(*oh->class), GFP_KERNEL); |
---|
| 3449 | + if (!class) |
---|
| 3450 | + goto out_unmap; |
---|
| 3451 | + } |
---|
3441 | 3452 | |
---|
3442 | | - class->sysc = sysc; |
---|
| 3453 | + if (list_empty(&oh->slave_ports)) { |
---|
| 3454 | + oi = kcalloc(1, sizeof(*oi), GFP_KERNEL); |
---|
| 3455 | + if (!oi) |
---|
| 3456 | + goto out_free_class; |
---|
| 3457 | + |
---|
| 3458 | + /* |
---|
| 3459 | + * Note that we assume interconnect interface clocks will be |
---|
| 3460 | + * managed by the interconnect driver for OCPIF_SWSUP_IDLE case |
---|
| 3461 | + * on omap24xx and omap3. |
---|
| 3462 | + */ |
---|
| 3463 | + oi->slave = oh; |
---|
| 3464 | + oi->user = OCP_USER_MPU | OCP_USER_SDMA; |
---|
| 3465 | + } |
---|
3443 | 3466 | |
---|
3444 | 3467 | spin_lock_irqsave(&oh->_lock, flags); |
---|
3445 | 3468 | if (regs) |
---|
3446 | 3469 | oh->_mpu_rt_va = regs; |
---|
3447 | | - oh->class = class; |
---|
| 3470 | + if (class) |
---|
| 3471 | + oh->class = class; |
---|
| 3472 | + oh->class->sysc = sysc; |
---|
| 3473 | + if (oi) |
---|
| 3474 | + _add_link(oi); |
---|
| 3475 | + if (clkdm) |
---|
| 3476 | + oh->clkdm = clkdm; |
---|
3448 | 3477 | oh->_state = _HWMOD_STATE_INITIALIZED; |
---|
| 3478 | + oh->_postsetup_state = _HWMOD_STATE_DEFAULT; |
---|
3449 | 3479 | _setup(oh, NULL); |
---|
3450 | 3480 | spin_unlock_irqrestore(&oh->_lock, flags); |
---|
3451 | 3481 | |
---|
3452 | 3482 | return 0; |
---|
| 3483 | + |
---|
| 3484 | +out_free_class: |
---|
| 3485 | + kfree(class); |
---|
| 3486 | +out_unmap: |
---|
| 3487 | + iounmap(regs); |
---|
| 3488 | +out_free_sysc: |
---|
| 3489 | + kfree(sysc); |
---|
| 3490 | + return -ENOMEM; |
---|
| 3491 | +} |
---|
| 3492 | + |
---|
| 3493 | +static const struct omap_hwmod_reset omap24xx_reset_quirks[] = { |
---|
| 3494 | + { .match = "msdi", .len = 4, .reset = omap_msdi_reset, }, |
---|
| 3495 | +}; |
---|
| 3496 | + |
---|
| 3497 | +static const struct omap_hwmod_reset dra7_reset_quirks[] = { |
---|
| 3498 | + { .match = "pcie", .len = 4, .reset = dra7xx_pciess_reset, }, |
---|
| 3499 | +}; |
---|
| 3500 | + |
---|
| 3501 | +static const struct omap_hwmod_reset omap_reset_quirks[] = { |
---|
| 3502 | + { .match = "dss_core", .len = 8, .reset = omap_dss_reset, }, |
---|
| 3503 | + { .match = "hdq1w", .len = 5, .reset = omap_hdq1w_reset, }, |
---|
| 3504 | + { .match = "i2c", .len = 3, .reset = omap_i2c_reset, }, |
---|
| 3505 | + { .match = "wd_timer", .len = 8, .reset = omap2_wd_timer_reset, }, |
---|
| 3506 | +}; |
---|
| 3507 | + |
---|
| 3508 | +static void |
---|
| 3509 | +omap_hwmod_init_reset_quirk(struct device *dev, struct omap_hwmod *oh, |
---|
| 3510 | + const struct ti_sysc_module_data *data, |
---|
| 3511 | + const struct omap_hwmod_reset *quirks, |
---|
| 3512 | + int quirks_sz) |
---|
| 3513 | +{ |
---|
| 3514 | + const struct omap_hwmod_reset *quirk; |
---|
| 3515 | + int i; |
---|
| 3516 | + |
---|
| 3517 | + for (i = 0; i < quirks_sz; i++) { |
---|
| 3518 | + quirk = &quirks[i]; |
---|
| 3519 | + if (!strncmp(data->name, quirk->match, quirk->len)) { |
---|
| 3520 | + oh->class->reset = quirk->reset; |
---|
| 3521 | + |
---|
| 3522 | + return; |
---|
| 3523 | + } |
---|
| 3524 | + } |
---|
| 3525 | +} |
---|
| 3526 | + |
---|
| 3527 | +static void |
---|
| 3528 | +omap_hwmod_init_reset_quirks(struct device *dev, struct omap_hwmod *oh, |
---|
| 3529 | + const struct ti_sysc_module_data *data) |
---|
| 3530 | +{ |
---|
| 3531 | + if (soc_is_omap24xx()) |
---|
| 3532 | + omap_hwmod_init_reset_quirk(dev, oh, data, |
---|
| 3533 | + omap24xx_reset_quirks, |
---|
| 3534 | + ARRAY_SIZE(omap24xx_reset_quirks)); |
---|
| 3535 | + |
---|
| 3536 | + if (soc_is_dra7xx()) |
---|
| 3537 | + omap_hwmod_init_reset_quirk(dev, oh, data, dra7_reset_quirks, |
---|
| 3538 | + ARRAY_SIZE(dra7_reset_quirks)); |
---|
| 3539 | + |
---|
| 3540 | + omap_hwmod_init_reset_quirk(dev, oh, data, omap_reset_quirks, |
---|
| 3541 | + ARRAY_SIZE(omap_reset_quirks)); |
---|
3453 | 3542 | } |
---|
3454 | 3543 | |
---|
3455 | 3544 | /** |
---|
.. | .. |
---|
3468 | 3557 | u32 sysc_flags, idlemodes; |
---|
3469 | 3558 | int error; |
---|
3470 | 3559 | |
---|
3471 | | - if (!dev || !data) |
---|
| 3560 | + if (!dev || !data || !data->name || !cookie) |
---|
3472 | 3561 | return -EINVAL; |
---|
3473 | 3562 | |
---|
3474 | 3563 | oh = _lookup(data->name); |
---|
3475 | | - if (!oh) |
---|
3476 | | - return -ENODEV; |
---|
| 3564 | + if (!oh) { |
---|
| 3565 | + oh = kzalloc(sizeof(*oh), GFP_KERNEL); |
---|
| 3566 | + if (!oh) |
---|
| 3567 | + return -ENOMEM; |
---|
| 3568 | + |
---|
| 3569 | + oh->name = data->name; |
---|
| 3570 | + oh->_state = _HWMOD_STATE_UNKNOWN; |
---|
| 3571 | + lockdep_register_key(&oh->hwmod_key); |
---|
| 3572 | + |
---|
| 3573 | + /* Unused, can be handled by PRM driver handling resets */ |
---|
| 3574 | + oh->prcm.omap4.flags = HWMOD_OMAP4_NO_CONTEXT_LOSS_BIT; |
---|
| 3575 | + |
---|
| 3576 | + oh->class = kzalloc(sizeof(*oh->class), GFP_KERNEL); |
---|
| 3577 | + if (!oh->class) { |
---|
| 3578 | + kfree(oh); |
---|
| 3579 | + return -ENOMEM; |
---|
| 3580 | + } |
---|
| 3581 | + |
---|
| 3582 | + omap_hwmod_init_reset_quirks(dev, oh, data); |
---|
| 3583 | + |
---|
| 3584 | + oh->class->name = data->name; |
---|
| 3585 | + mutex_lock(&list_lock); |
---|
| 3586 | + error = _register(oh); |
---|
| 3587 | + mutex_unlock(&list_lock); |
---|
| 3588 | + } |
---|
3477 | 3589 | |
---|
3478 | 3590 | cookie->data = oh; |
---|
3479 | 3591 | |
---|
3480 | | - error = omap_hwmod_init_regbits(dev, data, &sysc_fields); |
---|
| 3592 | + error = omap_hwmod_init_regbits(dev, oh, data, &sysc_fields); |
---|
3481 | 3593 | if (error) |
---|
3482 | 3594 | return error; |
---|
3483 | 3595 | |
---|
.. | .. |
---|
3494 | 3606 | if (error) |
---|
3495 | 3607 | return error; |
---|
3496 | 3608 | |
---|
| 3609 | + if (data->cfg->quirks & SYSC_QUIRK_NO_IDLE) |
---|
| 3610 | + oh->flags |= HWMOD_NO_IDLE; |
---|
3497 | 3611 | if (data->cfg->quirks & SYSC_QUIRK_NO_IDLE_ON_INIT) |
---|
3498 | 3612 | oh->flags |= HWMOD_INIT_NO_IDLE; |
---|
3499 | 3613 | if (data->cfg->quirks & SYSC_QUIRK_NO_RESET_ON_INIT) |
---|
3500 | 3614 | oh->flags |= HWMOD_INIT_NO_RESET; |
---|
| 3615 | + if (data->cfg->quirks & SYSC_QUIRK_USE_CLOCKACT) |
---|
| 3616 | + oh->flags |= HWMOD_SET_DEFAULT_CLOCKACT; |
---|
| 3617 | + if (data->cfg->quirks & SYSC_QUIRK_SWSUP_SIDLE) |
---|
| 3618 | + oh->flags |= HWMOD_SWSUP_SIDLE; |
---|
| 3619 | + if (data->cfg->quirks & SYSC_QUIRK_SWSUP_SIDLE_ACT) |
---|
| 3620 | + oh->flags |= HWMOD_SWSUP_SIDLE_ACT; |
---|
| 3621 | + if (data->cfg->quirks & SYSC_QUIRK_SWSUP_MSTANDBY) |
---|
| 3622 | + oh->flags |= HWMOD_SWSUP_MSTANDBY; |
---|
| 3623 | + if (data->cfg->quirks & SYSC_QUIRK_CLKDM_NOAUTO) |
---|
| 3624 | + oh->flags |= HWMOD_CLKDM_NOAUTO; |
---|
3501 | 3625 | |
---|
3502 | 3626 | error = omap_hwmod_check_module(dev, oh, data, sysc_fields, |
---|
3503 | 3627 | rev_offs, sysc_offs, syss_offs, |
---|
.. | .. |
---|
3506 | 3630 | return error; |
---|
3507 | 3631 | |
---|
3508 | 3632 | return omap_hwmod_allocate_module(dev, oh, data, sysc_fields, |
---|
3509 | | - rev_offs, sysc_offs, syss_offs, |
---|
| 3633 | + cookie->clkdm, rev_offs, |
---|
| 3634 | + sysc_offs, syss_offs, |
---|
3510 | 3635 | sysc_flags, idlemodes); |
---|
3511 | 3636 | } |
---|
3512 | 3637 | |
---|
.. | .. |
---|
3656 | 3781 | struct omap_hwmod_ocp_if *oi; |
---|
3657 | 3782 | struct clockdomain *clkdm; |
---|
3658 | 3783 | struct clk_hw_omap *clk; |
---|
| 3784 | + struct clk_hw *hw; |
---|
3659 | 3785 | |
---|
3660 | 3786 | if (!oh) |
---|
3661 | 3787 | return NULL; |
---|
.. | .. |
---|
3672 | 3798 | c = oi->_clk; |
---|
3673 | 3799 | } |
---|
3674 | 3800 | |
---|
3675 | | - clk = to_clk_hw_omap(__clk_get_hw(c)); |
---|
| 3801 | + hw = __clk_get_hw(c); |
---|
| 3802 | + if (!hw) |
---|
| 3803 | + return NULL; |
---|
| 3804 | + |
---|
| 3805 | + clk = to_clk_hw_omap(hw); |
---|
| 3806 | + if (!clk) |
---|
| 3807 | + return NULL; |
---|
| 3808 | + |
---|
3676 | 3809 | clkdm = clk->clkdm; |
---|
3677 | 3810 | if (!clkdm) |
---|
3678 | 3811 | return NULL; |
---|
.. | .. |
---|
3707 | 3840 | * XXX what about functions for drivers to save/restore ocp_sysconfig |
---|
3708 | 3841 | * for context save/restore operations? |
---|
3709 | 3842 | */ |
---|
3710 | | - |
---|
3711 | | -/** |
---|
3712 | | - * omap_hwmod_enable_wakeup - allow device to wake up the system |
---|
3713 | | - * @oh: struct omap_hwmod * |
---|
3714 | | - * |
---|
3715 | | - * Sets the module OCP socket ENAWAKEUP bit to allow the module to |
---|
3716 | | - * send wakeups to the PRCM, and enable I/O ring wakeup events for |
---|
3717 | | - * this IP block if it has dynamic mux entries. Eventually this |
---|
3718 | | - * should set PRCM wakeup registers to cause the PRCM to receive |
---|
3719 | | - * wakeup events from the module. Does not set any wakeup routing |
---|
3720 | | - * registers beyond this point - if the module is to wake up any other |
---|
3721 | | - * module or subsystem, that must be set separately. Called by |
---|
3722 | | - * omap_device code. Returns -EINVAL on error or 0 upon success. |
---|
3723 | | - */ |
---|
3724 | | -int omap_hwmod_enable_wakeup(struct omap_hwmod *oh) |
---|
3725 | | -{ |
---|
3726 | | - unsigned long flags; |
---|
3727 | | - u32 v; |
---|
3728 | | - |
---|
3729 | | - spin_lock_irqsave(&oh->_lock, flags); |
---|
3730 | | - |
---|
3731 | | - if (oh->class->sysc && |
---|
3732 | | - (oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP)) { |
---|
3733 | | - v = oh->_sysc_cache; |
---|
3734 | | - _enable_wakeup(oh, &v); |
---|
3735 | | - _write_sysconfig(v, oh); |
---|
3736 | | - } |
---|
3737 | | - |
---|
3738 | | - spin_unlock_irqrestore(&oh->_lock, flags); |
---|
3739 | | - |
---|
3740 | | - return 0; |
---|
3741 | | -} |
---|
3742 | | - |
---|
3743 | | -/** |
---|
3744 | | - * omap_hwmod_disable_wakeup - prevent device from waking the system |
---|
3745 | | - * @oh: struct omap_hwmod * |
---|
3746 | | - * |
---|
3747 | | - * Clears the module OCP socket ENAWAKEUP bit to prevent the module |
---|
3748 | | - * from sending wakeups to the PRCM, and disable I/O ring wakeup |
---|
3749 | | - * events for this IP block if it has dynamic mux entries. Eventually |
---|
3750 | | - * this should clear PRCM wakeup registers to cause the PRCM to ignore |
---|
3751 | | - * wakeup events from the module. Does not set any wakeup routing |
---|
3752 | | - * registers beyond this point - if the module is to wake up any other |
---|
3753 | | - * module or subsystem, that must be set separately. Called by |
---|
3754 | | - * omap_device code. Returns -EINVAL on error or 0 upon success. |
---|
3755 | | - */ |
---|
3756 | | -int omap_hwmod_disable_wakeup(struct omap_hwmod *oh) |
---|
3757 | | -{ |
---|
3758 | | - unsigned long flags; |
---|
3759 | | - u32 v; |
---|
3760 | | - |
---|
3761 | | - spin_lock_irqsave(&oh->_lock, flags); |
---|
3762 | | - |
---|
3763 | | - if (oh->class->sysc && |
---|
3764 | | - (oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP)) { |
---|
3765 | | - v = oh->_sysc_cache; |
---|
3766 | | - _disable_wakeup(oh, &v); |
---|
3767 | | - _write_sysconfig(v, oh); |
---|
3768 | | - } |
---|
3769 | | - |
---|
3770 | | - spin_unlock_irqrestore(&oh->_lock, flags); |
---|
3771 | | - |
---|
3772 | | - return 0; |
---|
3773 | | -} |
---|
3774 | 3843 | |
---|
3775 | 3844 | /** |
---|
3776 | 3845 | * omap_hwmod_assert_hardreset - assert the HW reset line of submodules |
---|