diff options
Diffstat (limited to 'arch/arm/mach-omap2/omap_hwmod.c')
-rw-r--r-- | arch/arm/mach-omap2/omap_hwmod.c | 143 |
1 files changed, 122 insertions, 21 deletions
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index e3f0ecaf87dd..8a1b5e0bad40 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c | |||
@@ -399,7 +399,7 @@ static int _set_clockactivity(struct omap_hwmod *oh, u8 clockact, u32 *v) | |||
399 | } | 399 | } |
400 | 400 | ||
401 | /** | 401 | /** |
402 | * _set_softreset: set OCP_SYSCONFIG.CLOCKACTIVITY bits in @v | 402 | * _set_softreset: set OCP_SYSCONFIG.SOFTRESET bit in @v |
403 | * @oh: struct omap_hwmod * | 403 | * @oh: struct omap_hwmod * |
404 | * @v: pointer to register contents to modify | 404 | * @v: pointer to register contents to modify |
405 | * | 405 | * |
@@ -427,6 +427,36 @@ static int _set_softreset(struct omap_hwmod *oh, u32 *v) | |||
427 | } | 427 | } |
428 | 428 | ||
429 | /** | 429 | /** |
430 | * _clear_softreset: clear OCP_SYSCONFIG.SOFTRESET bit in @v | ||
431 | * @oh: struct omap_hwmod * | ||
432 | * @v: pointer to register contents to modify | ||
433 | * | ||
434 | * Clear the SOFTRESET bit in @v for hwmod @oh. Returns -EINVAL upon | ||
435 | * error or 0 upon success. | ||
436 | */ | ||
437 | static int _clear_softreset(struct omap_hwmod *oh, u32 *v) | ||
438 | { | ||
439 | u32 softrst_mask; | ||
440 | |||
441 | if (!oh->class->sysc || | ||
442 | !(oh->class->sysc->sysc_flags & SYSC_HAS_SOFTRESET)) | ||
443 | return -EINVAL; | ||
444 | |||
445 | if (!oh->class->sysc->sysc_fields) { | ||
446 | WARN(1, | ||
447 | "omap_hwmod: %s: sysc_fields absent for sysconfig class\n", | ||
448 | oh->name); | ||
449 | return -EINVAL; | ||
450 | } | ||
451 | |||
452 | softrst_mask = (0x1 << oh->class->sysc->sysc_fields->srst_shift); | ||
453 | |||
454 | *v &= ~softrst_mask; | ||
455 | |||
456 | return 0; | ||
457 | } | ||
458 | |||
459 | /** | ||
430 | * _wait_softreset_complete - wait for an OCP softreset to complete | 460 | * _wait_softreset_complete - wait for an OCP softreset to complete |
431 | * @oh: struct omap_hwmod * to wait on | 461 | * @oh: struct omap_hwmod * to wait on |
432 | * | 462 | * |
@@ -785,6 +815,7 @@ static int _init_interface_clks(struct omap_hwmod *oh) | |||
785 | pr_warning("omap_hwmod: %s: cannot clk_get interface_clk %s\n", | 815 | pr_warning("omap_hwmod: %s: cannot clk_get interface_clk %s\n", |
786 | oh->name, os->clk); | 816 | oh->name, os->clk); |
787 | ret = -EINVAL; | 817 | ret = -EINVAL; |
818 | continue; | ||
788 | } | 819 | } |
789 | os->_clk = c; | 820 | os->_clk = c; |
790 | /* | 821 | /* |
@@ -821,6 +852,7 @@ static int _init_opt_clks(struct omap_hwmod *oh) | |||
821 | pr_warning("omap_hwmod: %s: cannot clk_get opt_clk %s\n", | 852 | pr_warning("omap_hwmod: %s: cannot clk_get opt_clk %s\n", |
822 | oh->name, oc->clk); | 853 | oh->name, oc->clk); |
823 | ret = -EINVAL; | 854 | ret = -EINVAL; |
855 | continue; | ||
824 | } | 856 | } |
825 | oc->_clk = c; | 857 | oc->_clk = c; |
826 | /* | 858 | /* |
@@ -1911,6 +1943,12 @@ static int _ocp_softreset(struct omap_hwmod *oh) | |||
1911 | ret = _set_softreset(oh, &v); | 1943 | ret = _set_softreset(oh, &v); |
1912 | if (ret) | 1944 | if (ret) |
1913 | goto dis_opt_clks; | 1945 | goto dis_opt_clks; |
1946 | |||
1947 | _write_sysconfig(v, oh); | ||
1948 | ret = _clear_softreset(oh, &v); | ||
1949 | if (ret) | ||
1950 | goto dis_opt_clks; | ||
1951 | |||
1914 | _write_sysconfig(v, oh); | 1952 | _write_sysconfig(v, oh); |
1915 | 1953 | ||
1916 | if (oh->class->sysc->srst_udelay) | 1954 | if (oh->class->sysc->srst_udelay) |
@@ -2326,38 +2364,80 @@ static int _shutdown(struct omap_hwmod *oh) | |||
2326 | return 0; | 2364 | return 0; |
2327 | } | 2365 | } |
2328 | 2366 | ||
2367 | static int of_dev_find_hwmod(struct device_node *np, | ||
2368 | struct omap_hwmod *oh) | ||
2369 | { | ||
2370 | int count, i, res; | ||
2371 | const char *p; | ||
2372 | |||
2373 | count = of_property_count_strings(np, "ti,hwmods"); | ||
2374 | if (count < 1) | ||
2375 | return -ENODEV; | ||
2376 | |||
2377 | for (i = 0; i < count; i++) { | ||
2378 | res = of_property_read_string_index(np, "ti,hwmods", | ||
2379 | i, &p); | ||
2380 | if (res) | ||
2381 | continue; | ||
2382 | if (!strcmp(p, oh->name)) { | ||
2383 | pr_debug("omap_hwmod: dt %s[%i] uses hwmod %s\n", | ||
2384 | np->name, i, oh->name); | ||
2385 | return i; | ||
2386 | } | ||
2387 | } | ||
2388 | |||
2389 | return -ENODEV; | ||
2390 | } | ||
2391 | |||
2329 | /** | 2392 | /** |
2330 | * of_dev_hwmod_lookup - look up needed hwmod from dt blob | 2393 | * of_dev_hwmod_lookup - look up needed hwmod from dt blob |
2331 | * @np: struct device_node * | 2394 | * @np: struct device_node * |
2332 | * @oh: struct omap_hwmod * | 2395 | * @oh: struct omap_hwmod * |
2396 | * @index: index of the entry found | ||
2397 | * @found: struct device_node * found or NULL | ||
2333 | * | 2398 | * |
2334 | * Parse the dt blob and find out needed hwmod. Recursive function is | 2399 | * Parse the dt blob and find out needed hwmod. Recursive function is |
2335 | * implemented to take care hierarchical dt blob parsing. | 2400 | * implemented to take care hierarchical dt blob parsing. |
2336 | * Return: The device node on success or NULL on failure. | 2401 | * Return: Returns 0 on success, -ENODEV when not found. |
2337 | */ | 2402 | */ |
2338 | static struct device_node *of_dev_hwmod_lookup(struct device_node *np, | 2403 | static int of_dev_hwmod_lookup(struct device_node *np, |
2339 | struct omap_hwmod *oh) | 2404 | struct omap_hwmod *oh, |
2405 | int *index, | ||
2406 | struct device_node **found) | ||
2340 | { | 2407 | { |
2341 | struct device_node *np0 = NULL, *np1 = NULL; | 2408 | struct device_node *np0 = NULL; |
2342 | const char *p; | 2409 | int res; |
2410 | |||
2411 | res = of_dev_find_hwmod(np, oh); | ||
2412 | if (res >= 0) { | ||
2413 | *found = np; | ||
2414 | *index = res; | ||
2415 | return 0; | ||
2416 | } | ||
2343 | 2417 | ||
2344 | for_each_child_of_node(np, np0) { | 2418 | for_each_child_of_node(np, np0) { |
2345 | if (of_find_property(np0, "ti,hwmods", NULL)) { | 2419 | struct device_node *fc; |
2346 | p = of_get_property(np0, "ti,hwmods", NULL); | 2420 | int i; |
2347 | if (!strcmp(p, oh->name)) | 2421 | |
2348 | return np0; | 2422 | res = of_dev_hwmod_lookup(np0, oh, &i, &fc); |
2349 | np1 = of_dev_hwmod_lookup(np0, oh); | 2423 | if (res == 0) { |
2350 | if (np1) | 2424 | *found = fc; |
2351 | return np1; | 2425 | *index = i; |
2426 | return 0; | ||
2352 | } | 2427 | } |
2353 | } | 2428 | } |
2354 | return NULL; | 2429 | |
2430 | *found = NULL; | ||
2431 | *index = 0; | ||
2432 | |||
2433 | return -ENODEV; | ||
2355 | } | 2434 | } |
2356 | 2435 | ||
2357 | /** | 2436 | /** |
2358 | * _init_mpu_rt_base - populate the virtual address for a hwmod | 2437 | * _init_mpu_rt_base - populate the virtual address for a hwmod |
2359 | * @oh: struct omap_hwmod * to locate the virtual address | 2438 | * @oh: struct omap_hwmod * to locate the virtual address |
2360 | * @data: (unused, caller should pass NULL) | 2439 | * @data: (unused, caller should pass NULL) |
2440 | * @index: index of the reg entry iospace in device tree | ||
2361 | * @np: struct device_node * of the IP block's device node in the DT data | 2441 | * @np: struct device_node * of the IP block's device node in the DT data |
2362 | * | 2442 | * |
2363 | * Cache the virtual address used by the MPU to access this IP block's | 2443 | * Cache the virtual address used by the MPU to access this IP block's |
@@ -2368,7 +2448,7 @@ static struct device_node *of_dev_hwmod_lookup(struct device_node *np, | |||
2368 | * -ENXIO on absent or invalid register target address space. | 2448 | * -ENXIO on absent or invalid register target address space. |
2369 | */ | 2449 | */ |
2370 | static int __init _init_mpu_rt_base(struct omap_hwmod *oh, void *data, | 2450 | static int __init _init_mpu_rt_base(struct omap_hwmod *oh, void *data, |
2371 | struct device_node *np) | 2451 | int index, struct device_node *np) |
2372 | { | 2452 | { |
2373 | struct omap_hwmod_addr_space *mem; | 2453 | struct omap_hwmod_addr_space *mem; |
2374 | void __iomem *va_start = NULL; | 2454 | void __iomem *va_start = NULL; |
@@ -2390,13 +2470,17 @@ static int __init _init_mpu_rt_base(struct omap_hwmod *oh, void *data, | |||
2390 | if (!np) | 2470 | if (!np) |
2391 | return -ENXIO; | 2471 | return -ENXIO; |
2392 | 2472 | ||
2393 | va_start = of_iomap(np, oh->mpu_rt_idx); | 2473 | va_start = of_iomap(np, index + oh->mpu_rt_idx); |
2394 | } else { | 2474 | } else { |
2395 | va_start = ioremap(mem->pa_start, mem->pa_end - mem->pa_start); | 2475 | va_start = ioremap(mem->pa_start, mem->pa_end - mem->pa_start); |
2396 | } | 2476 | } |
2397 | 2477 | ||
2398 | if (!va_start) { | 2478 | if (!va_start) { |
2399 | pr_err("omap_hwmod: %s: Could not ioremap\n", oh->name); | 2479 | if (mem) |
2480 | pr_err("omap_hwmod: %s: Could not ioremap\n", oh->name); | ||
2481 | else | ||
2482 | pr_err("omap_hwmod: %s: Missing dt reg%i for %s\n", | ||
2483 | oh->name, index, np->full_name); | ||
2400 | return -ENXIO; | 2484 | return -ENXIO; |
2401 | } | 2485 | } |
2402 | 2486 | ||
@@ -2422,17 +2506,29 @@ static int __init _init_mpu_rt_base(struct omap_hwmod *oh, void *data, | |||
2422 | */ | 2506 | */ |
2423 | static int __init _init(struct omap_hwmod *oh, void *data) | 2507 | static int __init _init(struct omap_hwmod *oh, void *data) |
2424 | { | 2508 | { |
2425 | int r; | 2509 | int r, index; |
2426 | struct device_node *np = NULL; | 2510 | struct device_node *np = NULL; |
2427 | 2511 | ||
2428 | if (oh->_state != _HWMOD_STATE_REGISTERED) | 2512 | if (oh->_state != _HWMOD_STATE_REGISTERED) |
2429 | return 0; | 2513 | return 0; |
2430 | 2514 | ||
2431 | if (of_have_populated_dt()) | 2515 | if (of_have_populated_dt()) { |
2432 | np = of_dev_hwmod_lookup(of_find_node_by_name(NULL, "ocp"), oh); | 2516 | struct device_node *bus; |
2517 | |||
2518 | bus = of_find_node_by_name(NULL, "ocp"); | ||
2519 | if (!bus) | ||
2520 | return -ENODEV; | ||
2521 | |||
2522 | r = of_dev_hwmod_lookup(bus, oh, &index, &np); | ||
2523 | if (r) | ||
2524 | pr_debug("omap_hwmod: %s missing dt data\n", oh->name); | ||
2525 | else if (np && index) | ||
2526 | pr_warn("omap_hwmod: %s using broken dt data from %s\n", | ||
2527 | oh->name, np->name); | ||
2528 | } | ||
2433 | 2529 | ||
2434 | if (oh->class->sysc) { | 2530 | if (oh->class->sysc) { |
2435 | r = _init_mpu_rt_base(oh, NULL, np); | 2531 | r = _init_mpu_rt_base(oh, NULL, index, np); |
2436 | if (r < 0) { | 2532 | if (r < 0) { |
2437 | WARN(1, "omap_hwmod: %s: doesn't have mpu register target base\n", | 2533 | WARN(1, "omap_hwmod: %s: doesn't have mpu register target base\n", |
2438 | oh->name); | 2534 | oh->name); |
@@ -3169,6 +3265,11 @@ int omap_hwmod_softreset(struct omap_hwmod *oh) | |||
3169 | goto error; | 3265 | goto error; |
3170 | _write_sysconfig(v, oh); | 3266 | _write_sysconfig(v, oh); |
3171 | 3267 | ||
3268 | ret = _clear_softreset(oh, &v); | ||
3269 | if (ret) | ||
3270 | goto error; | ||
3271 | _write_sysconfig(v, oh); | ||
3272 | |||
3172 | error: | 3273 | error: |
3173 | return ret; | 3274 | return ret; |
3174 | } | 3275 | } |