125c7d49eSTony Lindgren /*
225c7d49eSTony Lindgren  * omap_device implementation
325c7d49eSTony Lindgren  *
425c7d49eSTony Lindgren  * Copyright (C) 2009-2010 Nokia Corporation
525c7d49eSTony Lindgren  * Paul Walmsley, Kevin Hilman
625c7d49eSTony Lindgren  *
725c7d49eSTony Lindgren  * Developed in collaboration with (alphabetical order): Benoit
825c7d49eSTony Lindgren  * Cousson, Thara Gopinath, Tony Lindgren, Rajendra Nayak, Vikram
925c7d49eSTony Lindgren  * Pandita, Sakari Poussa, Anand Sawant, Santosh Shilimkar, Richard
1025c7d49eSTony Lindgren  * Woodruff
1125c7d49eSTony Lindgren  *
1225c7d49eSTony Lindgren  * This program is free software; you can redistribute it and/or modify
1325c7d49eSTony Lindgren  * it under the terms of the GNU General Public License version 2 as
1425c7d49eSTony Lindgren  * published by the Free Software Foundation.
1525c7d49eSTony Lindgren  *
1625c7d49eSTony Lindgren  * This code provides a consistent interface for OMAP device drivers
1725c7d49eSTony Lindgren  * to control power management and interconnect properties of their
1825c7d49eSTony Lindgren  * devices.
1925c7d49eSTony Lindgren  *
2025c7d49eSTony Lindgren  * In the medium- to long-term, this code should either be
2125c7d49eSTony Lindgren  * a) implemented via arch-specific pointers in platform_data
2225c7d49eSTony Lindgren  * or
2325c7d49eSTony Lindgren  * b) implemented as a proper omap_bus/omap_device in Linux, no more
2425c7d49eSTony Lindgren  *    platform_data func pointers
2525c7d49eSTony Lindgren  *
2625c7d49eSTony Lindgren  *
2725c7d49eSTony Lindgren  * Guidelines for usage by driver authors:
2825c7d49eSTony Lindgren  *
2925c7d49eSTony Lindgren  * 1. These functions are intended to be used by device drivers via
3025c7d49eSTony Lindgren  * function pointers in struct platform_data.  As an example,
3125c7d49eSTony Lindgren  * omap_device_enable() should be passed to the driver as
3225c7d49eSTony Lindgren  *
3325c7d49eSTony Lindgren  * struct foo_driver_platform_data {
3425c7d49eSTony Lindgren  * ...
3525c7d49eSTony Lindgren  *      int (*device_enable)(struct platform_device *pdev);
3625c7d49eSTony Lindgren  * ...
3725c7d49eSTony Lindgren  * }
3825c7d49eSTony Lindgren  *
3925c7d49eSTony Lindgren  * Note that the generic "device_enable" name is used, rather than
4025c7d49eSTony Lindgren  * "omap_device_enable".  This is so other architectures can pass in their
4125c7d49eSTony Lindgren  * own enable/disable functions here.
4225c7d49eSTony Lindgren  *
4325c7d49eSTony Lindgren  * This should be populated during device setup:
4425c7d49eSTony Lindgren  *
4525c7d49eSTony Lindgren  * ...
4625c7d49eSTony Lindgren  * pdata->device_enable = omap_device_enable;
4725c7d49eSTony Lindgren  * ...
4825c7d49eSTony Lindgren  *
4925c7d49eSTony Lindgren  * 2. Drivers should first check to ensure the function pointer is not null
5025c7d49eSTony Lindgren  * before calling it, as in:
5125c7d49eSTony Lindgren  *
5225c7d49eSTony Lindgren  * if (pdata->device_enable)
5325c7d49eSTony Lindgren  *     pdata->device_enable(pdev);
5425c7d49eSTony Lindgren  *
5525c7d49eSTony Lindgren  * This allows other architectures that don't use similar device_enable()/
5625c7d49eSTony Lindgren  * device_shutdown() functions to execute normally.
5725c7d49eSTony Lindgren  *
5825c7d49eSTony Lindgren  * ...
5925c7d49eSTony Lindgren  *
6025c7d49eSTony Lindgren  * Suggested usage by device drivers:
6125c7d49eSTony Lindgren  *
6225c7d49eSTony Lindgren  * During device initialization:
6325c7d49eSTony Lindgren  * device_enable()
6425c7d49eSTony Lindgren  *
6525c7d49eSTony Lindgren  * During device idle:
6625c7d49eSTony Lindgren  * (save remaining device context if necessary)
6725c7d49eSTony Lindgren  * device_idle();
6825c7d49eSTony Lindgren  *
6925c7d49eSTony Lindgren  * During device resume:
7025c7d49eSTony Lindgren  * device_enable();
7125c7d49eSTony Lindgren  * (restore context if necessary)
7225c7d49eSTony Lindgren  *
7325c7d49eSTony Lindgren  * During device shutdown:
7425c7d49eSTony Lindgren  * device_shutdown()
7525c7d49eSTony Lindgren  * (device must be reinitialized at this point to use it again)
7625c7d49eSTony Lindgren  *
7725c7d49eSTony Lindgren  */
7825c7d49eSTony Lindgren #undef DEBUG
7925c7d49eSTony Lindgren 
8025c7d49eSTony Lindgren #include <linux/kernel.h>
8125c7d49eSTony Lindgren #include <linux/export.h>
8225c7d49eSTony Lindgren #include <linux/platform_device.h>
8325c7d49eSTony Lindgren #include <linux/slab.h>
8425c7d49eSTony Lindgren #include <linux/err.h>
8525c7d49eSTony Lindgren #include <linux/io.h>
8625c7d49eSTony Lindgren #include <linux/clk.h>
8725c7d49eSTony Lindgren #include <linux/clkdev.h>
8825c7d49eSTony Lindgren #include <linux/pm_runtime.h>
8925c7d49eSTony Lindgren #include <linux/of.h>
9025c7d49eSTony Lindgren #include <linux/notifier.h>
9125c7d49eSTony Lindgren 
9225c7d49eSTony Lindgren #include "omap_device.h"
932a296c8fSTony Lindgren #include "omap_hwmod.h"
9425c7d49eSTony Lindgren #include <plat/clock.h>
9525c7d49eSTony Lindgren 
9625c7d49eSTony Lindgren /* These parameters are passed to _omap_device_{de,}activate() */
9725c7d49eSTony Lindgren #define USE_WAKEUP_LAT			0
9825c7d49eSTony Lindgren #define IGNORE_WAKEUP_LAT		1
9925c7d49eSTony Lindgren 
10025c7d49eSTony Lindgren static int omap_early_device_register(struct platform_device *pdev);
10125c7d49eSTony Lindgren 
10225c7d49eSTony Lindgren static struct omap_device_pm_latency omap_default_latency[] = {
10325c7d49eSTony Lindgren 	{
10425c7d49eSTony Lindgren 		.deactivate_func = omap_device_idle_hwmods,
10525c7d49eSTony Lindgren 		.activate_func   = omap_device_enable_hwmods,
10625c7d49eSTony Lindgren 		.flags = OMAP_DEVICE_LATENCY_AUTO_ADJUST,
10725c7d49eSTony Lindgren 	}
10825c7d49eSTony Lindgren };
10925c7d49eSTony Lindgren 
11025c7d49eSTony Lindgren /* Private functions */
11125c7d49eSTony Lindgren 
11225c7d49eSTony Lindgren /**
11325c7d49eSTony Lindgren  * _omap_device_activate - increase device readiness
11425c7d49eSTony Lindgren  * @od: struct omap_device *
11525c7d49eSTony Lindgren  * @ignore_lat: increase to latency target (0) or full readiness (1)?
11625c7d49eSTony Lindgren  *
11725c7d49eSTony Lindgren  * Increase readiness of omap_device @od (thus decreasing device
11825c7d49eSTony Lindgren  * wakeup latency, but consuming more power).  If @ignore_lat is
11925c7d49eSTony Lindgren  * IGNORE_WAKEUP_LAT, make the omap_device fully active.  Otherwise,
12025c7d49eSTony Lindgren  * if @ignore_lat is USE_WAKEUP_LAT, and the device's maximum wakeup
12125c7d49eSTony Lindgren  * latency is greater than the requested maximum wakeup latency, step
12225c7d49eSTony Lindgren  * backwards in the omap_device_pm_latency table to ensure the
12325c7d49eSTony Lindgren  * device's maximum wakeup latency is less than or equal to the
12425c7d49eSTony Lindgren  * requested maximum wakeup latency.  Returns 0.
12525c7d49eSTony Lindgren  */
12625c7d49eSTony Lindgren static int _omap_device_activate(struct omap_device *od, u8 ignore_lat)
12725c7d49eSTony Lindgren {
12825c7d49eSTony Lindgren 	struct timespec a, b, c;
12925c7d49eSTony Lindgren 
13025c7d49eSTony Lindgren 	dev_dbg(&od->pdev->dev, "omap_device: activating\n");
13125c7d49eSTony Lindgren 
13225c7d49eSTony Lindgren 	while (od->pm_lat_level > 0) {
13325c7d49eSTony Lindgren 		struct omap_device_pm_latency *odpl;
13425c7d49eSTony Lindgren 		unsigned long long act_lat = 0;
13525c7d49eSTony Lindgren 
13625c7d49eSTony Lindgren 		od->pm_lat_level--;
13725c7d49eSTony Lindgren 
13825c7d49eSTony Lindgren 		odpl = od->pm_lats + od->pm_lat_level;
13925c7d49eSTony Lindgren 
14025c7d49eSTony Lindgren 		if (!ignore_lat &&
14125c7d49eSTony Lindgren 		    (od->dev_wakeup_lat <= od->_dev_wakeup_lat_limit))
14225c7d49eSTony Lindgren 			break;
14325c7d49eSTony Lindgren 
14425c7d49eSTony Lindgren 		read_persistent_clock(&a);
14525c7d49eSTony Lindgren 
14625c7d49eSTony Lindgren 		/* XXX check return code */
14725c7d49eSTony Lindgren 		odpl->activate_func(od);
14825c7d49eSTony Lindgren 
14925c7d49eSTony Lindgren 		read_persistent_clock(&b);
15025c7d49eSTony Lindgren 
15125c7d49eSTony Lindgren 		c = timespec_sub(b, a);
15225c7d49eSTony Lindgren 		act_lat = timespec_to_ns(&c);
15325c7d49eSTony Lindgren 
15425c7d49eSTony Lindgren 		dev_dbg(&od->pdev->dev,
15525c7d49eSTony Lindgren 			"omap_device: pm_lat %d: activate: elapsed time %llu nsec\n",
15625c7d49eSTony Lindgren 			od->pm_lat_level, act_lat);
15725c7d49eSTony Lindgren 
15825c7d49eSTony Lindgren 		if (act_lat > odpl->activate_lat) {
15925c7d49eSTony Lindgren 			odpl->activate_lat_worst = act_lat;
16025c7d49eSTony Lindgren 			if (odpl->flags & OMAP_DEVICE_LATENCY_AUTO_ADJUST) {
16125c7d49eSTony Lindgren 				odpl->activate_lat = act_lat;
16225c7d49eSTony Lindgren 				dev_dbg(&od->pdev->dev,
16325c7d49eSTony Lindgren 					"new worst case activate latency %d: %llu\n",
16425c7d49eSTony Lindgren 					od->pm_lat_level, act_lat);
16525c7d49eSTony Lindgren 			} else
16625c7d49eSTony Lindgren 				dev_warn(&od->pdev->dev,
16725c7d49eSTony Lindgren 					 "activate latency %d higher than expected. (%llu > %d)\n",
16825c7d49eSTony Lindgren 					 od->pm_lat_level, act_lat,
16925c7d49eSTony Lindgren 					 odpl->activate_lat);
17025c7d49eSTony Lindgren 		}
17125c7d49eSTony Lindgren 
17225c7d49eSTony Lindgren 		od->dev_wakeup_lat -= odpl->activate_lat;
17325c7d49eSTony Lindgren 	}
17425c7d49eSTony Lindgren 
17525c7d49eSTony Lindgren 	return 0;
17625c7d49eSTony Lindgren }
17725c7d49eSTony Lindgren 
17825c7d49eSTony Lindgren /**
17925c7d49eSTony Lindgren  * _omap_device_deactivate - decrease device readiness
18025c7d49eSTony Lindgren  * @od: struct omap_device *
18125c7d49eSTony Lindgren  * @ignore_lat: decrease to latency target (0) or full inactivity (1)?
18225c7d49eSTony Lindgren  *
18325c7d49eSTony Lindgren  * Decrease readiness of omap_device @od (thus increasing device
18425c7d49eSTony Lindgren  * wakeup latency, but conserving power).  If @ignore_lat is
18525c7d49eSTony Lindgren  * IGNORE_WAKEUP_LAT, make the omap_device fully inactive.  Otherwise,
18625c7d49eSTony Lindgren  * if @ignore_lat is USE_WAKEUP_LAT, and the device's maximum wakeup
18725c7d49eSTony Lindgren  * latency is less than the requested maximum wakeup latency, step
18825c7d49eSTony Lindgren  * forwards in the omap_device_pm_latency table to ensure the device's
18925c7d49eSTony Lindgren  * maximum wakeup latency is less than or equal to the requested
19025c7d49eSTony Lindgren  * maximum wakeup latency.  Returns 0.
19125c7d49eSTony Lindgren  */
19225c7d49eSTony Lindgren static int _omap_device_deactivate(struct omap_device *od, u8 ignore_lat)
19325c7d49eSTony Lindgren {
19425c7d49eSTony Lindgren 	struct timespec a, b, c;
19525c7d49eSTony Lindgren 
19625c7d49eSTony Lindgren 	dev_dbg(&od->pdev->dev, "omap_device: deactivating\n");
19725c7d49eSTony Lindgren 
19825c7d49eSTony Lindgren 	while (od->pm_lat_level < od->pm_lats_cnt) {
19925c7d49eSTony Lindgren 		struct omap_device_pm_latency *odpl;
20025c7d49eSTony Lindgren 		unsigned long long deact_lat = 0;
20125c7d49eSTony Lindgren 
20225c7d49eSTony Lindgren 		odpl = od->pm_lats + od->pm_lat_level;
20325c7d49eSTony Lindgren 
20425c7d49eSTony Lindgren 		if (!ignore_lat &&
20525c7d49eSTony Lindgren 		    ((od->dev_wakeup_lat + odpl->activate_lat) >
20625c7d49eSTony Lindgren 		     od->_dev_wakeup_lat_limit))
20725c7d49eSTony Lindgren 			break;
20825c7d49eSTony Lindgren 
20925c7d49eSTony Lindgren 		read_persistent_clock(&a);
21025c7d49eSTony Lindgren 
21125c7d49eSTony Lindgren 		/* XXX check return code */
21225c7d49eSTony Lindgren 		odpl->deactivate_func(od);
21325c7d49eSTony Lindgren 
21425c7d49eSTony Lindgren 		read_persistent_clock(&b);
21525c7d49eSTony Lindgren 
21625c7d49eSTony Lindgren 		c = timespec_sub(b, a);
21725c7d49eSTony Lindgren 		deact_lat = timespec_to_ns(&c);
21825c7d49eSTony Lindgren 
21925c7d49eSTony Lindgren 		dev_dbg(&od->pdev->dev,
22025c7d49eSTony Lindgren 			"omap_device: pm_lat %d: deactivate: elapsed time %llu nsec\n",
22125c7d49eSTony Lindgren 			od->pm_lat_level, deact_lat);
22225c7d49eSTony Lindgren 
22325c7d49eSTony Lindgren 		if (deact_lat > odpl->deactivate_lat) {
22425c7d49eSTony Lindgren 			odpl->deactivate_lat_worst = deact_lat;
22525c7d49eSTony Lindgren 			if (odpl->flags & OMAP_DEVICE_LATENCY_AUTO_ADJUST) {
22625c7d49eSTony Lindgren 				odpl->deactivate_lat = deact_lat;
22725c7d49eSTony Lindgren 				dev_dbg(&od->pdev->dev,
22825c7d49eSTony Lindgren 					"new worst case deactivate latency %d: %llu\n",
22925c7d49eSTony Lindgren 					od->pm_lat_level, deact_lat);
23025c7d49eSTony Lindgren 			} else
23125c7d49eSTony Lindgren 				dev_warn(&od->pdev->dev,
23225c7d49eSTony Lindgren 					 "deactivate latency %d higher than expected. (%llu > %d)\n",
23325c7d49eSTony Lindgren 					 od->pm_lat_level, deact_lat,
23425c7d49eSTony Lindgren 					 odpl->deactivate_lat);
23525c7d49eSTony Lindgren 		}
23625c7d49eSTony Lindgren 
23725c7d49eSTony Lindgren 		od->dev_wakeup_lat += odpl->activate_lat;
23825c7d49eSTony Lindgren 
23925c7d49eSTony Lindgren 		od->pm_lat_level++;
24025c7d49eSTony Lindgren 	}
24125c7d49eSTony Lindgren 
24225c7d49eSTony Lindgren 	return 0;
24325c7d49eSTony Lindgren }
24425c7d49eSTony Lindgren 
24525c7d49eSTony Lindgren static void _add_clkdev(struct omap_device *od, const char *clk_alias,
24625c7d49eSTony Lindgren 		       const char *clk_name)
24725c7d49eSTony Lindgren {
24825c7d49eSTony Lindgren 	struct clk *r;
24925c7d49eSTony Lindgren 	struct clk_lookup *l;
25025c7d49eSTony Lindgren 
25125c7d49eSTony Lindgren 	if (!clk_alias || !clk_name)
25225c7d49eSTony Lindgren 		return;
25325c7d49eSTony Lindgren 
25425c7d49eSTony Lindgren 	dev_dbg(&od->pdev->dev, "Creating %s -> %s\n", clk_alias, clk_name);
25525c7d49eSTony Lindgren 
25625c7d49eSTony Lindgren 	r = clk_get_sys(dev_name(&od->pdev->dev), clk_alias);
25725c7d49eSTony Lindgren 	if (!IS_ERR(r)) {
25825c7d49eSTony Lindgren 		dev_warn(&od->pdev->dev,
25925c7d49eSTony Lindgren 			 "alias %s already exists\n", clk_alias);
26025c7d49eSTony Lindgren 		clk_put(r);
26125c7d49eSTony Lindgren 		return;
26225c7d49eSTony Lindgren 	}
26325c7d49eSTony Lindgren 
26425c7d49eSTony Lindgren 	r = clk_get(NULL, clk_name);
26525c7d49eSTony Lindgren 	if (IS_ERR(r)) {
26625c7d49eSTony Lindgren 		dev_err(&od->pdev->dev,
26725c7d49eSTony Lindgren 			"clk_get for %s failed\n", clk_name);
26825c7d49eSTony Lindgren 		return;
26925c7d49eSTony Lindgren 	}
27025c7d49eSTony Lindgren 
27125c7d49eSTony Lindgren 	l = clkdev_alloc(r, clk_alias, dev_name(&od->pdev->dev));
27225c7d49eSTony Lindgren 	if (!l) {
27325c7d49eSTony Lindgren 		dev_err(&od->pdev->dev,
27425c7d49eSTony Lindgren 			"clkdev_alloc for %s failed\n", clk_alias);
27525c7d49eSTony Lindgren 		return;
27625c7d49eSTony Lindgren 	}
27725c7d49eSTony Lindgren 
27825c7d49eSTony Lindgren 	clkdev_add(l);
27925c7d49eSTony Lindgren }
28025c7d49eSTony Lindgren 
28125c7d49eSTony Lindgren /**
28225c7d49eSTony Lindgren  * _add_hwmod_clocks_clkdev - Add clkdev entry for hwmod optional clocks
28325c7d49eSTony Lindgren  * and main clock
28425c7d49eSTony Lindgren  * @od: struct omap_device *od
28525c7d49eSTony Lindgren  * @oh: struct omap_hwmod *oh
28625c7d49eSTony Lindgren  *
28725c7d49eSTony Lindgren  * For the main clock and every optional clock present per hwmod per
28825c7d49eSTony Lindgren  * omap_device, this function adds an entry in the clkdev table of the
28925c7d49eSTony Lindgren  * form <dev-id=dev_name, con-id=role> if it does not exist already.
29025c7d49eSTony Lindgren  *
29125c7d49eSTony Lindgren  * The function is called from inside omap_device_build_ss(), after
29225c7d49eSTony Lindgren  * omap_device_register.
29325c7d49eSTony Lindgren  *
29425c7d49eSTony Lindgren  * This allows drivers to get a pointer to its optional clocks based on its role
29525c7d49eSTony Lindgren  * by calling clk_get(<dev*>, <role>).
29625c7d49eSTony Lindgren  * In the case of the main clock, a "fck" alias is used.
29725c7d49eSTony Lindgren  *
29825c7d49eSTony Lindgren  * No return value.
29925c7d49eSTony Lindgren  */
30025c7d49eSTony Lindgren static void _add_hwmod_clocks_clkdev(struct omap_device *od,
30125c7d49eSTony Lindgren 				     struct omap_hwmod *oh)
30225c7d49eSTony Lindgren {
30325c7d49eSTony Lindgren 	int i;
30425c7d49eSTony Lindgren 
30525c7d49eSTony Lindgren 	_add_clkdev(od, "fck", oh->main_clk);
30625c7d49eSTony Lindgren 
30725c7d49eSTony Lindgren 	for (i = 0; i < oh->opt_clks_cnt; i++)
30825c7d49eSTony Lindgren 		_add_clkdev(od, oh->opt_clks[i].role, oh->opt_clks[i].clk);
30925c7d49eSTony Lindgren }
31025c7d49eSTony Lindgren 
31125c7d49eSTony Lindgren 
31225c7d49eSTony Lindgren /**
31325c7d49eSTony Lindgren  * omap_device_build_from_dt - build an omap_device with multiple hwmods
31425c7d49eSTony Lindgren  * @pdev_name: name of the platform_device driver to use
31525c7d49eSTony Lindgren  * @pdev_id: this platform_device's connection ID
31625c7d49eSTony Lindgren  * @oh: ptr to the single omap_hwmod that backs this omap_device
31725c7d49eSTony Lindgren  * @pdata: platform_data ptr to associate with the platform_device
31825c7d49eSTony Lindgren  * @pdata_len: amount of memory pointed to by @pdata
31925c7d49eSTony Lindgren  * @pm_lats: pointer to a omap_device_pm_latency array for this device
32025c7d49eSTony Lindgren  * @pm_lats_cnt: ARRAY_SIZE() of @pm_lats
32125c7d49eSTony Lindgren  * @is_early_device: should the device be registered as an early device or not
32225c7d49eSTony Lindgren  *
32325c7d49eSTony Lindgren  * Function for building an omap_device already registered from device-tree
32425c7d49eSTony Lindgren  *
32525c7d49eSTony Lindgren  * Returns 0 or PTR_ERR() on error.
32625c7d49eSTony Lindgren  */
32725c7d49eSTony Lindgren static int omap_device_build_from_dt(struct platform_device *pdev)
32825c7d49eSTony Lindgren {
32925c7d49eSTony Lindgren 	struct omap_hwmod **hwmods;
33025c7d49eSTony Lindgren 	struct omap_device *od;
33125c7d49eSTony Lindgren 	struct omap_hwmod *oh;
33225c7d49eSTony Lindgren 	struct device_node *node = pdev->dev.of_node;
33325c7d49eSTony Lindgren 	const char *oh_name;
33425c7d49eSTony Lindgren 	int oh_cnt, i, ret = 0;
33525c7d49eSTony Lindgren 
33625c7d49eSTony Lindgren 	oh_cnt = of_property_count_strings(node, "ti,hwmods");
33725c7d49eSTony Lindgren 	if (!oh_cnt || IS_ERR_VALUE(oh_cnt)) {
33825c7d49eSTony Lindgren 		dev_dbg(&pdev->dev, "No 'hwmods' to build omap_device\n");
33925c7d49eSTony Lindgren 		return -ENODEV;
34025c7d49eSTony Lindgren 	}
34125c7d49eSTony Lindgren 
34225c7d49eSTony Lindgren 	hwmods = kzalloc(sizeof(struct omap_hwmod *) * oh_cnt, GFP_KERNEL);
34325c7d49eSTony Lindgren 	if (!hwmods) {
34425c7d49eSTony Lindgren 		ret = -ENOMEM;
34525c7d49eSTony Lindgren 		goto odbfd_exit;
34625c7d49eSTony Lindgren 	}
34725c7d49eSTony Lindgren 
34825c7d49eSTony Lindgren 	for (i = 0; i < oh_cnt; i++) {
34925c7d49eSTony Lindgren 		of_property_read_string_index(node, "ti,hwmods", i, &oh_name);
35025c7d49eSTony Lindgren 		oh = omap_hwmod_lookup(oh_name);
35125c7d49eSTony Lindgren 		if (!oh) {
35225c7d49eSTony Lindgren 			dev_err(&pdev->dev, "Cannot lookup hwmod '%s'\n",
35325c7d49eSTony Lindgren 				oh_name);
35425c7d49eSTony Lindgren 			ret = -EINVAL;
35525c7d49eSTony Lindgren 			goto odbfd_exit1;
35625c7d49eSTony Lindgren 		}
35725c7d49eSTony Lindgren 		hwmods[i] = oh;
35825c7d49eSTony Lindgren 	}
35925c7d49eSTony Lindgren 
36025c7d49eSTony Lindgren 	od = omap_device_alloc(pdev, hwmods, oh_cnt, NULL, 0);
36125c7d49eSTony Lindgren 	if (!od) {
36225c7d49eSTony Lindgren 		dev_err(&pdev->dev, "Cannot allocate omap_device for :%s\n",
36325c7d49eSTony Lindgren 			oh_name);
36425c7d49eSTony Lindgren 		ret = PTR_ERR(od);
36525c7d49eSTony Lindgren 		goto odbfd_exit1;
36625c7d49eSTony Lindgren 	}
36725c7d49eSTony Lindgren 
36825c7d49eSTony Lindgren 	/* Fix up missing resource names */
36925c7d49eSTony Lindgren 	for (i = 0; i < pdev->num_resources; i++) {
37025c7d49eSTony Lindgren 		struct resource *r = &pdev->resource[i];
37125c7d49eSTony Lindgren 
37225c7d49eSTony Lindgren 		if (r->name == NULL)
37325c7d49eSTony Lindgren 			r->name = dev_name(&pdev->dev);
37425c7d49eSTony Lindgren 	}
37525c7d49eSTony Lindgren 
37625c7d49eSTony Lindgren 	if (of_get_property(node, "ti,no_idle_on_suspend", NULL))
37725c7d49eSTony Lindgren 		omap_device_disable_idle_on_suspend(pdev);
37825c7d49eSTony Lindgren 
37925c7d49eSTony Lindgren 	pdev->dev.pm_domain = &omap_device_pm_domain;
38025c7d49eSTony Lindgren 
38125c7d49eSTony Lindgren odbfd_exit1:
38225c7d49eSTony Lindgren 	kfree(hwmods);
38325c7d49eSTony Lindgren odbfd_exit:
38425c7d49eSTony Lindgren 	return ret;
38525c7d49eSTony Lindgren }
38625c7d49eSTony Lindgren 
38725c7d49eSTony Lindgren static int _omap_device_notifier_call(struct notifier_block *nb,
38825c7d49eSTony Lindgren 				      unsigned long event, void *dev)
38925c7d49eSTony Lindgren {
39025c7d49eSTony Lindgren 	struct platform_device *pdev = to_platform_device(dev);
39125c7d49eSTony Lindgren 	struct omap_device *od;
39225c7d49eSTony Lindgren 
39325c7d49eSTony Lindgren 	switch (event) {
39425c7d49eSTony Lindgren 	case BUS_NOTIFY_DEL_DEVICE:
39525c7d49eSTony Lindgren 		if (pdev->archdata.od)
39625c7d49eSTony Lindgren 			omap_device_delete(pdev->archdata.od);
39725c7d49eSTony Lindgren 		break;
39825c7d49eSTony Lindgren 	case BUS_NOTIFY_ADD_DEVICE:
39925c7d49eSTony Lindgren 		if (pdev->dev.of_node)
40025c7d49eSTony Lindgren 			omap_device_build_from_dt(pdev);
40125c7d49eSTony Lindgren 		/* fall through */
40225c7d49eSTony Lindgren 	default:
40325c7d49eSTony Lindgren 		od = to_omap_device(pdev);
40425c7d49eSTony Lindgren 		if (od)
40525c7d49eSTony Lindgren 			od->_driver_status = event;
40625c7d49eSTony Lindgren 	}
40725c7d49eSTony Lindgren 
40825c7d49eSTony Lindgren 	return NOTIFY_DONE;
40925c7d49eSTony Lindgren }
41025c7d49eSTony Lindgren 
41125c7d49eSTony Lindgren 
41225c7d49eSTony Lindgren /* Public functions for use by core code */
41325c7d49eSTony Lindgren 
41425c7d49eSTony Lindgren /**
41525c7d49eSTony Lindgren  * omap_device_get_context_loss_count - get lost context count
41625c7d49eSTony Lindgren  * @od: struct omap_device *
41725c7d49eSTony Lindgren  *
41825c7d49eSTony Lindgren  * Using the primary hwmod, query the context loss count for this
41925c7d49eSTony Lindgren  * device.
42025c7d49eSTony Lindgren  *
42125c7d49eSTony Lindgren  * Callers should consider context for this device lost any time this
42225c7d49eSTony Lindgren  * function returns a value different than the value the caller got
42325c7d49eSTony Lindgren  * the last time it called this function.
42425c7d49eSTony Lindgren  *
42525c7d49eSTony Lindgren  * If any hwmods exist for the omap_device assoiated with @pdev,
42625c7d49eSTony Lindgren  * return the context loss counter for that hwmod, otherwise return
42725c7d49eSTony Lindgren  * zero.
42825c7d49eSTony Lindgren  */
42925c7d49eSTony Lindgren int omap_device_get_context_loss_count(struct platform_device *pdev)
43025c7d49eSTony Lindgren {
43125c7d49eSTony Lindgren 	struct omap_device *od;
43225c7d49eSTony Lindgren 	u32 ret = 0;
43325c7d49eSTony Lindgren 
43425c7d49eSTony Lindgren 	od = to_omap_device(pdev);
43525c7d49eSTony Lindgren 
43625c7d49eSTony Lindgren 	if (od->hwmods_cnt)
43725c7d49eSTony Lindgren 		ret = omap_hwmod_get_context_loss_count(od->hwmods[0]);
43825c7d49eSTony Lindgren 
43925c7d49eSTony Lindgren 	return ret;
44025c7d49eSTony Lindgren }
44125c7d49eSTony Lindgren 
44225c7d49eSTony Lindgren /**
44325c7d49eSTony Lindgren  * omap_device_count_resources - count number of struct resource entries needed
44425c7d49eSTony Lindgren  * @od: struct omap_device *
44525c7d49eSTony Lindgren  *
44625c7d49eSTony Lindgren  * Count the number of struct resource entries needed for this
44725c7d49eSTony Lindgren  * omap_device @od.  Used by omap_device_build_ss() to determine how
44825c7d49eSTony Lindgren  * much memory to allocate before calling
44925c7d49eSTony Lindgren  * omap_device_fill_resources().  Returns the count.
45025c7d49eSTony Lindgren  */
45125c7d49eSTony Lindgren static int omap_device_count_resources(struct omap_device *od)
45225c7d49eSTony Lindgren {
45325c7d49eSTony Lindgren 	int c = 0;
45425c7d49eSTony Lindgren 	int i;
45525c7d49eSTony Lindgren 
45625c7d49eSTony Lindgren 	for (i = 0; i < od->hwmods_cnt; i++)
45725c7d49eSTony Lindgren 		c += omap_hwmod_count_resources(od->hwmods[i]);
45825c7d49eSTony Lindgren 
45925c7d49eSTony Lindgren 	pr_debug("omap_device: %s: counted %d total resources across %d hwmods\n",
46025c7d49eSTony Lindgren 		 od->pdev->name, c, od->hwmods_cnt);
46125c7d49eSTony Lindgren 
46225c7d49eSTony Lindgren 	return c;
46325c7d49eSTony Lindgren }
46425c7d49eSTony Lindgren 
46525c7d49eSTony Lindgren /**
46625c7d49eSTony Lindgren  * omap_device_fill_resources - fill in array of struct resource
46725c7d49eSTony Lindgren  * @od: struct omap_device *
46825c7d49eSTony Lindgren  * @res: pointer to an array of struct resource to be filled in
46925c7d49eSTony Lindgren  *
47025c7d49eSTony Lindgren  * Populate one or more empty struct resource pointed to by @res with
47125c7d49eSTony Lindgren  * the resource data for this omap_device @od.  Used by
47225c7d49eSTony Lindgren  * omap_device_build_ss() after calling omap_device_count_resources().
47325c7d49eSTony Lindgren  * Ideally this function would not be needed at all.  If omap_device
47425c7d49eSTony Lindgren  * replaces platform_device, then we can specify our own
47525c7d49eSTony Lindgren  * get_resource()/ get_irq()/etc functions that use the underlying
47625c7d49eSTony Lindgren  * omap_hwmod information.  Or if platform_device is extended to use
47725c7d49eSTony Lindgren  * subarchitecture-specific function pointers, the various
47825c7d49eSTony Lindgren  * platform_device functions can simply call omap_device internal
47925c7d49eSTony Lindgren  * functions to get device resources.  Hacking around the existing
48025c7d49eSTony Lindgren  * platform_device code wastes memory.  Returns 0.
48125c7d49eSTony Lindgren  */
48225c7d49eSTony Lindgren static int omap_device_fill_resources(struct omap_device *od,
48325c7d49eSTony Lindgren 				      struct resource *res)
48425c7d49eSTony Lindgren {
48525c7d49eSTony Lindgren 	int i, r;
48625c7d49eSTony Lindgren 
48725c7d49eSTony Lindgren 	for (i = 0; i < od->hwmods_cnt; i++) {
48825c7d49eSTony Lindgren 		r = omap_hwmod_fill_resources(od->hwmods[i], res);
48925c7d49eSTony Lindgren 		res += r;
49025c7d49eSTony Lindgren 	}
49125c7d49eSTony Lindgren 
49225c7d49eSTony Lindgren 	return 0;
49325c7d49eSTony Lindgren }
49425c7d49eSTony Lindgren 
49525c7d49eSTony Lindgren /**
49625c7d49eSTony Lindgren  * _od_fill_dma_resources - fill in array of struct resource with dma resources
49725c7d49eSTony Lindgren  * @od: struct omap_device *
49825c7d49eSTony Lindgren  * @res: pointer to an array of struct resource to be filled in
49925c7d49eSTony Lindgren  *
50025c7d49eSTony Lindgren  * Populate one or more empty struct resource pointed to by @res with
50125c7d49eSTony Lindgren  * the dma resource data for this omap_device @od.  Used by
50225c7d49eSTony Lindgren  * omap_device_alloc() after calling omap_device_count_resources().
50325c7d49eSTony Lindgren  *
50425c7d49eSTony Lindgren  * Ideally this function would not be needed at all.  If we have
50525c7d49eSTony Lindgren  * mechanism to get dma resources from DT.
50625c7d49eSTony Lindgren  *
50725c7d49eSTony Lindgren  * Returns 0.
50825c7d49eSTony Lindgren  */
50925c7d49eSTony Lindgren static int _od_fill_dma_resources(struct omap_device *od,
51025c7d49eSTony Lindgren 				      struct resource *res)
51125c7d49eSTony Lindgren {
51225c7d49eSTony Lindgren 	int i, r;
51325c7d49eSTony Lindgren 
51425c7d49eSTony Lindgren 	for (i = 0; i < od->hwmods_cnt; i++) {
51525c7d49eSTony Lindgren 		r = omap_hwmod_fill_dma_resources(od->hwmods[i], res);
51625c7d49eSTony Lindgren 		res += r;
51725c7d49eSTony Lindgren 	}
51825c7d49eSTony Lindgren 
51925c7d49eSTony Lindgren 	return 0;
52025c7d49eSTony Lindgren }
52125c7d49eSTony Lindgren 
52225c7d49eSTony Lindgren /**
52325c7d49eSTony Lindgren  * omap_device_alloc - allocate an omap_device
52425c7d49eSTony Lindgren  * @pdev: platform_device that will be included in this omap_device
52525c7d49eSTony Lindgren  * @oh: ptr to the single omap_hwmod that backs this omap_device
52625c7d49eSTony Lindgren  * @pdata: platform_data ptr to associate with the platform_device
52725c7d49eSTony Lindgren  * @pdata_len: amount of memory pointed to by @pdata
52825c7d49eSTony Lindgren  * @pm_lats: pointer to a omap_device_pm_latency array for this device
52925c7d49eSTony Lindgren  * @pm_lats_cnt: ARRAY_SIZE() of @pm_lats
53025c7d49eSTony Lindgren  *
53125c7d49eSTony Lindgren  * Convenience function for allocating an omap_device structure and filling
53225c7d49eSTony Lindgren  * hwmods, resources and pm_latency attributes.
53325c7d49eSTony Lindgren  *
53425c7d49eSTony Lindgren  * Returns an struct omap_device pointer or ERR_PTR() on error;
53525c7d49eSTony Lindgren  */
53625c7d49eSTony Lindgren struct omap_device *omap_device_alloc(struct platform_device *pdev,
53725c7d49eSTony Lindgren 					struct omap_hwmod **ohs, int oh_cnt,
53825c7d49eSTony Lindgren 					struct omap_device_pm_latency *pm_lats,
53925c7d49eSTony Lindgren 					int pm_lats_cnt)
54025c7d49eSTony Lindgren {
54125c7d49eSTony Lindgren 	int ret = -ENOMEM;
54225c7d49eSTony Lindgren 	struct omap_device *od;
54325c7d49eSTony Lindgren 	struct resource *res = NULL;
54425c7d49eSTony Lindgren 	int i, res_count;
54525c7d49eSTony Lindgren 	struct omap_hwmod **hwmods;
54625c7d49eSTony Lindgren 
54725c7d49eSTony Lindgren 	od = kzalloc(sizeof(struct omap_device), GFP_KERNEL);
54825c7d49eSTony Lindgren 	if (!od) {
54925c7d49eSTony Lindgren 		ret = -ENOMEM;
55025c7d49eSTony Lindgren 		goto oda_exit1;
55125c7d49eSTony Lindgren 	}
55225c7d49eSTony Lindgren 	od->hwmods_cnt = oh_cnt;
55325c7d49eSTony Lindgren 
55425c7d49eSTony Lindgren 	hwmods = kmemdup(ohs, sizeof(struct omap_hwmod *) * oh_cnt, GFP_KERNEL);
55525c7d49eSTony Lindgren 	if (!hwmods)
55625c7d49eSTony Lindgren 		goto oda_exit2;
55725c7d49eSTony Lindgren 
55825c7d49eSTony Lindgren 	od->hwmods = hwmods;
55925c7d49eSTony Lindgren 	od->pdev = pdev;
56025c7d49eSTony Lindgren 
56125c7d49eSTony Lindgren 	res_count = omap_device_count_resources(od);
56225c7d49eSTony Lindgren 	/*
56325c7d49eSTony Lindgren 	 * DT Boot:
56425c7d49eSTony Lindgren 	 *   OF framework will construct the resource structure (currently
56525c7d49eSTony Lindgren 	 *   does for MEM & IRQ resource) and we should respect/use these
56625c7d49eSTony Lindgren 	 *   resources, killing hwmod dependency.
56725c7d49eSTony Lindgren 	 *   If pdev->num_resources > 0, we assume that MEM & IRQ resources
56825c7d49eSTony Lindgren 	 *   have been allocated by OF layer already (through DTB).
56925c7d49eSTony Lindgren 	 *
57025c7d49eSTony Lindgren 	 * Non-DT Boot:
57125c7d49eSTony Lindgren 	 *   Here, pdev->num_resources = 0, and we should get all the
57225c7d49eSTony Lindgren 	 *   resources from hwmod.
57325c7d49eSTony Lindgren 	 *
57425c7d49eSTony Lindgren 	 * TODO: Once DMA resource is available from OF layer, we should
57525c7d49eSTony Lindgren 	 *   kill filling any resources from hwmod.
57625c7d49eSTony Lindgren 	 */
57725c7d49eSTony Lindgren 	if (res_count > pdev->num_resources) {
57825c7d49eSTony Lindgren 		/* Allocate resources memory to account for new resources */
57925c7d49eSTony Lindgren 		res = kzalloc(sizeof(struct resource) * res_count, GFP_KERNEL);
58025c7d49eSTony Lindgren 		if (!res)
58125c7d49eSTony Lindgren 			goto oda_exit3;
58225c7d49eSTony Lindgren 
58325c7d49eSTony Lindgren 		/*
58425c7d49eSTony Lindgren 		 * If pdev->num_resources > 0, then assume that,
58525c7d49eSTony Lindgren 		 * MEM and IRQ resources will only come from DT and only
58625c7d49eSTony Lindgren 		 * fill DMA resource from hwmod layer.
58725c7d49eSTony Lindgren 		 */
58825c7d49eSTony Lindgren 		if (pdev->num_resources && pdev->resource) {
58925c7d49eSTony Lindgren 			dev_dbg(&pdev->dev, "%s(): resources already allocated %d\n",
59025c7d49eSTony Lindgren 				__func__, res_count);
59125c7d49eSTony Lindgren 			memcpy(res, pdev->resource,
59225c7d49eSTony Lindgren 			       sizeof(struct resource) * pdev->num_resources);
59325c7d49eSTony Lindgren 			_od_fill_dma_resources(od, &res[pdev->num_resources]);
59425c7d49eSTony Lindgren 		} else {
59525c7d49eSTony Lindgren 			dev_dbg(&pdev->dev, "%s(): using resources from hwmod %d\n",
59625c7d49eSTony Lindgren 				__func__, res_count);
59725c7d49eSTony Lindgren 			omap_device_fill_resources(od, res);
59825c7d49eSTony Lindgren 		}
59925c7d49eSTony Lindgren 
60025c7d49eSTony Lindgren 		ret = platform_device_add_resources(pdev, res, res_count);
60125c7d49eSTony Lindgren 		kfree(res);
60225c7d49eSTony Lindgren 
60325c7d49eSTony Lindgren 		if (ret)
60425c7d49eSTony Lindgren 			goto oda_exit3;
60525c7d49eSTony Lindgren 	}
60625c7d49eSTony Lindgren 
60725c7d49eSTony Lindgren 	if (!pm_lats) {
60825c7d49eSTony Lindgren 		pm_lats = omap_default_latency;
60925c7d49eSTony Lindgren 		pm_lats_cnt = ARRAY_SIZE(omap_default_latency);
61025c7d49eSTony Lindgren 	}
61125c7d49eSTony Lindgren 
61225c7d49eSTony Lindgren 	od->pm_lats_cnt = pm_lats_cnt;
61325c7d49eSTony Lindgren 	od->pm_lats = kmemdup(pm_lats,
61425c7d49eSTony Lindgren 			sizeof(struct omap_device_pm_latency) * pm_lats_cnt,
61525c7d49eSTony Lindgren 			GFP_KERNEL);
61625c7d49eSTony Lindgren 	if (!od->pm_lats)
61725c7d49eSTony Lindgren 		goto oda_exit3;
61825c7d49eSTony Lindgren 
61925c7d49eSTony Lindgren 	pdev->archdata.od = od;
62025c7d49eSTony Lindgren 
62125c7d49eSTony Lindgren 	for (i = 0; i < oh_cnt; i++) {
62225c7d49eSTony Lindgren 		hwmods[i]->od = od;
62325c7d49eSTony Lindgren 		_add_hwmod_clocks_clkdev(od, hwmods[i]);
62425c7d49eSTony Lindgren 	}
62525c7d49eSTony Lindgren 
62625c7d49eSTony Lindgren 	return od;
62725c7d49eSTony Lindgren 
62825c7d49eSTony Lindgren oda_exit3:
62925c7d49eSTony Lindgren 	kfree(hwmods);
63025c7d49eSTony Lindgren oda_exit2:
63125c7d49eSTony Lindgren 	kfree(od);
63225c7d49eSTony Lindgren oda_exit1:
63325c7d49eSTony Lindgren 	dev_err(&pdev->dev, "omap_device: build failed (%d)\n", ret);
63425c7d49eSTony Lindgren 
63525c7d49eSTony Lindgren 	return ERR_PTR(ret);
63625c7d49eSTony Lindgren }
63725c7d49eSTony Lindgren 
63825c7d49eSTony Lindgren void omap_device_delete(struct omap_device *od)
63925c7d49eSTony Lindgren {
64025c7d49eSTony Lindgren 	if (!od)
64125c7d49eSTony Lindgren 		return;
64225c7d49eSTony Lindgren 
64325c7d49eSTony Lindgren 	od->pdev->archdata.od = NULL;
64425c7d49eSTony Lindgren 	kfree(od->pm_lats);
64525c7d49eSTony Lindgren 	kfree(od->hwmods);
64625c7d49eSTony Lindgren 	kfree(od);
64725c7d49eSTony Lindgren }
64825c7d49eSTony Lindgren 
64925c7d49eSTony Lindgren /**
65025c7d49eSTony Lindgren  * omap_device_build - build and register an omap_device with one omap_hwmod
65125c7d49eSTony Lindgren  * @pdev_name: name of the platform_device driver to use
65225c7d49eSTony Lindgren  * @pdev_id: this platform_device's connection ID
65325c7d49eSTony Lindgren  * @oh: ptr to the single omap_hwmod that backs this omap_device
65425c7d49eSTony Lindgren  * @pdata: platform_data ptr to associate with the platform_device
65525c7d49eSTony Lindgren  * @pdata_len: amount of memory pointed to by @pdata
65625c7d49eSTony Lindgren  * @pm_lats: pointer to a omap_device_pm_latency array for this device
65725c7d49eSTony Lindgren  * @pm_lats_cnt: ARRAY_SIZE() of @pm_lats
65825c7d49eSTony Lindgren  * @is_early_device: should the device be registered as an early device or not
65925c7d49eSTony Lindgren  *
66025c7d49eSTony Lindgren  * Convenience function for building and registering a single
66125c7d49eSTony Lindgren  * omap_device record, which in turn builds and registers a
66225c7d49eSTony Lindgren  * platform_device record.  See omap_device_build_ss() for more
66325c7d49eSTony Lindgren  * information.  Returns ERR_PTR(-EINVAL) if @oh is NULL; otherwise,
66425c7d49eSTony Lindgren  * passes along the return value of omap_device_build_ss().
66525c7d49eSTony Lindgren  */
66625c7d49eSTony Lindgren struct platform_device __init *omap_device_build(const char *pdev_name, int pdev_id,
66725c7d49eSTony Lindgren 				      struct omap_hwmod *oh, void *pdata,
66825c7d49eSTony Lindgren 				      int pdata_len,
66925c7d49eSTony Lindgren 				      struct omap_device_pm_latency *pm_lats,
67025c7d49eSTony Lindgren 				      int pm_lats_cnt, int is_early_device)
67125c7d49eSTony Lindgren {
67225c7d49eSTony Lindgren 	struct omap_hwmod *ohs[] = { oh };
67325c7d49eSTony Lindgren 
67425c7d49eSTony Lindgren 	if (!oh)
67525c7d49eSTony Lindgren 		return ERR_PTR(-EINVAL);
67625c7d49eSTony Lindgren 
67725c7d49eSTony Lindgren 	return omap_device_build_ss(pdev_name, pdev_id, ohs, 1, pdata,
67825c7d49eSTony Lindgren 				    pdata_len, pm_lats, pm_lats_cnt,
67925c7d49eSTony Lindgren 				    is_early_device);
68025c7d49eSTony Lindgren }
68125c7d49eSTony Lindgren 
68225c7d49eSTony Lindgren /**
68325c7d49eSTony Lindgren  * omap_device_build_ss - build and register an omap_device with multiple hwmods
68425c7d49eSTony Lindgren  * @pdev_name: name of the platform_device driver to use
68525c7d49eSTony Lindgren  * @pdev_id: this platform_device's connection ID
68625c7d49eSTony Lindgren  * @oh: ptr to the single omap_hwmod that backs this omap_device
68725c7d49eSTony Lindgren  * @pdata: platform_data ptr to associate with the platform_device
68825c7d49eSTony Lindgren  * @pdata_len: amount of memory pointed to by @pdata
68925c7d49eSTony Lindgren  * @pm_lats: pointer to a omap_device_pm_latency array for this device
69025c7d49eSTony Lindgren  * @pm_lats_cnt: ARRAY_SIZE() of @pm_lats
69125c7d49eSTony Lindgren  * @is_early_device: should the device be registered as an early device or not
69225c7d49eSTony Lindgren  *
69325c7d49eSTony Lindgren  * Convenience function for building and registering an omap_device
69425c7d49eSTony Lindgren  * subsystem record.  Subsystem records consist of multiple
69525c7d49eSTony Lindgren  * omap_hwmods.  This function in turn builds and registers a
69625c7d49eSTony Lindgren  * platform_device record.  Returns an ERR_PTR() on error, or passes
69725c7d49eSTony Lindgren  * along the return value of omap_device_register().
69825c7d49eSTony Lindgren  */
69925c7d49eSTony Lindgren struct platform_device __init *omap_device_build_ss(const char *pdev_name, int pdev_id,
70025c7d49eSTony Lindgren 					 struct omap_hwmod **ohs, int oh_cnt,
70125c7d49eSTony Lindgren 					 void *pdata, int pdata_len,
70225c7d49eSTony Lindgren 					 struct omap_device_pm_latency *pm_lats,
70325c7d49eSTony Lindgren 					 int pm_lats_cnt, int is_early_device)
70425c7d49eSTony Lindgren {
70525c7d49eSTony Lindgren 	int ret = -ENOMEM;
70625c7d49eSTony Lindgren 	struct platform_device *pdev;
70725c7d49eSTony Lindgren 	struct omap_device *od;
70825c7d49eSTony Lindgren 
70925c7d49eSTony Lindgren 	if (!ohs || oh_cnt == 0 || !pdev_name)
71025c7d49eSTony Lindgren 		return ERR_PTR(-EINVAL);
71125c7d49eSTony Lindgren 
71225c7d49eSTony Lindgren 	if (!pdata && pdata_len > 0)
71325c7d49eSTony Lindgren 		return ERR_PTR(-EINVAL);
71425c7d49eSTony Lindgren 
71525c7d49eSTony Lindgren 	pdev = platform_device_alloc(pdev_name, pdev_id);
71625c7d49eSTony Lindgren 	if (!pdev) {
71725c7d49eSTony Lindgren 		ret = -ENOMEM;
71825c7d49eSTony Lindgren 		goto odbs_exit;
71925c7d49eSTony Lindgren 	}
72025c7d49eSTony Lindgren 
72125c7d49eSTony Lindgren 	/* Set the dev_name early to allow dev_xxx in omap_device_alloc */
72225c7d49eSTony Lindgren 	if (pdev->id != -1)
72325c7d49eSTony Lindgren 		dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
72425c7d49eSTony Lindgren 	else
72525c7d49eSTony Lindgren 		dev_set_name(&pdev->dev, "%s", pdev->name);
72625c7d49eSTony Lindgren 
72725c7d49eSTony Lindgren 	od = omap_device_alloc(pdev, ohs, oh_cnt, pm_lats, pm_lats_cnt);
72825c7d49eSTony Lindgren 	if (IS_ERR(od))
72925c7d49eSTony Lindgren 		goto odbs_exit1;
73025c7d49eSTony Lindgren 
73125c7d49eSTony Lindgren 	ret = platform_device_add_data(pdev, pdata, pdata_len);
73225c7d49eSTony Lindgren 	if (ret)
73325c7d49eSTony Lindgren 		goto odbs_exit2;
73425c7d49eSTony Lindgren 
73525c7d49eSTony Lindgren 	if (is_early_device)
73625c7d49eSTony Lindgren 		ret = omap_early_device_register(pdev);
73725c7d49eSTony Lindgren 	else
73825c7d49eSTony Lindgren 		ret = omap_device_register(pdev);
73925c7d49eSTony Lindgren 	if (ret)
74025c7d49eSTony Lindgren 		goto odbs_exit2;
74125c7d49eSTony Lindgren 
74225c7d49eSTony Lindgren 	return pdev;
74325c7d49eSTony Lindgren 
74425c7d49eSTony Lindgren odbs_exit2:
74525c7d49eSTony Lindgren 	omap_device_delete(od);
74625c7d49eSTony Lindgren odbs_exit1:
74725c7d49eSTony Lindgren 	platform_device_put(pdev);
74825c7d49eSTony Lindgren odbs_exit:
74925c7d49eSTony Lindgren 
75025c7d49eSTony Lindgren 	pr_err("omap_device: %s: build failed (%d)\n", pdev_name, ret);
75125c7d49eSTony Lindgren 
75225c7d49eSTony Lindgren 	return ERR_PTR(ret);
75325c7d49eSTony Lindgren }
75425c7d49eSTony Lindgren 
75525c7d49eSTony Lindgren /**
75625c7d49eSTony Lindgren  * omap_early_device_register - register an omap_device as an early platform
75725c7d49eSTony Lindgren  * device.
75825c7d49eSTony Lindgren  * @od: struct omap_device * to register
75925c7d49eSTony Lindgren  *
76025c7d49eSTony Lindgren  * Register the omap_device structure.  This currently just calls
76125c7d49eSTony Lindgren  * platform_early_add_device() on the underlying platform_device.
76225c7d49eSTony Lindgren  * Returns 0 by default.
76325c7d49eSTony Lindgren  */
76425c7d49eSTony Lindgren static int __init omap_early_device_register(struct platform_device *pdev)
76525c7d49eSTony Lindgren {
76625c7d49eSTony Lindgren 	struct platform_device *devices[1];
76725c7d49eSTony Lindgren 
76825c7d49eSTony Lindgren 	devices[0] = pdev;
76925c7d49eSTony Lindgren 	early_platform_add_devices(devices, 1);
77025c7d49eSTony Lindgren 	return 0;
77125c7d49eSTony Lindgren }
77225c7d49eSTony Lindgren 
77325c7d49eSTony Lindgren #ifdef CONFIG_PM_RUNTIME
77425c7d49eSTony Lindgren static int _od_runtime_suspend(struct device *dev)
77525c7d49eSTony Lindgren {
77625c7d49eSTony Lindgren 	struct platform_device *pdev = to_platform_device(dev);
77725c7d49eSTony Lindgren 	int ret;
77825c7d49eSTony Lindgren 
77925c7d49eSTony Lindgren 	ret = pm_generic_runtime_suspend(dev);
78025c7d49eSTony Lindgren 
78125c7d49eSTony Lindgren 	if (!ret)
78225c7d49eSTony Lindgren 		omap_device_idle(pdev);
78325c7d49eSTony Lindgren 
78425c7d49eSTony Lindgren 	return ret;
78525c7d49eSTony Lindgren }
78625c7d49eSTony Lindgren 
78725c7d49eSTony Lindgren static int _od_runtime_idle(struct device *dev)
78825c7d49eSTony Lindgren {
78925c7d49eSTony Lindgren 	return pm_generic_runtime_idle(dev);
79025c7d49eSTony Lindgren }
79125c7d49eSTony Lindgren 
79225c7d49eSTony Lindgren static int _od_runtime_resume(struct device *dev)
79325c7d49eSTony Lindgren {
79425c7d49eSTony Lindgren 	struct platform_device *pdev = to_platform_device(dev);
79525c7d49eSTony Lindgren 
79625c7d49eSTony Lindgren 	omap_device_enable(pdev);
79725c7d49eSTony Lindgren 
79825c7d49eSTony Lindgren 	return pm_generic_runtime_resume(dev);
79925c7d49eSTony Lindgren }
80025c7d49eSTony Lindgren #endif
80125c7d49eSTony Lindgren 
80225c7d49eSTony Lindgren #ifdef CONFIG_SUSPEND
80325c7d49eSTony Lindgren static int _od_suspend_noirq(struct device *dev)
80425c7d49eSTony Lindgren {
80525c7d49eSTony Lindgren 	struct platform_device *pdev = to_platform_device(dev);
80625c7d49eSTony Lindgren 	struct omap_device *od = to_omap_device(pdev);
80725c7d49eSTony Lindgren 	int ret;
80825c7d49eSTony Lindgren 
80925c7d49eSTony Lindgren 	/* Don't attempt late suspend on a driver that is not bound */
81025c7d49eSTony Lindgren 	if (od->_driver_status != BUS_NOTIFY_BOUND_DRIVER)
81125c7d49eSTony Lindgren 		return 0;
81225c7d49eSTony Lindgren 
81325c7d49eSTony Lindgren 	ret = pm_generic_suspend_noirq(dev);
81425c7d49eSTony Lindgren 
81525c7d49eSTony Lindgren 	if (!ret && !pm_runtime_status_suspended(dev)) {
81625c7d49eSTony Lindgren 		if (pm_generic_runtime_suspend(dev) == 0) {
81725c7d49eSTony Lindgren 			if (!(od->flags & OMAP_DEVICE_NO_IDLE_ON_SUSPEND))
81825c7d49eSTony Lindgren 				omap_device_idle(pdev);
81925c7d49eSTony Lindgren 			od->flags |= OMAP_DEVICE_SUSPENDED;
82025c7d49eSTony Lindgren 		}
82125c7d49eSTony Lindgren 	}
82225c7d49eSTony Lindgren 
82325c7d49eSTony Lindgren 	return ret;
82425c7d49eSTony Lindgren }
82525c7d49eSTony Lindgren 
82625c7d49eSTony Lindgren static int _od_resume_noirq(struct device *dev)
82725c7d49eSTony Lindgren {
82825c7d49eSTony Lindgren 	struct platform_device *pdev = to_platform_device(dev);
82925c7d49eSTony Lindgren 	struct omap_device *od = to_omap_device(pdev);
83025c7d49eSTony Lindgren 
83125c7d49eSTony Lindgren 	if ((od->flags & OMAP_DEVICE_SUSPENDED) &&
83225c7d49eSTony Lindgren 	    !pm_runtime_status_suspended(dev)) {
83325c7d49eSTony Lindgren 		od->flags &= ~OMAP_DEVICE_SUSPENDED;
83425c7d49eSTony Lindgren 		if (!(od->flags & OMAP_DEVICE_NO_IDLE_ON_SUSPEND))
83525c7d49eSTony Lindgren 			omap_device_enable(pdev);
83625c7d49eSTony Lindgren 		pm_generic_runtime_resume(dev);
83725c7d49eSTony Lindgren 	}
83825c7d49eSTony Lindgren 
83925c7d49eSTony Lindgren 	return pm_generic_resume_noirq(dev);
84025c7d49eSTony Lindgren }
84125c7d49eSTony Lindgren #else
84225c7d49eSTony Lindgren #define _od_suspend_noirq NULL
84325c7d49eSTony Lindgren #define _od_resume_noirq NULL
84425c7d49eSTony Lindgren #endif
84525c7d49eSTony Lindgren 
84625c7d49eSTony Lindgren struct dev_pm_domain omap_device_pm_domain = {
84725c7d49eSTony Lindgren 	.ops = {
84825c7d49eSTony Lindgren 		SET_RUNTIME_PM_OPS(_od_runtime_suspend, _od_runtime_resume,
84925c7d49eSTony Lindgren 				   _od_runtime_idle)
85025c7d49eSTony Lindgren 		USE_PLATFORM_PM_SLEEP_OPS
85125c7d49eSTony Lindgren 		.suspend_noirq = _od_suspend_noirq,
85225c7d49eSTony Lindgren 		.resume_noirq = _od_resume_noirq,
85325c7d49eSTony Lindgren 	}
85425c7d49eSTony Lindgren };
85525c7d49eSTony Lindgren 
85625c7d49eSTony Lindgren /**
85725c7d49eSTony Lindgren  * omap_device_register - register an omap_device with one omap_hwmod
85825c7d49eSTony Lindgren  * @od: struct omap_device * to register
85925c7d49eSTony Lindgren  *
86025c7d49eSTony Lindgren  * Register the omap_device structure.  This currently just calls
86125c7d49eSTony Lindgren  * platform_device_register() on the underlying platform_device.
86225c7d49eSTony Lindgren  * Returns the return value of platform_device_register().
86325c7d49eSTony Lindgren  */
86425c7d49eSTony Lindgren int omap_device_register(struct platform_device *pdev)
86525c7d49eSTony Lindgren {
86625c7d49eSTony Lindgren 	pr_debug("omap_device: %s: registering\n", pdev->name);
86725c7d49eSTony Lindgren 
86825c7d49eSTony Lindgren 	pdev->dev.pm_domain = &omap_device_pm_domain;
86925c7d49eSTony Lindgren 	return platform_device_add(pdev);
87025c7d49eSTony Lindgren }
87125c7d49eSTony Lindgren 
87225c7d49eSTony Lindgren 
87325c7d49eSTony Lindgren /* Public functions for use by device drivers through struct platform_data */
87425c7d49eSTony Lindgren 
87525c7d49eSTony Lindgren /**
87625c7d49eSTony Lindgren  * omap_device_enable - fully activate an omap_device
87725c7d49eSTony Lindgren  * @od: struct omap_device * to activate
87825c7d49eSTony Lindgren  *
87925c7d49eSTony Lindgren  * Do whatever is necessary for the hwmods underlying omap_device @od
88025c7d49eSTony Lindgren  * to be accessible and ready to operate.  This generally involves
88125c7d49eSTony Lindgren  * enabling clocks, setting SYSCONFIG registers; and in the future may
88225c7d49eSTony Lindgren  * involve remuxing pins.  Device drivers should call this function
88325c7d49eSTony Lindgren  * (through platform_data function pointers) where they would normally
88425c7d49eSTony Lindgren  * enable clocks, etc.  Returns -EINVAL if called when the omap_device
88525c7d49eSTony Lindgren  * is already enabled, or passes along the return value of
88625c7d49eSTony Lindgren  * _omap_device_activate().
88725c7d49eSTony Lindgren  */
88825c7d49eSTony Lindgren int omap_device_enable(struct platform_device *pdev)
88925c7d49eSTony Lindgren {
89025c7d49eSTony Lindgren 	int ret;
89125c7d49eSTony Lindgren 	struct omap_device *od;
89225c7d49eSTony Lindgren 
89325c7d49eSTony Lindgren 	od = to_omap_device(pdev);
89425c7d49eSTony Lindgren 
89525c7d49eSTony Lindgren 	if (od->_state == OMAP_DEVICE_STATE_ENABLED) {
89625c7d49eSTony Lindgren 		dev_warn(&pdev->dev,
89725c7d49eSTony Lindgren 			 "omap_device: %s() called from invalid state %d\n",
89825c7d49eSTony Lindgren 			 __func__, od->_state);
89925c7d49eSTony Lindgren 		return -EINVAL;
90025c7d49eSTony Lindgren 	}
90125c7d49eSTony Lindgren 
90225c7d49eSTony Lindgren 	/* Enable everything if we're enabling this device from scratch */
90325c7d49eSTony Lindgren 	if (od->_state == OMAP_DEVICE_STATE_UNKNOWN)
90425c7d49eSTony Lindgren 		od->pm_lat_level = od->pm_lats_cnt;
90525c7d49eSTony Lindgren 
90625c7d49eSTony Lindgren 	ret = _omap_device_activate(od, IGNORE_WAKEUP_LAT);
90725c7d49eSTony Lindgren 
90825c7d49eSTony Lindgren 	od->dev_wakeup_lat = 0;
90925c7d49eSTony Lindgren 	od->_dev_wakeup_lat_limit = UINT_MAX;
91025c7d49eSTony Lindgren 	od->_state = OMAP_DEVICE_STATE_ENABLED;
91125c7d49eSTony Lindgren 
91225c7d49eSTony Lindgren 	return ret;
91325c7d49eSTony Lindgren }
91425c7d49eSTony Lindgren 
91525c7d49eSTony Lindgren /**
91625c7d49eSTony Lindgren  * omap_device_idle - idle an omap_device
91725c7d49eSTony Lindgren  * @od: struct omap_device * to idle
91825c7d49eSTony Lindgren  *
91925c7d49eSTony Lindgren  * Idle omap_device @od by calling as many .deactivate_func() entries
92025c7d49eSTony Lindgren  * in the omap_device's pm_lats table as is possible without exceeding
92125c7d49eSTony Lindgren  * the device's maximum wakeup latency limit, pm_lat_limit.  Device
92225c7d49eSTony Lindgren  * drivers should call this function (through platform_data function
92325c7d49eSTony Lindgren  * pointers) where they would normally disable clocks after operations
92425c7d49eSTony Lindgren  * complete, etc..  Returns -EINVAL if the omap_device is not
92525c7d49eSTony Lindgren  * currently enabled, or passes along the return value of
92625c7d49eSTony Lindgren  * _omap_device_deactivate().
92725c7d49eSTony Lindgren  */
92825c7d49eSTony Lindgren int omap_device_idle(struct platform_device *pdev)
92925c7d49eSTony Lindgren {
93025c7d49eSTony Lindgren 	int ret;
93125c7d49eSTony Lindgren 	struct omap_device *od;
93225c7d49eSTony Lindgren 
93325c7d49eSTony Lindgren 	od = to_omap_device(pdev);
93425c7d49eSTony Lindgren 
93525c7d49eSTony Lindgren 	if (od->_state != OMAP_DEVICE_STATE_ENABLED) {
93625c7d49eSTony Lindgren 		dev_warn(&pdev->dev,
93725c7d49eSTony Lindgren 			 "omap_device: %s() called from invalid state %d\n",
93825c7d49eSTony Lindgren 			 __func__, od->_state);
93925c7d49eSTony Lindgren 		return -EINVAL;
94025c7d49eSTony Lindgren 	}
94125c7d49eSTony Lindgren 
94225c7d49eSTony Lindgren 	ret = _omap_device_deactivate(od, USE_WAKEUP_LAT);
94325c7d49eSTony Lindgren 
94425c7d49eSTony Lindgren 	od->_state = OMAP_DEVICE_STATE_IDLE;
94525c7d49eSTony Lindgren 
94625c7d49eSTony Lindgren 	return ret;
94725c7d49eSTony Lindgren }
94825c7d49eSTony Lindgren 
94925c7d49eSTony Lindgren /**
95025c7d49eSTony Lindgren  * omap_device_shutdown - shut down an omap_device
95125c7d49eSTony Lindgren  * @od: struct omap_device * to shut down
95225c7d49eSTony Lindgren  *
95325c7d49eSTony Lindgren  * Shut down omap_device @od by calling all .deactivate_func() entries
95425c7d49eSTony Lindgren  * in the omap_device's pm_lats table and then shutting down all of
95525c7d49eSTony Lindgren  * the underlying omap_hwmods.  Used when a device is being "removed"
95625c7d49eSTony Lindgren  * or a device driver is being unloaded.  Returns -EINVAL if the
95725c7d49eSTony Lindgren  * omap_device is not currently enabled or idle, or passes along the
95825c7d49eSTony Lindgren  * return value of _omap_device_deactivate().
95925c7d49eSTony Lindgren  */
96025c7d49eSTony Lindgren int omap_device_shutdown(struct platform_device *pdev)
96125c7d49eSTony Lindgren {
96225c7d49eSTony Lindgren 	int ret, i;
96325c7d49eSTony Lindgren 	struct omap_device *od;
96425c7d49eSTony Lindgren 
96525c7d49eSTony Lindgren 	od = to_omap_device(pdev);
96625c7d49eSTony Lindgren 
96725c7d49eSTony Lindgren 	if (od->_state != OMAP_DEVICE_STATE_ENABLED &&
96825c7d49eSTony Lindgren 	    od->_state != OMAP_DEVICE_STATE_IDLE) {
96925c7d49eSTony Lindgren 		dev_warn(&pdev->dev,
97025c7d49eSTony Lindgren 			 "omap_device: %s() called from invalid state %d\n",
97125c7d49eSTony Lindgren 			 __func__, od->_state);
97225c7d49eSTony Lindgren 		return -EINVAL;
97325c7d49eSTony Lindgren 	}
97425c7d49eSTony Lindgren 
97525c7d49eSTony Lindgren 	ret = _omap_device_deactivate(od, IGNORE_WAKEUP_LAT);
97625c7d49eSTony Lindgren 
97725c7d49eSTony Lindgren 	for (i = 0; i < od->hwmods_cnt; i++)
97825c7d49eSTony Lindgren 		omap_hwmod_shutdown(od->hwmods[i]);
97925c7d49eSTony Lindgren 
98025c7d49eSTony Lindgren 	od->_state = OMAP_DEVICE_STATE_SHUTDOWN;
98125c7d49eSTony Lindgren 
98225c7d49eSTony Lindgren 	return ret;
98325c7d49eSTony Lindgren }
98425c7d49eSTony Lindgren 
98525c7d49eSTony Lindgren /**
98625c7d49eSTony Lindgren  * omap_device_assert_hardreset - set a device's hardreset line
98725c7d49eSTony Lindgren  * @pdev: struct platform_device * to reset
98825c7d49eSTony Lindgren  * @name: const char * name of the reset line
98925c7d49eSTony Lindgren  *
99025c7d49eSTony Lindgren  * Set the hardreset line identified by @name on the IP blocks
99125c7d49eSTony Lindgren  * associated with the hwmods backing the platform_device @pdev.  All
99225c7d49eSTony Lindgren  * of the hwmods associated with @pdev must have the same hardreset
99325c7d49eSTony Lindgren  * line linked to them for this to work.  Passes along the return value
99425c7d49eSTony Lindgren  * of omap_hwmod_assert_hardreset() in the event of any failure, or
99525c7d49eSTony Lindgren  * returns 0 upon success.
99625c7d49eSTony Lindgren  */
99725c7d49eSTony Lindgren int omap_device_assert_hardreset(struct platform_device *pdev, const char *name)
99825c7d49eSTony Lindgren {
99925c7d49eSTony Lindgren 	struct omap_device *od = to_omap_device(pdev);
100025c7d49eSTony Lindgren 	int ret = 0;
100125c7d49eSTony Lindgren 	int i;
100225c7d49eSTony Lindgren 
100325c7d49eSTony Lindgren 	for (i = 0; i < od->hwmods_cnt; i++) {
100425c7d49eSTony Lindgren 		ret = omap_hwmod_assert_hardreset(od->hwmods[i], name);
100525c7d49eSTony Lindgren 		if (ret)
100625c7d49eSTony Lindgren 			break;
100725c7d49eSTony Lindgren 	}
100825c7d49eSTony Lindgren 
100925c7d49eSTony Lindgren 	return ret;
101025c7d49eSTony Lindgren }
101125c7d49eSTony Lindgren 
101225c7d49eSTony Lindgren /**
101325c7d49eSTony Lindgren  * omap_device_deassert_hardreset - release a device's hardreset line
101425c7d49eSTony Lindgren  * @pdev: struct platform_device * to reset
101525c7d49eSTony Lindgren  * @name: const char * name of the reset line
101625c7d49eSTony Lindgren  *
101725c7d49eSTony Lindgren  * Release the hardreset line identified by @name on the IP blocks
101825c7d49eSTony Lindgren  * associated with the hwmods backing the platform_device @pdev.  All
101925c7d49eSTony Lindgren  * of the hwmods associated with @pdev must have the same hardreset
102025c7d49eSTony Lindgren  * line linked to them for this to work.  Passes along the return
102125c7d49eSTony Lindgren  * value of omap_hwmod_deassert_hardreset() in the event of any
102225c7d49eSTony Lindgren  * failure, or returns 0 upon success.
102325c7d49eSTony Lindgren  */
102425c7d49eSTony Lindgren int omap_device_deassert_hardreset(struct platform_device *pdev,
102525c7d49eSTony Lindgren 				   const char *name)
102625c7d49eSTony Lindgren {
102725c7d49eSTony Lindgren 	struct omap_device *od = to_omap_device(pdev);
102825c7d49eSTony Lindgren 	int ret = 0;
102925c7d49eSTony Lindgren 	int i;
103025c7d49eSTony Lindgren 
103125c7d49eSTony Lindgren 	for (i = 0; i < od->hwmods_cnt; i++) {
103225c7d49eSTony Lindgren 		ret = omap_hwmod_deassert_hardreset(od->hwmods[i], name);
103325c7d49eSTony Lindgren 		if (ret)
103425c7d49eSTony Lindgren 			break;
103525c7d49eSTony Lindgren 	}
103625c7d49eSTony Lindgren 
103725c7d49eSTony Lindgren 	return ret;
103825c7d49eSTony Lindgren }
103925c7d49eSTony Lindgren 
104025c7d49eSTony Lindgren /**
104125c7d49eSTony Lindgren  * omap_device_align_pm_lat - activate/deactivate device to match wakeup lat lim
104225c7d49eSTony Lindgren  * @od: struct omap_device *
104325c7d49eSTony Lindgren  *
104425c7d49eSTony Lindgren  * When a device's maximum wakeup latency limit changes, call some of
104525c7d49eSTony Lindgren  * the .activate_func or .deactivate_func function pointers in the
104625c7d49eSTony Lindgren  * omap_device's pm_lats array to ensure that the device's maximum
104725c7d49eSTony Lindgren  * wakeup latency is less than or equal to the new latency limit.
104825c7d49eSTony Lindgren  * Intended to be called by OMAP PM code whenever a device's maximum
104925c7d49eSTony Lindgren  * wakeup latency limit changes (e.g., via
105025c7d49eSTony Lindgren  * omap_pm_set_dev_wakeup_lat()).  Returns 0 if nothing needs to be
105125c7d49eSTony Lindgren  * done (e.g., if the omap_device is not currently idle, or if the
105225c7d49eSTony Lindgren  * wakeup latency is already current with the new limit) or passes
105325c7d49eSTony Lindgren  * along the return value of _omap_device_deactivate() or
105425c7d49eSTony Lindgren  * _omap_device_activate().
105525c7d49eSTony Lindgren  */
105625c7d49eSTony Lindgren int omap_device_align_pm_lat(struct platform_device *pdev,
105725c7d49eSTony Lindgren 			     u32 new_wakeup_lat_limit)
105825c7d49eSTony Lindgren {
105925c7d49eSTony Lindgren 	int ret = -EINVAL;
106025c7d49eSTony Lindgren 	struct omap_device *od;
106125c7d49eSTony Lindgren 
106225c7d49eSTony Lindgren 	od = to_omap_device(pdev);
106325c7d49eSTony Lindgren 
106425c7d49eSTony Lindgren 	if (new_wakeup_lat_limit == od->dev_wakeup_lat)
106525c7d49eSTony Lindgren 		return 0;
106625c7d49eSTony Lindgren 
106725c7d49eSTony Lindgren 	od->_dev_wakeup_lat_limit = new_wakeup_lat_limit;
106825c7d49eSTony Lindgren 
106925c7d49eSTony Lindgren 	if (od->_state != OMAP_DEVICE_STATE_IDLE)
107025c7d49eSTony Lindgren 		return 0;
107125c7d49eSTony Lindgren 	else if (new_wakeup_lat_limit > od->dev_wakeup_lat)
107225c7d49eSTony Lindgren 		ret = _omap_device_deactivate(od, USE_WAKEUP_LAT);
107325c7d49eSTony Lindgren 	else if (new_wakeup_lat_limit < od->dev_wakeup_lat)
107425c7d49eSTony Lindgren 		ret = _omap_device_activate(od, USE_WAKEUP_LAT);
107525c7d49eSTony Lindgren 
107625c7d49eSTony Lindgren 	return ret;
107725c7d49eSTony Lindgren }
107825c7d49eSTony Lindgren 
107925c7d49eSTony Lindgren /**
108025c7d49eSTony Lindgren  * omap_device_get_pwrdm - return the powerdomain * associated with @od
108125c7d49eSTony Lindgren  * @od: struct omap_device *
108225c7d49eSTony Lindgren  *
108325c7d49eSTony Lindgren  * Return the powerdomain associated with the first underlying
108425c7d49eSTony Lindgren  * omap_hwmod for this omap_device.  Intended for use by core OMAP PM
108525c7d49eSTony Lindgren  * code.  Returns NULL on error or a struct powerdomain * upon
108625c7d49eSTony Lindgren  * success.
108725c7d49eSTony Lindgren  */
108825c7d49eSTony Lindgren struct powerdomain *omap_device_get_pwrdm(struct omap_device *od)
108925c7d49eSTony Lindgren {
109025c7d49eSTony Lindgren 	/*
109125c7d49eSTony Lindgren 	 * XXX Assumes that all omap_hwmod powerdomains are identical.
109225c7d49eSTony Lindgren 	 * This may not necessarily be true.  There should be a sanity
109325c7d49eSTony Lindgren 	 * check in here to WARN() if any difference appears.
109425c7d49eSTony Lindgren 	 */
109525c7d49eSTony Lindgren 	if (!od->hwmods_cnt)
109625c7d49eSTony Lindgren 		return NULL;
109725c7d49eSTony Lindgren 
109825c7d49eSTony Lindgren 	return omap_hwmod_get_pwrdm(od->hwmods[0]);
109925c7d49eSTony Lindgren }
110025c7d49eSTony Lindgren 
110125c7d49eSTony Lindgren /**
110225c7d49eSTony Lindgren  * omap_device_get_mpu_rt_va - return the MPU's virtual addr for the hwmod base
110325c7d49eSTony Lindgren  * @od: struct omap_device *
110425c7d49eSTony Lindgren  *
110525c7d49eSTony Lindgren  * Return the MPU's virtual address for the base of the hwmod, from
110625c7d49eSTony Lindgren  * the ioremap() that the hwmod code does.  Only valid if there is one
110725c7d49eSTony Lindgren  * hwmod associated with this device.  Returns NULL if there are zero
110825c7d49eSTony Lindgren  * or more than one hwmods associated with this omap_device;
110925c7d49eSTony Lindgren  * otherwise, passes along the return value from
111025c7d49eSTony Lindgren  * omap_hwmod_get_mpu_rt_va().
111125c7d49eSTony Lindgren  */
111225c7d49eSTony Lindgren void __iomem *omap_device_get_rt_va(struct omap_device *od)
111325c7d49eSTony Lindgren {
111425c7d49eSTony Lindgren 	if (od->hwmods_cnt != 1)
111525c7d49eSTony Lindgren 		return NULL;
111625c7d49eSTony Lindgren 
111725c7d49eSTony Lindgren 	return omap_hwmod_get_mpu_rt_va(od->hwmods[0]);
111825c7d49eSTony Lindgren }
111925c7d49eSTony Lindgren 
112025c7d49eSTony Lindgren /**
112125c7d49eSTony Lindgren  * omap_device_get_by_hwmod_name() - convert a hwmod name to
112225c7d49eSTony Lindgren  * device pointer.
112325c7d49eSTony Lindgren  * @oh_name: name of the hwmod device
112425c7d49eSTony Lindgren  *
112525c7d49eSTony Lindgren  * Returns back a struct device * pointer associated with a hwmod
112625c7d49eSTony Lindgren  * device represented by a hwmod_name
112725c7d49eSTony Lindgren  */
112825c7d49eSTony Lindgren struct device *omap_device_get_by_hwmod_name(const char *oh_name)
112925c7d49eSTony Lindgren {
113025c7d49eSTony Lindgren 	struct omap_hwmod *oh;
113125c7d49eSTony Lindgren 
113225c7d49eSTony Lindgren 	if (!oh_name) {
113325c7d49eSTony Lindgren 		WARN(1, "%s: no hwmod name!\n", __func__);
113425c7d49eSTony Lindgren 		return ERR_PTR(-EINVAL);
113525c7d49eSTony Lindgren 	}
113625c7d49eSTony Lindgren 
113725c7d49eSTony Lindgren 	oh = omap_hwmod_lookup(oh_name);
113825c7d49eSTony Lindgren 	if (IS_ERR_OR_NULL(oh)) {
113925c7d49eSTony Lindgren 		WARN(1, "%s: no hwmod for %s\n", __func__,
114025c7d49eSTony Lindgren 			oh_name);
114125c7d49eSTony Lindgren 		return ERR_PTR(oh ? PTR_ERR(oh) : -ENODEV);
114225c7d49eSTony Lindgren 	}
114325c7d49eSTony Lindgren 	if (IS_ERR_OR_NULL(oh->od)) {
114425c7d49eSTony Lindgren 		WARN(1, "%s: no omap_device for %s\n", __func__,
114525c7d49eSTony Lindgren 			oh_name);
114625c7d49eSTony Lindgren 		return ERR_PTR(oh->od ? PTR_ERR(oh->od) : -ENODEV);
114725c7d49eSTony Lindgren 	}
114825c7d49eSTony Lindgren 
114925c7d49eSTony Lindgren 	if (IS_ERR_OR_NULL(oh->od->pdev))
115025c7d49eSTony Lindgren 		return ERR_PTR(oh->od->pdev ? PTR_ERR(oh->od->pdev) : -ENODEV);
115125c7d49eSTony Lindgren 
115225c7d49eSTony Lindgren 	return &oh->od->pdev->dev;
115325c7d49eSTony Lindgren }
115425c7d49eSTony Lindgren EXPORT_SYMBOL(omap_device_get_by_hwmod_name);
115525c7d49eSTony Lindgren 
115625c7d49eSTony Lindgren /*
115725c7d49eSTony Lindgren  * Public functions intended for use in omap_device_pm_latency
115825c7d49eSTony Lindgren  * .activate_func and .deactivate_func function pointers
115925c7d49eSTony Lindgren  */
116025c7d49eSTony Lindgren 
116125c7d49eSTony Lindgren /**
116225c7d49eSTony Lindgren  * omap_device_enable_hwmods - call omap_hwmod_enable() on all hwmods
116325c7d49eSTony Lindgren  * @od: struct omap_device *od
116425c7d49eSTony Lindgren  *
116525c7d49eSTony Lindgren  * Enable all underlying hwmods.  Returns 0.
116625c7d49eSTony Lindgren  */
116725c7d49eSTony Lindgren int omap_device_enable_hwmods(struct omap_device *od)
116825c7d49eSTony Lindgren {
116925c7d49eSTony Lindgren 	int i;
117025c7d49eSTony Lindgren 
117125c7d49eSTony Lindgren 	for (i = 0; i < od->hwmods_cnt; i++)
117225c7d49eSTony Lindgren 		omap_hwmod_enable(od->hwmods[i]);
117325c7d49eSTony Lindgren 
117425c7d49eSTony Lindgren 	/* XXX pass along return value here? */
117525c7d49eSTony Lindgren 	return 0;
117625c7d49eSTony Lindgren }
117725c7d49eSTony Lindgren 
117825c7d49eSTony Lindgren /**
117925c7d49eSTony Lindgren  * omap_device_idle_hwmods - call omap_hwmod_idle() on all hwmods
118025c7d49eSTony Lindgren  * @od: struct omap_device *od
118125c7d49eSTony Lindgren  *
118225c7d49eSTony Lindgren  * Idle all underlying hwmods.  Returns 0.
118325c7d49eSTony Lindgren  */
118425c7d49eSTony Lindgren int omap_device_idle_hwmods(struct omap_device *od)
118525c7d49eSTony Lindgren {
118625c7d49eSTony Lindgren 	int i;
118725c7d49eSTony Lindgren 
118825c7d49eSTony Lindgren 	for (i = 0; i < od->hwmods_cnt; i++)
118925c7d49eSTony Lindgren 		omap_hwmod_idle(od->hwmods[i]);
119025c7d49eSTony Lindgren 
119125c7d49eSTony Lindgren 	/* XXX pass along return value here? */
119225c7d49eSTony Lindgren 	return 0;
119325c7d49eSTony Lindgren }
119425c7d49eSTony Lindgren 
119525c7d49eSTony Lindgren /**
119625c7d49eSTony Lindgren  * omap_device_disable_clocks - disable all main and interface clocks
119725c7d49eSTony Lindgren  * @od: struct omap_device *od
119825c7d49eSTony Lindgren  *
119925c7d49eSTony Lindgren  * Disable the main functional clock and interface clock for all of the
120025c7d49eSTony Lindgren  * omap_hwmods associated with the omap_device.  Returns 0.
120125c7d49eSTony Lindgren  */
120225c7d49eSTony Lindgren int omap_device_disable_clocks(struct omap_device *od)
120325c7d49eSTony Lindgren {
120425c7d49eSTony Lindgren 	int i;
120525c7d49eSTony Lindgren 
120625c7d49eSTony Lindgren 	for (i = 0; i < od->hwmods_cnt; i++)
120725c7d49eSTony Lindgren 		omap_hwmod_disable_clocks(od->hwmods[i]);
120825c7d49eSTony Lindgren 
120925c7d49eSTony Lindgren 	/* XXX pass along return value here? */
121025c7d49eSTony Lindgren 	return 0;
121125c7d49eSTony Lindgren }
121225c7d49eSTony Lindgren 
121325c7d49eSTony Lindgren /**
121425c7d49eSTony Lindgren  * omap_device_enable_clocks - enable all main and interface clocks
121525c7d49eSTony Lindgren  * @od: struct omap_device *od
121625c7d49eSTony Lindgren  *
121725c7d49eSTony Lindgren  * Enable the main functional clock and interface clock for all of the
121825c7d49eSTony Lindgren  * omap_hwmods associated with the omap_device.  Returns 0.
121925c7d49eSTony Lindgren  */
122025c7d49eSTony Lindgren int omap_device_enable_clocks(struct omap_device *od)
122125c7d49eSTony Lindgren {
122225c7d49eSTony Lindgren 	int i;
122325c7d49eSTony Lindgren 
122425c7d49eSTony Lindgren 	for (i = 0; i < od->hwmods_cnt; i++)
122525c7d49eSTony Lindgren 		omap_hwmod_enable_clocks(od->hwmods[i]);
122625c7d49eSTony Lindgren 
122725c7d49eSTony Lindgren 	/* XXX pass along return value here? */
122825c7d49eSTony Lindgren 	return 0;
122925c7d49eSTony Lindgren }
123025c7d49eSTony Lindgren 
123125c7d49eSTony Lindgren static struct notifier_block platform_nb = {
123225c7d49eSTony Lindgren 	.notifier_call = _omap_device_notifier_call,
123325c7d49eSTony Lindgren };
123425c7d49eSTony Lindgren 
123525c7d49eSTony Lindgren static int __init omap_device_init(void)
123625c7d49eSTony Lindgren {
123725c7d49eSTony Lindgren 	bus_register_notifier(&platform_bus_type, &platform_nb);
123825c7d49eSTony Lindgren 	return 0;
123925c7d49eSTony Lindgren }
124025c7d49eSTony Lindgren core_initcall(omap_device_init);
124125c7d49eSTony Lindgren 
124225c7d49eSTony Lindgren /**
124325c7d49eSTony Lindgren  * omap_device_late_idle - idle devices without drivers
124425c7d49eSTony Lindgren  * @dev: struct device * associated with omap_device
124525c7d49eSTony Lindgren  * @data: unused
124625c7d49eSTony Lindgren  *
124725c7d49eSTony Lindgren  * Check the driver bound status of this device, and idle it
124825c7d49eSTony Lindgren  * if there is no driver attached.
124925c7d49eSTony Lindgren  */
125025c7d49eSTony Lindgren static int __init omap_device_late_idle(struct device *dev, void *data)
125125c7d49eSTony Lindgren {
125225c7d49eSTony Lindgren 	struct platform_device *pdev = to_platform_device(dev);
125325c7d49eSTony Lindgren 	struct omap_device *od = to_omap_device(pdev);
125425c7d49eSTony Lindgren 
125525c7d49eSTony Lindgren 	if (!od)
125625c7d49eSTony Lindgren 		return 0;
125725c7d49eSTony Lindgren 
125825c7d49eSTony Lindgren 	/*
125925c7d49eSTony Lindgren 	 * If omap_device state is enabled, but has no driver bound,
126025c7d49eSTony Lindgren 	 * idle it.
126125c7d49eSTony Lindgren 	 */
126225c7d49eSTony Lindgren 	if (od->_driver_status != BUS_NOTIFY_BOUND_DRIVER) {
126325c7d49eSTony Lindgren 		if (od->_state == OMAP_DEVICE_STATE_ENABLED) {
126425c7d49eSTony Lindgren 			dev_warn(dev, "%s: enabled but no driver.  Idling\n",
126525c7d49eSTony Lindgren 				 __func__);
126625c7d49eSTony Lindgren 			omap_device_idle(pdev);
126725c7d49eSTony Lindgren 		}
126825c7d49eSTony Lindgren 	}
126925c7d49eSTony Lindgren 
127025c7d49eSTony Lindgren 	return 0;
127125c7d49eSTony Lindgren }
127225c7d49eSTony Lindgren 
127325c7d49eSTony Lindgren static int __init omap_device_late_init(void)
127425c7d49eSTony Lindgren {
127525c7d49eSTony Lindgren 	bus_for_each_dev(&platform_bus_type, NULL, NULL, omap_device_late_idle);
127625c7d49eSTony Lindgren 	return 0;
127725c7d49eSTony Lindgren }
127825c7d49eSTony Lindgren late_initcall(omap_device_late_init);
1279