1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
225c7d49eSTony Lindgren /*
325c7d49eSTony Lindgren  * omap_device implementation
425c7d49eSTony Lindgren  *
525c7d49eSTony Lindgren  * Copyright (C) 2009-2010 Nokia Corporation
625c7d49eSTony Lindgren  * Paul Walmsley, Kevin Hilman
725c7d49eSTony Lindgren  *
825c7d49eSTony Lindgren  * Developed in collaboration with (alphabetical order): Benoit
925c7d49eSTony Lindgren  * Cousson, Thara Gopinath, Tony Lindgren, Rajendra Nayak, Vikram
1025c7d49eSTony Lindgren  * Pandita, Sakari Poussa, Anand Sawant, Santosh Shilimkar, Richard
1125c7d49eSTony Lindgren  * Woodruff
1225c7d49eSTony Lindgren  *
1325c7d49eSTony Lindgren  * This code provides a consistent interface for OMAP device drivers
1425c7d49eSTony Lindgren  * to control power management and interconnect properties of their
1525c7d49eSTony Lindgren  * devices.
1625c7d49eSTony Lindgren  *
17c1d1cd59SPaul Walmsley  * In the medium- to long-term, this code should be implemented as a
18c1d1cd59SPaul Walmsley  * proper omap_bus/omap_device in Linux, no more platform_data func
19c1d1cd59SPaul Walmsley  * pointers
2025c7d49eSTony Lindgren  */
2125c7d49eSTony Lindgren #undef DEBUG
2225c7d49eSTony Lindgren 
2325c7d49eSTony Lindgren #include <linux/kernel.h>
2425c7d49eSTony Lindgren #include <linux/platform_device.h>
2525c7d49eSTony Lindgren #include <linux/slab.h>
2625c7d49eSTony Lindgren #include <linux/err.h>
2725c7d49eSTony Lindgren #include <linux/io.h>
2825c7d49eSTony Lindgren #include <linux/clk.h>
2925c7d49eSTony Lindgren #include <linux/clkdev.h>
30989561deSTomeu Vizoso #include <linux/pm_domain.h>
3125c7d49eSTony Lindgren #include <linux/pm_runtime.h>
3225c7d49eSTony Lindgren #include <linux/of.h>
33d85a2d61STony Lindgren #include <linux/of_address.h>
34d85a2d61STony Lindgren #include <linux/of_irq.h>
3525c7d49eSTony Lindgren #include <linux/notifier.h>
3625c7d49eSTony Lindgren 
37dad12d11STony Lindgren #include "common.h"
38b76c8b19STony Lindgren #include "soc.h"
3925c7d49eSTony Lindgren #include "omap_device.h"
402a296c8fSTony Lindgren #include "omap_hwmod.h"
4125c7d49eSTony Lindgren 
4225c7d49eSTony Lindgren /* Private functions */
4325c7d49eSTony Lindgren 
4425c7d49eSTony Lindgren static void _add_clkdev(struct omap_device *od, const char *clk_alias,
4525c7d49eSTony Lindgren 		       const char *clk_name)
4625c7d49eSTony Lindgren {
4725c7d49eSTony Lindgren 	struct clk *r;
481ca90bd4SRussell King 	int rc;
4925c7d49eSTony Lindgren 
5025c7d49eSTony Lindgren 	if (!clk_alias || !clk_name)
5125c7d49eSTony Lindgren 		return;
5225c7d49eSTony Lindgren 
5325c7d49eSTony Lindgren 	dev_dbg(&od->pdev->dev, "Creating %s -> %s\n", clk_alias, clk_name);
5425c7d49eSTony Lindgren 
5525c7d49eSTony Lindgren 	r = clk_get_sys(dev_name(&od->pdev->dev), clk_alias);
5625c7d49eSTony Lindgren 	if (!IS_ERR(r)) {
579a02ae4eSMarkus Pargmann 		dev_dbg(&od->pdev->dev,
5825c7d49eSTony Lindgren 			 "alias %s already exists\n", clk_alias);
5925c7d49eSTony Lindgren 		clk_put(r);
6025c7d49eSTony Lindgren 		return;
6125c7d49eSTony Lindgren 	}
6225c7d49eSTony Lindgren 
6359dcfc48STero Kristo 	r = clk_get_sys(NULL, clk_name);
6459dcfc48STero Kristo 
651aa8f0cbSTony Lindgren 	if (IS_ERR(r)) {
6659dcfc48STero Kristo 		struct of_phandle_args clkspec;
6759dcfc48STero Kristo 
6859dcfc48STero Kristo 		clkspec.np = of_find_node_by_name(NULL, clk_name);
6959dcfc48STero Kristo 
7059dcfc48STero Kristo 		r = of_clk_get_from_provider(&clkspec);
7159dcfc48STero Kristo 
7259dcfc48STero Kristo 		rc = clk_register_clkdev(r, clk_alias,
7359dcfc48STero Kristo 					 dev_name(&od->pdev->dev));
7459dcfc48STero Kristo 	} else {
7559dcfc48STero Kristo 		rc = clk_add_alias(clk_alias, dev_name(&od->pdev->dev),
7659dcfc48STero Kristo 				   clk_name, NULL);
7759dcfc48STero Kristo 	}
7859dcfc48STero Kristo 
791ca90bd4SRussell King 	if (rc) {
801ca90bd4SRussell King 		if (rc == -ENODEV || rc == -ENOMEM)
8125c7d49eSTony Lindgren 			dev_err(&od->pdev->dev,
8225c7d49eSTony Lindgren 				"clkdev_alloc for %s failed\n", clk_alias);
831ca90bd4SRussell King 		else
841ca90bd4SRussell King 			dev_err(&od->pdev->dev,
851ca90bd4SRussell King 				"clk_get for %s failed\n", clk_name);
8625c7d49eSTony Lindgren 	}
8725c7d49eSTony Lindgren }
8825c7d49eSTony Lindgren 
8925c7d49eSTony Lindgren /**
9025c7d49eSTony Lindgren  * _add_hwmod_clocks_clkdev - Add clkdev entry for hwmod optional clocks
9125c7d49eSTony Lindgren  * and main clock
9225c7d49eSTony Lindgren  * @od: struct omap_device *od
9325c7d49eSTony Lindgren  * @oh: struct omap_hwmod *oh
9425c7d49eSTony Lindgren  *
9525c7d49eSTony Lindgren  * For the main clock and every optional clock present per hwmod per
9625c7d49eSTony Lindgren  * omap_device, this function adds an entry in the clkdev table of the
9725c7d49eSTony Lindgren  * form <dev-id=dev_name, con-id=role> if it does not exist already.
9825c7d49eSTony Lindgren  *
9925c7d49eSTony Lindgren  * The function is called from inside omap_device_build_ss(), after
10025c7d49eSTony Lindgren  * omap_device_register.
10125c7d49eSTony Lindgren  *
10225c7d49eSTony Lindgren  * This allows drivers to get a pointer to its optional clocks based on its role
10325c7d49eSTony Lindgren  * by calling clk_get(<dev*>, <role>).
10425c7d49eSTony Lindgren  * In the case of the main clock, a "fck" alias is used.
10525c7d49eSTony Lindgren  *
10625c7d49eSTony Lindgren  * No return value.
10725c7d49eSTony Lindgren  */
10825c7d49eSTony Lindgren static void _add_hwmod_clocks_clkdev(struct omap_device *od,
10925c7d49eSTony Lindgren 				     struct omap_hwmod *oh)
11025c7d49eSTony Lindgren {
11125c7d49eSTony Lindgren 	int i;
11225c7d49eSTony Lindgren 
11325c7d49eSTony Lindgren 	_add_clkdev(od, "fck", oh->main_clk);
11425c7d49eSTony Lindgren 
11525c7d49eSTony Lindgren 	for (i = 0; i < oh->opt_clks_cnt; i++)
11625c7d49eSTony Lindgren 		_add_clkdev(od, oh->opt_clks[i].role, oh->opt_clks[i].clk);
11725c7d49eSTony Lindgren }
11825c7d49eSTony Lindgren 
11925c7d49eSTony Lindgren 
12025c7d49eSTony Lindgren /**
12125c7d49eSTony Lindgren  * omap_device_build_from_dt - build an omap_device with multiple hwmods
1227b6560b4SBen Dooks (Codethink)  * @pdev: The platform device to update.
12325c7d49eSTony Lindgren  *
12425c7d49eSTony Lindgren  * Function for building an omap_device already registered from device-tree
12525c7d49eSTony Lindgren  *
12625c7d49eSTony Lindgren  * Returns 0 or PTR_ERR() on error.
12725c7d49eSTony Lindgren  */
12825c7d49eSTony Lindgren static int omap_device_build_from_dt(struct platform_device *pdev)
12925c7d49eSTony Lindgren {
13025c7d49eSTony Lindgren 	struct omap_hwmod **hwmods;
13125c7d49eSTony Lindgren 	struct omap_device *od;
13225c7d49eSTony Lindgren 	struct omap_hwmod *oh;
13325c7d49eSTony Lindgren 	struct device_node *node = pdev->dev.of_node;
134695eea3dSTony Lindgren 	struct resource res;
13525c7d49eSTony Lindgren 	const char *oh_name;
13625c7d49eSTony Lindgren 	int oh_cnt, i, ret = 0;
13771941002STony Lindgren 	bool device_active = false, skip_pm_domain = false;
13825c7d49eSTony Lindgren 
13925c7d49eSTony Lindgren 	oh_cnt = of_property_count_strings(node, "ti,hwmods");
140c48cd659SRussell King 	if (oh_cnt <= 0) {
14125c7d49eSTony Lindgren 		dev_dbg(&pdev->dev, "No 'hwmods' to build omap_device\n");
14225c7d49eSTony Lindgren 		return -ENODEV;
14325c7d49eSTony Lindgren 	}
14425c7d49eSTony Lindgren 
14571941002STony Lindgren 	/* SDMA still needs special handling for omap_device_build() */
14671941002STony Lindgren 	ret = of_property_read_string_index(node, "ti,hwmods", 0, &oh_name);
14771941002STony Lindgren 	if (!ret && (!strncmp("dma_system", oh_name, 10) ||
14871941002STony Lindgren 		     !strncmp("dma", oh_name, 3)))
14971941002STony Lindgren 		skip_pm_domain = true;
15071941002STony Lindgren 
151695eea3dSTony Lindgren 	/* Use ti-sysc driver instead of omap_device? */
15271941002STony Lindgren 	if (!skip_pm_domain &&
15371941002STony Lindgren 	    !omap_hwmod_parse_module_range(NULL, node, &res))
154695eea3dSTony Lindgren 		return -ENODEV;
155695eea3dSTony Lindgren 
1566396bb22SKees Cook 	hwmods = kcalloc(oh_cnt, sizeof(struct omap_hwmod *), GFP_KERNEL);
15725c7d49eSTony Lindgren 	if (!hwmods) {
15825c7d49eSTony Lindgren 		ret = -ENOMEM;
15925c7d49eSTony Lindgren 		goto odbfd_exit;
16025c7d49eSTony Lindgren 	}
16125c7d49eSTony Lindgren 
16225c7d49eSTony Lindgren 	for (i = 0; i < oh_cnt; i++) {
16325c7d49eSTony Lindgren 		of_property_read_string_index(node, "ti,hwmods", i, &oh_name);
16425c7d49eSTony Lindgren 		oh = omap_hwmod_lookup(oh_name);
16525c7d49eSTony Lindgren 		if (!oh) {
16625c7d49eSTony Lindgren 			dev_err(&pdev->dev, "Cannot lookup hwmod '%s'\n",
16725c7d49eSTony Lindgren 				oh_name);
16825c7d49eSTony Lindgren 			ret = -EINVAL;
16925c7d49eSTony Lindgren 			goto odbfd_exit1;
17025c7d49eSTony Lindgren 		}
17125c7d49eSTony Lindgren 		hwmods[i] = oh;
1727268032dSRajendra Nayak 		if (oh->flags & HWMOD_INIT_NO_IDLE)
1737268032dSRajendra Nayak 			device_active = true;
17425c7d49eSTony Lindgren 	}
17525c7d49eSTony Lindgren 
176c1d1cd59SPaul Walmsley 	od = omap_device_alloc(pdev, hwmods, oh_cnt);
1774cf9cf89SWei Yongjun 	if (IS_ERR(od)) {
17825c7d49eSTony Lindgren 		dev_err(&pdev->dev, "Cannot allocate omap_device for :%s\n",
17925c7d49eSTony Lindgren 			oh_name);
18025c7d49eSTony Lindgren 		ret = PTR_ERR(od);
18125c7d49eSTony Lindgren 		goto odbfd_exit1;
18225c7d49eSTony Lindgren 	}
18325c7d49eSTony Lindgren 
18425c7d49eSTony Lindgren 	/* Fix up missing resource names */
18525c7d49eSTony Lindgren 	for (i = 0; i < pdev->num_resources; i++) {
18625c7d49eSTony Lindgren 		struct resource *r = &pdev->resource[i];
18725c7d49eSTony Lindgren 
18825c7d49eSTony Lindgren 		if (r->name == NULL)
18925c7d49eSTony Lindgren 			r->name = dev_name(&pdev->dev);
19025c7d49eSTony Lindgren 	}
19125c7d49eSTony Lindgren 
19271941002STony Lindgren 	if (!skip_pm_domain) {
193989561deSTomeu Vizoso 		dev_pm_domain_set(&pdev->dev, &omap_device_pm_domain);
1947268032dSRajendra Nayak 		if (device_active) {
1957268032dSRajendra Nayak 			omap_device_enable(pdev);
1967268032dSRajendra Nayak 			pm_runtime_set_active(&pdev->dev);
1977268032dSRajendra Nayak 		}
19871941002STony Lindgren 	}
1997268032dSRajendra Nayak 
20025c7d49eSTony Lindgren odbfd_exit1:
20125c7d49eSTony Lindgren 	kfree(hwmods);
20225c7d49eSTony Lindgren odbfd_exit:
203f5c33b07SNishanth Menon 	/* if data/we are at fault.. load up a fail handler */
204f5c33b07SNishanth Menon 	if (ret)
205989561deSTomeu Vizoso 		dev_pm_domain_set(&pdev->dev, &omap_device_fail_pm_domain);
206f5c33b07SNishanth Menon 
20725c7d49eSTony Lindgren 	return ret;
20825c7d49eSTony Lindgren }
20925c7d49eSTony Lindgren 
21025c7d49eSTony Lindgren static int _omap_device_notifier_call(struct notifier_block *nb,
21125c7d49eSTony Lindgren 				      unsigned long event, void *dev)
21225c7d49eSTony Lindgren {
21325c7d49eSTony Lindgren 	struct platform_device *pdev = to_platform_device(dev);
21425c7d49eSTony Lindgren 	struct omap_device *od;
215cf26f113STony Lindgren 	int err;
21625c7d49eSTony Lindgren 
21725c7d49eSTony Lindgren 	switch (event) {
218213fa10dSGrygorii Strashko 	case BUS_NOTIFY_REMOVED_DEVICE:
21925c7d49eSTony Lindgren 		if (pdev->archdata.od)
22025c7d49eSTony Lindgren 			omap_device_delete(pdev->archdata.od);
22125c7d49eSTony Lindgren 		break;
222cf26f113STony Lindgren 	case BUS_NOTIFY_UNBOUND_DRIVER:
223cf26f113STony Lindgren 		od = to_omap_device(pdev);
224cf26f113STony Lindgren 		if (od && (od->_state == OMAP_DEVICE_STATE_ENABLED)) {
225cf26f113STony Lindgren 			dev_info(dev, "enabled after unload, idling\n");
226cf26f113STony Lindgren 			err = omap_device_idle(pdev);
227cf26f113STony Lindgren 			if (err)
228cf26f113STony Lindgren 				dev_err(dev, "failed to idle\n");
229cf26f113STony Lindgren 		}
230cf26f113STony Lindgren 		break;
23104abaf07SDave Gerlach 	case BUS_NOTIFY_BIND_DRIVER:
23204abaf07SDave Gerlach 		od = to_omap_device(pdev);
233*ec76c2eeSAndreas Kemnade 		if (od) {
23404abaf07SDave Gerlach 			od->_driver_status = BUS_NOTIFY_BIND_DRIVER;
235*ec76c2eeSAndreas Kemnade 			if (od->_state == OMAP_DEVICE_STATE_ENABLED &&
236*ec76c2eeSAndreas Kemnade 			    pm_runtime_status_suspended(dev)) {
23704abaf07SDave Gerlach 				pm_runtime_set_active(dev);
23804abaf07SDave Gerlach 			}
239*ec76c2eeSAndreas Kemnade 		}
24004abaf07SDave Gerlach 		break;
24125c7d49eSTony Lindgren 	case BUS_NOTIFY_ADD_DEVICE:
24225c7d49eSTony Lindgren 		if (pdev->dev.of_node)
24325c7d49eSTony Lindgren 			omap_device_build_from_dt(pdev);
244dad12d11STony Lindgren 		omap_auxdata_legacy_init(dev);
245df561f66SGustavo A. R. Silva 		fallthrough;
24625c7d49eSTony Lindgren 	default:
24725c7d49eSTony Lindgren 		od = to_omap_device(pdev);
24825c7d49eSTony Lindgren 		if (od)
24925c7d49eSTony Lindgren 			od->_driver_status = event;
25025c7d49eSTony Lindgren 	}
25125c7d49eSTony Lindgren 
25225c7d49eSTony Lindgren 	return NOTIFY_DONE;
25325c7d49eSTony Lindgren }
25425c7d49eSTony Lindgren 
255c1d1cd59SPaul Walmsley /**
256c1d1cd59SPaul Walmsley  * _omap_device_enable_hwmods - call omap_hwmod_enable() on all hwmods
257c1d1cd59SPaul Walmsley  * @od: struct omap_device *od
258c1d1cd59SPaul Walmsley  *
259c1d1cd59SPaul Walmsley  * Enable all underlying hwmods.  Returns 0.
260c1d1cd59SPaul Walmsley  */
261c1d1cd59SPaul Walmsley static int _omap_device_enable_hwmods(struct omap_device *od)
262c1d1cd59SPaul Walmsley {
2636da23358SPali Rohár 	int ret = 0;
264c1d1cd59SPaul Walmsley 	int i;
265c1d1cd59SPaul Walmsley 
266c1d1cd59SPaul Walmsley 	for (i = 0; i < od->hwmods_cnt; i++)
2676da23358SPali Rohár 		ret |= omap_hwmod_enable(od->hwmods[i]);
268c1d1cd59SPaul Walmsley 
2696da23358SPali Rohár 	return ret;
270c1d1cd59SPaul Walmsley }
271c1d1cd59SPaul Walmsley 
272c1d1cd59SPaul Walmsley /**
273c1d1cd59SPaul Walmsley  * _omap_device_idle_hwmods - call omap_hwmod_idle() on all hwmods
274c1d1cd59SPaul Walmsley  * @od: struct omap_device *od
275c1d1cd59SPaul Walmsley  *
276c1d1cd59SPaul Walmsley  * Idle all underlying hwmods.  Returns 0.
277c1d1cd59SPaul Walmsley  */
278c1d1cd59SPaul Walmsley static int _omap_device_idle_hwmods(struct omap_device *od)
279c1d1cd59SPaul Walmsley {
2806da23358SPali Rohár 	int ret = 0;
281c1d1cd59SPaul Walmsley 	int i;
282c1d1cd59SPaul Walmsley 
283c1d1cd59SPaul Walmsley 	for (i = 0; i < od->hwmods_cnt; i++)
2846da23358SPali Rohár 		ret |= omap_hwmod_idle(od->hwmods[i]);
285c1d1cd59SPaul Walmsley 
2866da23358SPali Rohár 	return ret;
287c1d1cd59SPaul Walmsley }
28825c7d49eSTony Lindgren 
28925c7d49eSTony Lindgren /* Public functions for use by core code */
29025c7d49eSTony Lindgren 
29125c7d49eSTony Lindgren /**
29225c7d49eSTony Lindgren  * omap_device_get_context_loss_count - get lost context count
2937b6560b4SBen Dooks (Codethink)  * @pdev: The platform device to update.
29425c7d49eSTony Lindgren  *
29525c7d49eSTony Lindgren  * Using the primary hwmod, query the context loss count for this
29625c7d49eSTony Lindgren  * device.
29725c7d49eSTony Lindgren  *
29825c7d49eSTony Lindgren  * Callers should consider context for this device lost any time this
29925c7d49eSTony Lindgren  * function returns a value different than the value the caller got
30025c7d49eSTony Lindgren  * the last time it called this function.
30125c7d49eSTony Lindgren  *
302f733e7c0SAndrea Gelmini  * If any hwmods exist for the omap_device associated with @pdev,
30325c7d49eSTony Lindgren  * return the context loss counter for that hwmod, otherwise return
30425c7d49eSTony Lindgren  * zero.
30525c7d49eSTony Lindgren  */
30625c7d49eSTony Lindgren int omap_device_get_context_loss_count(struct platform_device *pdev)
30725c7d49eSTony Lindgren {
30825c7d49eSTony Lindgren 	struct omap_device *od;
30925c7d49eSTony Lindgren 	u32 ret = 0;
31025c7d49eSTony Lindgren 
31125c7d49eSTony Lindgren 	od = to_omap_device(pdev);
31225c7d49eSTony Lindgren 
31325c7d49eSTony Lindgren 	if (od->hwmods_cnt)
31425c7d49eSTony Lindgren 		ret = omap_hwmod_get_context_loss_count(od->hwmods[0]);
31525c7d49eSTony Lindgren 
31625c7d49eSTony Lindgren 	return ret;
31725c7d49eSTony Lindgren }
31825c7d49eSTony Lindgren 
31925c7d49eSTony Lindgren /**
32025c7d49eSTony Lindgren  * omap_device_alloc - allocate an omap_device
32125c7d49eSTony Lindgren  * @pdev: platform_device that will be included in this omap_device
3227b6560b4SBen Dooks (Codethink)  * @ohs: ptr to the omap_hwmod for this omap_device
3237b6560b4SBen Dooks (Codethink)  * @oh_cnt: the size of the ohs list
32425c7d49eSTony Lindgren  *
32525c7d49eSTony Lindgren  * Convenience function for allocating an omap_device structure and filling
326c1d1cd59SPaul Walmsley  * hwmods, and resources.
32725c7d49eSTony Lindgren  *
32825c7d49eSTony Lindgren  * Returns an struct omap_device pointer or ERR_PTR() on error;
32925c7d49eSTony Lindgren  */
33025c7d49eSTony Lindgren struct omap_device *omap_device_alloc(struct platform_device *pdev,
331c1d1cd59SPaul Walmsley 					struct omap_hwmod **ohs, int oh_cnt)
33225c7d49eSTony Lindgren {
33325c7d49eSTony Lindgren 	int ret = -ENOMEM;
33425c7d49eSTony Lindgren 	struct omap_device *od;
335c2b84a9bSTony Lindgren 	int i;
33625c7d49eSTony Lindgren 	struct omap_hwmod **hwmods;
33725c7d49eSTony Lindgren 
33825c7d49eSTony Lindgren 	od = kzalloc(sizeof(struct omap_device), GFP_KERNEL);
33925c7d49eSTony Lindgren 	if (!od) {
34025c7d49eSTony Lindgren 		ret = -ENOMEM;
34125c7d49eSTony Lindgren 		goto oda_exit1;
34225c7d49eSTony Lindgren 	}
34325c7d49eSTony Lindgren 	od->hwmods_cnt = oh_cnt;
34425c7d49eSTony Lindgren 
34525c7d49eSTony Lindgren 	hwmods = kmemdup(ohs, sizeof(struct omap_hwmod *) * oh_cnt, GFP_KERNEL);
34625c7d49eSTony Lindgren 	if (!hwmods)
34725c7d49eSTony Lindgren 		goto oda_exit2;
34825c7d49eSTony Lindgren 
34925c7d49eSTony Lindgren 	od->hwmods = hwmods;
35025c7d49eSTony Lindgren 	od->pdev = pdev;
35125c7d49eSTony Lindgren 	pdev->archdata.od = od;
35225c7d49eSTony Lindgren 
35325c7d49eSTony Lindgren 	for (i = 0; i < oh_cnt; i++) {
35425c7d49eSTony Lindgren 		hwmods[i]->od = od;
35525c7d49eSTony Lindgren 		_add_hwmod_clocks_clkdev(od, hwmods[i]);
35625c7d49eSTony Lindgren 	}
35725c7d49eSTony Lindgren 
35825c7d49eSTony Lindgren 	return od;
35925c7d49eSTony Lindgren 
36025c7d49eSTony Lindgren oda_exit2:
36125c7d49eSTony Lindgren 	kfree(od);
36225c7d49eSTony Lindgren oda_exit1:
36325c7d49eSTony Lindgren 	dev_err(&pdev->dev, "omap_device: build failed (%d)\n", ret);
36425c7d49eSTony Lindgren 
36525c7d49eSTony Lindgren 	return ERR_PTR(ret);
36625c7d49eSTony Lindgren }
36725c7d49eSTony Lindgren 
36825c7d49eSTony Lindgren void omap_device_delete(struct omap_device *od)
36925c7d49eSTony Lindgren {
37025c7d49eSTony Lindgren 	if (!od)
37125c7d49eSTony Lindgren 		return;
37225c7d49eSTony Lindgren 
37325c7d49eSTony Lindgren 	od->pdev->archdata.od = NULL;
37425c7d49eSTony Lindgren 	kfree(od->hwmods);
37525c7d49eSTony Lindgren 	kfree(od);
37625c7d49eSTony Lindgren }
37725c7d49eSTony Lindgren 
378bf7c5449SRafael J. Wysocki #ifdef CONFIG_PM
37925c7d49eSTony Lindgren static int _od_runtime_suspend(struct device *dev)
38025c7d49eSTony Lindgren {
38125c7d49eSTony Lindgren 	struct platform_device *pdev = to_platform_device(dev);
38225c7d49eSTony Lindgren 	int ret;
38325c7d49eSTony Lindgren 
38425c7d49eSTony Lindgren 	ret = pm_generic_runtime_suspend(dev);
3856da23358SPali Rohár 	if (ret)
38625c7d49eSTony Lindgren 		return ret;
3876da23358SPali Rohár 
3886da23358SPali Rohár 	return omap_device_idle(pdev);
38925c7d49eSTony Lindgren }
39025c7d49eSTony Lindgren 
39125c7d49eSTony Lindgren static int _od_runtime_resume(struct device *dev)
39225c7d49eSTony Lindgren {
39325c7d49eSTony Lindgren 	struct platform_device *pdev = to_platform_device(dev);
3946da23358SPali Rohár 	int ret;
39525c7d49eSTony Lindgren 
3966da23358SPali Rohár 	ret = omap_device_enable(pdev);
39708c78e9dSTony Lindgren 	if (ret) {
39808c78e9dSTony Lindgren 		dev_err(dev, "use pm_runtime_put_sync_suspend() in driver?\n");
3996da23358SPali Rohár 		return ret;
40008c78e9dSTony Lindgren 	}
40125c7d49eSTony Lindgren 
40225c7d49eSTony Lindgren 	return pm_generic_runtime_resume(dev);
40325c7d49eSTony Lindgren }
404f5c33b07SNishanth Menon 
405f5c33b07SNishanth Menon static int _od_fail_runtime_suspend(struct device *dev)
406f5c33b07SNishanth Menon {
407f5c33b07SNishanth Menon 	dev_warn(dev, "%s: FIXME: missing hwmod/omap_dev info\n", __func__);
408f5c33b07SNishanth Menon 	return -ENODEV;
409f5c33b07SNishanth Menon }
410f5c33b07SNishanth Menon 
411f5c33b07SNishanth Menon static int _od_fail_runtime_resume(struct device *dev)
412f5c33b07SNishanth Menon {
413f5c33b07SNishanth Menon 	dev_warn(dev, "%s: FIXME: missing hwmod/omap_dev info\n", __func__);
414f5c33b07SNishanth Menon 	return -ENODEV;
415f5c33b07SNishanth Menon }
416f5c33b07SNishanth Menon 
41725c7d49eSTony Lindgren #endif
41825c7d49eSTony Lindgren 
41925c7d49eSTony Lindgren #ifdef CONFIG_SUSPEND
42025c7d49eSTony Lindgren static int _od_suspend_noirq(struct device *dev)
42125c7d49eSTony Lindgren {
42225c7d49eSTony Lindgren 	struct platform_device *pdev = to_platform_device(dev);
42325c7d49eSTony Lindgren 	struct omap_device *od = to_omap_device(pdev);
42425c7d49eSTony Lindgren 	int ret;
42525c7d49eSTony Lindgren 
42625c7d49eSTony Lindgren 	/* Don't attempt late suspend on a driver that is not bound */
42725c7d49eSTony Lindgren 	if (od->_driver_status != BUS_NOTIFY_BOUND_DRIVER)
42825c7d49eSTony Lindgren 		return 0;
42925c7d49eSTony Lindgren 
43025c7d49eSTony Lindgren 	ret = pm_generic_suspend_noirq(dev);
43125c7d49eSTony Lindgren 
43225c7d49eSTony Lindgren 	if (!ret && !pm_runtime_status_suspended(dev)) {
43325c7d49eSTony Lindgren 		if (pm_generic_runtime_suspend(dev) == 0) {
43425c7d49eSTony Lindgren 			omap_device_idle(pdev);
43525c7d49eSTony Lindgren 			od->flags |= OMAP_DEVICE_SUSPENDED;
43625c7d49eSTony Lindgren 		}
43725c7d49eSTony Lindgren 	}
43825c7d49eSTony Lindgren 
43925c7d49eSTony Lindgren 	return ret;
44025c7d49eSTony Lindgren }
44125c7d49eSTony Lindgren 
44225c7d49eSTony Lindgren static int _od_resume_noirq(struct device *dev)
44325c7d49eSTony Lindgren {
44425c7d49eSTony Lindgren 	struct platform_device *pdev = to_platform_device(dev);
44525c7d49eSTony Lindgren 	struct omap_device *od = to_omap_device(pdev);
44625c7d49eSTony Lindgren 
4473522bf7bSNishanth Menon 	if (od->flags & OMAP_DEVICE_SUSPENDED) {
44825c7d49eSTony Lindgren 		od->flags &= ~OMAP_DEVICE_SUSPENDED;
44925c7d49eSTony Lindgren 		omap_device_enable(pdev);
45025c7d49eSTony Lindgren 		pm_generic_runtime_resume(dev);
45125c7d49eSTony Lindgren 	}
45225c7d49eSTony Lindgren 
45325c7d49eSTony Lindgren 	return pm_generic_resume_noirq(dev);
45425c7d49eSTony Lindgren }
45525c7d49eSTony Lindgren #else
45625c7d49eSTony Lindgren #define _od_suspend_noirq NULL
45725c7d49eSTony Lindgren #define _od_resume_noirq NULL
45825c7d49eSTony Lindgren #endif
45925c7d49eSTony Lindgren 
460f5c33b07SNishanth Menon struct dev_pm_domain omap_device_fail_pm_domain = {
461f5c33b07SNishanth Menon 	.ops = {
462f5c33b07SNishanth Menon 		SET_RUNTIME_PM_OPS(_od_fail_runtime_suspend,
463f5c33b07SNishanth Menon 				   _od_fail_runtime_resume, NULL)
464f5c33b07SNishanth Menon 	}
465f5c33b07SNishanth Menon };
466f5c33b07SNishanth Menon 
46725c7d49eSTony Lindgren struct dev_pm_domain omap_device_pm_domain = {
46825c7d49eSTony Lindgren 	.ops = {
46925c7d49eSTony Lindgren 		SET_RUNTIME_PM_OPS(_od_runtime_suspend, _od_runtime_resume,
47045f0a85cSRafael J. Wysocki 				   NULL)
47125c7d49eSTony Lindgren 		USE_PLATFORM_PM_SLEEP_OPS
472c381f229SGrygorii Strashko 		SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(_od_suspend_noirq,
473c381f229SGrygorii Strashko 					      _od_resume_noirq)
47425c7d49eSTony Lindgren 	}
47525c7d49eSTony Lindgren };
47625c7d49eSTony Lindgren 
47725c7d49eSTony Lindgren /**
47825c7d49eSTony Lindgren  * omap_device_register - register an omap_device with one omap_hwmod
4797b6560b4SBen Dooks (Codethink)  * @pdev: the platform device (omap_device) to register.
48025c7d49eSTony Lindgren  *
48125c7d49eSTony Lindgren  * Register the omap_device structure.  This currently just calls
48225c7d49eSTony Lindgren  * platform_device_register() on the underlying platform_device.
48325c7d49eSTony Lindgren  * Returns the return value of platform_device_register().
48425c7d49eSTony Lindgren  */
48525c7d49eSTony Lindgren int omap_device_register(struct platform_device *pdev)
48625c7d49eSTony Lindgren {
48725c7d49eSTony Lindgren 	pr_debug("omap_device: %s: registering\n", pdev->name);
48825c7d49eSTony Lindgren 
489989561deSTomeu Vizoso 	dev_pm_domain_set(&pdev->dev, &omap_device_pm_domain);
49025c7d49eSTony Lindgren 	return platform_device_add(pdev);
49125c7d49eSTony Lindgren }
49225c7d49eSTony Lindgren 
49325c7d49eSTony Lindgren 
49425c7d49eSTony Lindgren /* Public functions for use by device drivers through struct platform_data */
49525c7d49eSTony Lindgren 
49625c7d49eSTony Lindgren /**
49725c7d49eSTony Lindgren  * omap_device_enable - fully activate an omap_device
4987b6560b4SBen Dooks (Codethink)  * @pdev: the platform device to activate
49925c7d49eSTony Lindgren  *
50025c7d49eSTony Lindgren  * Do whatever is necessary for the hwmods underlying omap_device @od
50125c7d49eSTony Lindgren  * to be accessible and ready to operate.  This generally involves
50225c7d49eSTony Lindgren  * enabling clocks, setting SYSCONFIG registers; and in the future may
50325c7d49eSTony Lindgren  * involve remuxing pins.  Device drivers should call this function
504c1d1cd59SPaul Walmsley  * indirectly via pm_runtime_get*().  Returns -EINVAL if called when
505c1d1cd59SPaul Walmsley  * the omap_device is already enabled, or passes along the return
506c1d1cd59SPaul Walmsley  * value of _omap_device_enable_hwmods().
50725c7d49eSTony Lindgren  */
50825c7d49eSTony Lindgren int omap_device_enable(struct platform_device *pdev)
50925c7d49eSTony Lindgren {
51025c7d49eSTony Lindgren 	int ret;
51125c7d49eSTony Lindgren 	struct omap_device *od;
51225c7d49eSTony Lindgren 
51325c7d49eSTony Lindgren 	od = to_omap_device(pdev);
51425c7d49eSTony Lindgren 
51525c7d49eSTony Lindgren 	if (od->_state == OMAP_DEVICE_STATE_ENABLED) {
51625c7d49eSTony Lindgren 		dev_warn(&pdev->dev,
51725c7d49eSTony Lindgren 			 "omap_device: %s() called from invalid state %d\n",
51825c7d49eSTony Lindgren 			 __func__, od->_state);
51925c7d49eSTony Lindgren 		return -EINVAL;
52025c7d49eSTony Lindgren 	}
52125c7d49eSTony Lindgren 
522c1d1cd59SPaul Walmsley 	ret = _omap_device_enable_hwmods(od);
52325c7d49eSTony Lindgren 
5246da23358SPali Rohár 	if (ret == 0)
52525c7d49eSTony Lindgren 		od->_state = OMAP_DEVICE_STATE_ENABLED;
52625c7d49eSTony Lindgren 
52725c7d49eSTony Lindgren 	return ret;
52825c7d49eSTony Lindgren }
52925c7d49eSTony Lindgren 
53025c7d49eSTony Lindgren /**
53125c7d49eSTony Lindgren  * omap_device_idle - idle an omap_device
5327b6560b4SBen Dooks (Codethink)  * @pdev: The platform_device (omap_device) to idle
53325c7d49eSTony Lindgren  *
534c1d1cd59SPaul Walmsley  * Idle omap_device @od.  Device drivers call this function indirectly
535c1d1cd59SPaul Walmsley  * via pm_runtime_put*().  Returns -EINVAL if the omap_device is not
53625c7d49eSTony Lindgren  * currently enabled, or passes along the return value of
537c1d1cd59SPaul Walmsley  * _omap_device_idle_hwmods().
53825c7d49eSTony Lindgren  */
53925c7d49eSTony Lindgren int omap_device_idle(struct platform_device *pdev)
54025c7d49eSTony Lindgren {
54125c7d49eSTony Lindgren 	int ret;
54225c7d49eSTony Lindgren 	struct omap_device *od;
54325c7d49eSTony Lindgren 
54425c7d49eSTony Lindgren 	od = to_omap_device(pdev);
54525c7d49eSTony Lindgren 
54625c7d49eSTony Lindgren 	if (od->_state != OMAP_DEVICE_STATE_ENABLED) {
54725c7d49eSTony Lindgren 		dev_warn(&pdev->dev,
54825c7d49eSTony Lindgren 			 "omap_device: %s() called from invalid state %d\n",
54925c7d49eSTony Lindgren 			 __func__, od->_state);
55025c7d49eSTony Lindgren 		return -EINVAL;
55125c7d49eSTony Lindgren 	}
55225c7d49eSTony Lindgren 
553c1d1cd59SPaul Walmsley 	ret = _omap_device_idle_hwmods(od);
55425c7d49eSTony Lindgren 
5556da23358SPali Rohár 	if (ret == 0)
55625c7d49eSTony Lindgren 		od->_state = OMAP_DEVICE_STATE_IDLE;
55725c7d49eSTony Lindgren 
55825c7d49eSTony Lindgren 	return ret;
55925c7d49eSTony Lindgren }
56025c7d49eSTony Lindgren 
56125c7d49eSTony Lindgren /**
56225c7d49eSTony Lindgren  * omap_device_assert_hardreset - set a device's hardreset line
56325c7d49eSTony Lindgren  * @pdev: struct platform_device * to reset
56425c7d49eSTony Lindgren  * @name: const char * name of the reset line
56525c7d49eSTony Lindgren  *
56625c7d49eSTony Lindgren  * Set the hardreset line identified by @name on the IP blocks
56725c7d49eSTony Lindgren  * associated with the hwmods backing the platform_device @pdev.  All
56825c7d49eSTony Lindgren  * of the hwmods associated with @pdev must have the same hardreset
56925c7d49eSTony Lindgren  * line linked to them for this to work.  Passes along the return value
57025c7d49eSTony Lindgren  * of omap_hwmod_assert_hardreset() in the event of any failure, or
57125c7d49eSTony Lindgren  * returns 0 upon success.
57225c7d49eSTony Lindgren  */
57325c7d49eSTony Lindgren int omap_device_assert_hardreset(struct platform_device *pdev, const char *name)
57425c7d49eSTony Lindgren {
57525c7d49eSTony Lindgren 	struct omap_device *od = to_omap_device(pdev);
57625c7d49eSTony Lindgren 	int ret = 0;
57725c7d49eSTony Lindgren 	int i;
57825c7d49eSTony Lindgren 
57925c7d49eSTony Lindgren 	for (i = 0; i < od->hwmods_cnt; i++) {
58025c7d49eSTony Lindgren 		ret = omap_hwmod_assert_hardreset(od->hwmods[i], name);
58125c7d49eSTony Lindgren 		if (ret)
58225c7d49eSTony Lindgren 			break;
58325c7d49eSTony Lindgren 	}
58425c7d49eSTony Lindgren 
58525c7d49eSTony Lindgren 	return ret;
58625c7d49eSTony Lindgren }
58725c7d49eSTony Lindgren 
58825c7d49eSTony Lindgren /**
58925c7d49eSTony Lindgren  * omap_device_deassert_hardreset - release a device's hardreset line
59025c7d49eSTony Lindgren  * @pdev: struct platform_device * to reset
59125c7d49eSTony Lindgren  * @name: const char * name of the reset line
59225c7d49eSTony Lindgren  *
59325c7d49eSTony Lindgren  * Release the hardreset line identified by @name on the IP blocks
59425c7d49eSTony Lindgren  * associated with the hwmods backing the platform_device @pdev.  All
59525c7d49eSTony Lindgren  * of the hwmods associated with @pdev must have the same hardreset
59625c7d49eSTony Lindgren  * line linked to them for this to work.  Passes along the return
59725c7d49eSTony Lindgren  * value of omap_hwmod_deassert_hardreset() in the event of any
59825c7d49eSTony Lindgren  * failure, or returns 0 upon success.
59925c7d49eSTony Lindgren  */
60025c7d49eSTony Lindgren int omap_device_deassert_hardreset(struct platform_device *pdev,
60125c7d49eSTony Lindgren 				   const char *name)
60225c7d49eSTony Lindgren {
60325c7d49eSTony Lindgren 	struct omap_device *od = to_omap_device(pdev);
60425c7d49eSTony Lindgren 	int ret = 0;
60525c7d49eSTony Lindgren 	int i;
60625c7d49eSTony Lindgren 
60725c7d49eSTony Lindgren 	for (i = 0; i < od->hwmods_cnt; i++) {
60825c7d49eSTony Lindgren 		ret = omap_hwmod_deassert_hardreset(od->hwmods[i], name);
60925c7d49eSTony Lindgren 		if (ret)
61025c7d49eSTony Lindgren 			break;
61125c7d49eSTony Lindgren 	}
61225c7d49eSTony Lindgren 
61325c7d49eSTony Lindgren 	return ret;
61425c7d49eSTony Lindgren }
61525c7d49eSTony Lindgren 
61625c7d49eSTony Lindgren /**
61725c7d49eSTony Lindgren  * omap_device_get_by_hwmod_name() - convert a hwmod name to
61825c7d49eSTony Lindgren  * device pointer.
61925c7d49eSTony Lindgren  * @oh_name: name of the hwmod device
62025c7d49eSTony Lindgren  *
62125c7d49eSTony Lindgren  * Returns back a struct device * pointer associated with a hwmod
62225c7d49eSTony Lindgren  * device represented by a hwmod_name
62325c7d49eSTony Lindgren  */
62425c7d49eSTony Lindgren struct device *omap_device_get_by_hwmod_name(const char *oh_name)
62525c7d49eSTony Lindgren {
62625c7d49eSTony Lindgren 	struct omap_hwmod *oh;
62725c7d49eSTony Lindgren 
62825c7d49eSTony Lindgren 	if (!oh_name) {
62925c7d49eSTony Lindgren 		WARN(1, "%s: no hwmod name!\n", __func__);
63025c7d49eSTony Lindgren 		return ERR_PTR(-EINVAL);
63125c7d49eSTony Lindgren 	}
63225c7d49eSTony Lindgren 
63325c7d49eSTony Lindgren 	oh = omap_hwmod_lookup(oh_name);
634857835c6SRussell King 	if (!oh) {
63525c7d49eSTony Lindgren 		WARN(1, "%s: no hwmod for %s\n", __func__,
63625c7d49eSTony Lindgren 			oh_name);
637857835c6SRussell King 		return ERR_PTR(-ENODEV);
63825c7d49eSTony Lindgren 	}
639857835c6SRussell King 	if (!oh->od) {
64025c7d49eSTony Lindgren 		WARN(1, "%s: no omap_device for %s\n", __func__,
64125c7d49eSTony Lindgren 			oh_name);
642857835c6SRussell King 		return ERR_PTR(-ENODEV);
64325c7d49eSTony Lindgren 	}
64425c7d49eSTony Lindgren 
64525c7d49eSTony Lindgren 	return &oh->od->pdev->dev;
64625c7d49eSTony Lindgren }
64725c7d49eSTony Lindgren 
64825c7d49eSTony Lindgren static struct notifier_block platform_nb = {
64925c7d49eSTony Lindgren 	.notifier_call = _omap_device_notifier_call,
65025c7d49eSTony Lindgren };
65125c7d49eSTony Lindgren 
65225c7d49eSTony Lindgren static int __init omap_device_init(void)
65325c7d49eSTony Lindgren {
65425c7d49eSTony Lindgren 	bus_register_notifier(&platform_bus_type, &platform_nb);
65525c7d49eSTony Lindgren 	return 0;
65625c7d49eSTony Lindgren }
6578dd5ea72STony Lindgren omap_postcore_initcall(omap_device_init);
65825c7d49eSTony Lindgren 
65925c7d49eSTony Lindgren /**
66025c7d49eSTony Lindgren  * omap_device_late_idle - idle devices without drivers
66125c7d49eSTony Lindgren  * @dev: struct device * associated with omap_device
66225c7d49eSTony Lindgren  * @data: unused
66325c7d49eSTony Lindgren  *
66425c7d49eSTony Lindgren  * Check the driver bound status of this device, and idle it
66525c7d49eSTony Lindgren  * if there is no driver attached.
66625c7d49eSTony Lindgren  */
66725c7d49eSTony Lindgren static int __init omap_device_late_idle(struct device *dev, void *data)
66825c7d49eSTony Lindgren {
66925c7d49eSTony Lindgren 	struct platform_device *pdev = to_platform_device(dev);
67025c7d49eSTony Lindgren 	struct omap_device *od = to_omap_device(pdev);
671f66e329dSRajendra Nayak 	int i;
67225c7d49eSTony Lindgren 
67325c7d49eSTony Lindgren 	if (!od)
67425c7d49eSTony Lindgren 		return 0;
67525c7d49eSTony Lindgren 
67625c7d49eSTony Lindgren 	/*
67725c7d49eSTony Lindgren 	 * If omap_device state is enabled, but has no driver bound,
67825c7d49eSTony Lindgren 	 * idle it.
67925c7d49eSTony Lindgren 	 */
680f66e329dSRajendra Nayak 
681f66e329dSRajendra Nayak 	/*
682f66e329dSRajendra Nayak 	 * Some devices (like memory controllers) are always kept
683f66e329dSRajendra Nayak 	 * enabled, and should not be idled even with no drivers.
684f66e329dSRajendra Nayak 	 */
685f66e329dSRajendra Nayak 	for (i = 0; i < od->hwmods_cnt; i++)
686f66e329dSRajendra Nayak 		if (od->hwmods[i]->flags & HWMOD_INIT_NO_IDLE)
687f66e329dSRajendra Nayak 			return 0;
688f66e329dSRajendra Nayak 
689fe8291e8SGrygorii Strashko 	if (od->_driver_status != BUS_NOTIFY_BOUND_DRIVER &&
690fe8291e8SGrygorii Strashko 	    od->_driver_status != BUS_NOTIFY_BIND_DRIVER) {
69125c7d49eSTony Lindgren 		if (od->_state == OMAP_DEVICE_STATE_ENABLED) {
69225c7d49eSTony Lindgren 			dev_warn(dev, "%s: enabled but no driver.  Idling\n",
69325c7d49eSTony Lindgren 				 __func__);
69425c7d49eSTony Lindgren 			omap_device_idle(pdev);
69525c7d49eSTony Lindgren 		}
69625c7d49eSTony Lindgren 	}
69725c7d49eSTony Lindgren 
69825c7d49eSTony Lindgren 	return 0;
69925c7d49eSTony Lindgren }
70025c7d49eSTony Lindgren 
70125c7d49eSTony Lindgren static int __init omap_device_late_init(void)
70225c7d49eSTony Lindgren {
70325c7d49eSTony Lindgren 	bus_for_each_dev(&platform_bus_type, NULL, NULL, omap_device_late_idle);
7044b91f7f3STony Lindgren 
70525c7d49eSTony Lindgren 	return 0;
70625c7d49eSTony Lindgren }
707e7e17c53SKevin Hilman omap_late_initcall_sync(omap_device_late_init);
708