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  *
20c1d1cd59SPaul Walmsley  * In the medium- to long-term, this code should be implemented as a
21c1d1cd59SPaul Walmsley  * proper omap_bus/omap_device in Linux, no more platform_data func
22c1d1cd59SPaul Walmsley  * pointers
2325c7d49eSTony Lindgren  *
2425c7d49eSTony Lindgren  *
2525c7d49eSTony Lindgren  */
2625c7d49eSTony Lindgren #undef DEBUG
2725c7d49eSTony Lindgren 
2825c7d49eSTony Lindgren #include <linux/kernel.h>
2925c7d49eSTony Lindgren #include <linux/platform_device.h>
3025c7d49eSTony Lindgren #include <linux/slab.h>
3125c7d49eSTony Lindgren #include <linux/err.h>
3225c7d49eSTony Lindgren #include <linux/io.h>
3325c7d49eSTony Lindgren #include <linux/clk.h>
3425c7d49eSTony Lindgren #include <linux/clkdev.h>
3525c7d49eSTony Lindgren #include <linux/pm_runtime.h>
3625c7d49eSTony Lindgren #include <linux/of.h>
3725c7d49eSTony Lindgren #include <linux/notifier.h>
3825c7d49eSTony Lindgren 
39dad12d11STony Lindgren #include "common.h"
40b76c8b19STony Lindgren #include "soc.h"
4125c7d49eSTony Lindgren #include "omap_device.h"
422a296c8fSTony Lindgren #include "omap_hwmod.h"
4325c7d49eSTony Lindgren 
4425c7d49eSTony Lindgren /* Private functions */
4525c7d49eSTony Lindgren 
4625c7d49eSTony Lindgren static void _add_clkdev(struct omap_device *od, const char *clk_alias,
4725c7d49eSTony Lindgren 		       const char *clk_name)
4825c7d49eSTony Lindgren {
4925c7d49eSTony Lindgren 	struct clk *r;
5025c7d49eSTony Lindgren 	struct clk_lookup *l;
5125c7d49eSTony Lindgren 
5225c7d49eSTony Lindgren 	if (!clk_alias || !clk_name)
5325c7d49eSTony Lindgren 		return;
5425c7d49eSTony Lindgren 
5525c7d49eSTony Lindgren 	dev_dbg(&od->pdev->dev, "Creating %s -> %s\n", clk_alias, clk_name);
5625c7d49eSTony Lindgren 
5725c7d49eSTony Lindgren 	r = clk_get_sys(dev_name(&od->pdev->dev), clk_alias);
5825c7d49eSTony Lindgren 	if (!IS_ERR(r)) {
5925c7d49eSTony Lindgren 		dev_warn(&od->pdev->dev,
6025c7d49eSTony Lindgren 			 "alias %s already exists\n", clk_alias);
6125c7d49eSTony Lindgren 		clk_put(r);
6225c7d49eSTony Lindgren 		return;
6325c7d49eSTony Lindgren 	}
6425c7d49eSTony Lindgren 
6525c7d49eSTony Lindgren 	r = clk_get(NULL, clk_name);
6625c7d49eSTony Lindgren 	if (IS_ERR(r)) {
6725c7d49eSTony Lindgren 		dev_err(&od->pdev->dev,
6825c7d49eSTony Lindgren 			"clk_get for %s failed\n", clk_name);
6925c7d49eSTony Lindgren 		return;
7025c7d49eSTony Lindgren 	}
7125c7d49eSTony Lindgren 
7225c7d49eSTony Lindgren 	l = clkdev_alloc(r, clk_alias, dev_name(&od->pdev->dev));
7325c7d49eSTony Lindgren 	if (!l) {
7425c7d49eSTony Lindgren 		dev_err(&od->pdev->dev,
7525c7d49eSTony Lindgren 			"clkdev_alloc for %s failed\n", clk_alias);
7625c7d49eSTony Lindgren 		return;
7725c7d49eSTony Lindgren 	}
7825c7d49eSTony Lindgren 
7925c7d49eSTony Lindgren 	clkdev_add(l);
8025c7d49eSTony Lindgren }
8125c7d49eSTony Lindgren 
8225c7d49eSTony Lindgren /**
8325c7d49eSTony Lindgren  * _add_hwmod_clocks_clkdev - Add clkdev entry for hwmod optional clocks
8425c7d49eSTony Lindgren  * and main clock
8525c7d49eSTony Lindgren  * @od: struct omap_device *od
8625c7d49eSTony Lindgren  * @oh: struct omap_hwmod *oh
8725c7d49eSTony Lindgren  *
8825c7d49eSTony Lindgren  * For the main clock and every optional clock present per hwmod per
8925c7d49eSTony Lindgren  * omap_device, this function adds an entry in the clkdev table of the
9025c7d49eSTony Lindgren  * form <dev-id=dev_name, con-id=role> if it does not exist already.
9125c7d49eSTony Lindgren  *
9225c7d49eSTony Lindgren  * The function is called from inside omap_device_build_ss(), after
9325c7d49eSTony Lindgren  * omap_device_register.
9425c7d49eSTony Lindgren  *
9525c7d49eSTony Lindgren  * This allows drivers to get a pointer to its optional clocks based on its role
9625c7d49eSTony Lindgren  * by calling clk_get(<dev*>, <role>).
9725c7d49eSTony Lindgren  * In the case of the main clock, a "fck" alias is used.
9825c7d49eSTony Lindgren  *
9925c7d49eSTony Lindgren  * No return value.
10025c7d49eSTony Lindgren  */
10125c7d49eSTony Lindgren static void _add_hwmod_clocks_clkdev(struct omap_device *od,
10225c7d49eSTony Lindgren 				     struct omap_hwmod *oh)
10325c7d49eSTony Lindgren {
10425c7d49eSTony Lindgren 	int i;
10525c7d49eSTony Lindgren 
10625c7d49eSTony Lindgren 	_add_clkdev(od, "fck", oh->main_clk);
10725c7d49eSTony Lindgren 
10825c7d49eSTony Lindgren 	for (i = 0; i < oh->opt_clks_cnt; i++)
10925c7d49eSTony Lindgren 		_add_clkdev(od, oh->opt_clks[i].role, oh->opt_clks[i].clk);
11025c7d49eSTony Lindgren }
11125c7d49eSTony Lindgren 
11225c7d49eSTony Lindgren 
11325c7d49eSTony Lindgren /**
11425c7d49eSTony Lindgren  * omap_device_build_from_dt - build an omap_device with multiple hwmods
11525c7d49eSTony Lindgren  * @pdev_name: name of the platform_device driver to use
11625c7d49eSTony Lindgren  * @pdev_id: this platform_device's connection ID
11725c7d49eSTony Lindgren  * @oh: ptr to the single omap_hwmod that backs this omap_device
11825c7d49eSTony Lindgren  * @pdata: platform_data ptr to associate with the platform_device
11925c7d49eSTony Lindgren  * @pdata_len: amount of memory pointed to by @pdata
12025c7d49eSTony Lindgren  *
12125c7d49eSTony Lindgren  * Function for building an omap_device already registered from device-tree
12225c7d49eSTony Lindgren  *
12325c7d49eSTony Lindgren  * Returns 0 or PTR_ERR() on error.
12425c7d49eSTony Lindgren  */
12525c7d49eSTony Lindgren static int omap_device_build_from_dt(struct platform_device *pdev)
12625c7d49eSTony Lindgren {
12725c7d49eSTony Lindgren 	struct omap_hwmod **hwmods;
12825c7d49eSTony Lindgren 	struct omap_device *od;
12925c7d49eSTony Lindgren 	struct omap_hwmod *oh;
13025c7d49eSTony Lindgren 	struct device_node *node = pdev->dev.of_node;
13125c7d49eSTony Lindgren 	const char *oh_name;
13225c7d49eSTony Lindgren 	int oh_cnt, i, ret = 0;
1337268032dSRajendra Nayak 	bool device_active = false;
13425c7d49eSTony Lindgren 
13525c7d49eSTony Lindgren 	oh_cnt = of_property_count_strings(node, "ti,hwmods");
136c48cd659SRussell King 	if (oh_cnt <= 0) {
13725c7d49eSTony Lindgren 		dev_dbg(&pdev->dev, "No 'hwmods' to build omap_device\n");
13825c7d49eSTony Lindgren 		return -ENODEV;
13925c7d49eSTony Lindgren 	}
14025c7d49eSTony Lindgren 
14125c7d49eSTony Lindgren 	hwmods = kzalloc(sizeof(struct omap_hwmod *) * oh_cnt, GFP_KERNEL);
14225c7d49eSTony Lindgren 	if (!hwmods) {
14325c7d49eSTony Lindgren 		ret = -ENOMEM;
14425c7d49eSTony Lindgren 		goto odbfd_exit;
14525c7d49eSTony Lindgren 	}
14625c7d49eSTony Lindgren 
14725c7d49eSTony Lindgren 	for (i = 0; i < oh_cnt; i++) {
14825c7d49eSTony Lindgren 		of_property_read_string_index(node, "ti,hwmods", i, &oh_name);
14925c7d49eSTony Lindgren 		oh = omap_hwmod_lookup(oh_name);
15025c7d49eSTony Lindgren 		if (!oh) {
15125c7d49eSTony Lindgren 			dev_err(&pdev->dev, "Cannot lookup hwmod '%s'\n",
15225c7d49eSTony Lindgren 				oh_name);
15325c7d49eSTony Lindgren 			ret = -EINVAL;
15425c7d49eSTony Lindgren 			goto odbfd_exit1;
15525c7d49eSTony Lindgren 		}
15625c7d49eSTony Lindgren 		hwmods[i] = oh;
1577268032dSRajendra Nayak 		if (oh->flags & HWMOD_INIT_NO_IDLE)
1587268032dSRajendra Nayak 			device_active = true;
15925c7d49eSTony Lindgren 	}
16025c7d49eSTony Lindgren 
161c1d1cd59SPaul Walmsley 	od = omap_device_alloc(pdev, hwmods, oh_cnt);
1624cf9cf89SWei Yongjun 	if (IS_ERR(od)) {
16325c7d49eSTony Lindgren 		dev_err(&pdev->dev, "Cannot allocate omap_device for :%s\n",
16425c7d49eSTony Lindgren 			oh_name);
16525c7d49eSTony Lindgren 		ret = PTR_ERR(od);
16625c7d49eSTony Lindgren 		goto odbfd_exit1;
16725c7d49eSTony Lindgren 	}
16825c7d49eSTony Lindgren 
16925c7d49eSTony Lindgren 	/* Fix up missing resource names */
17025c7d49eSTony Lindgren 	for (i = 0; i < pdev->num_resources; i++) {
17125c7d49eSTony Lindgren 		struct resource *r = &pdev->resource[i];
17225c7d49eSTony Lindgren 
17325c7d49eSTony Lindgren 		if (r->name == NULL)
17425c7d49eSTony Lindgren 			r->name = dev_name(&pdev->dev);
17525c7d49eSTony Lindgren 	}
17625c7d49eSTony Lindgren 
17725c7d49eSTony Lindgren 	pdev->dev.pm_domain = &omap_device_pm_domain;
17825c7d49eSTony Lindgren 
1797268032dSRajendra Nayak 	if (device_active) {
1807268032dSRajendra Nayak 		omap_device_enable(pdev);
1817268032dSRajendra Nayak 		pm_runtime_set_active(&pdev->dev);
1827268032dSRajendra Nayak 	}
1837268032dSRajendra Nayak 
18425c7d49eSTony Lindgren odbfd_exit1:
18525c7d49eSTony Lindgren 	kfree(hwmods);
18625c7d49eSTony Lindgren odbfd_exit:
18725c7d49eSTony Lindgren 	return ret;
18825c7d49eSTony Lindgren }
18925c7d49eSTony Lindgren 
19025c7d49eSTony Lindgren static int _omap_device_notifier_call(struct notifier_block *nb,
19125c7d49eSTony Lindgren 				      unsigned long event, void *dev)
19225c7d49eSTony Lindgren {
19325c7d49eSTony Lindgren 	struct platform_device *pdev = to_platform_device(dev);
19425c7d49eSTony Lindgren 	struct omap_device *od;
19525c7d49eSTony Lindgren 
19625c7d49eSTony Lindgren 	switch (event) {
19725c7d49eSTony Lindgren 	case BUS_NOTIFY_DEL_DEVICE:
19825c7d49eSTony Lindgren 		if (pdev->archdata.od)
19925c7d49eSTony Lindgren 			omap_device_delete(pdev->archdata.od);
20025c7d49eSTony Lindgren 		break;
20125c7d49eSTony Lindgren 	case BUS_NOTIFY_ADD_DEVICE:
20225c7d49eSTony Lindgren 		if (pdev->dev.of_node)
20325c7d49eSTony Lindgren 			omap_device_build_from_dt(pdev);
204dad12d11STony Lindgren 		omap_auxdata_legacy_init(dev);
20525c7d49eSTony Lindgren 		/* fall through */
20625c7d49eSTony Lindgren 	default:
20725c7d49eSTony Lindgren 		od = to_omap_device(pdev);
20825c7d49eSTony Lindgren 		if (od)
20925c7d49eSTony Lindgren 			od->_driver_status = event;
21025c7d49eSTony Lindgren 	}
21125c7d49eSTony Lindgren 
21225c7d49eSTony Lindgren 	return NOTIFY_DONE;
21325c7d49eSTony Lindgren }
21425c7d49eSTony Lindgren 
215c1d1cd59SPaul Walmsley /**
216c1d1cd59SPaul Walmsley  * _omap_device_enable_hwmods - call omap_hwmod_enable() on all hwmods
217c1d1cd59SPaul Walmsley  * @od: struct omap_device *od
218c1d1cd59SPaul Walmsley  *
219c1d1cd59SPaul Walmsley  * Enable all underlying hwmods.  Returns 0.
220c1d1cd59SPaul Walmsley  */
221c1d1cd59SPaul Walmsley static int _omap_device_enable_hwmods(struct omap_device *od)
222c1d1cd59SPaul Walmsley {
223c1d1cd59SPaul Walmsley 	int i;
224c1d1cd59SPaul Walmsley 
225c1d1cd59SPaul Walmsley 	for (i = 0; i < od->hwmods_cnt; i++)
226c1d1cd59SPaul Walmsley 		omap_hwmod_enable(od->hwmods[i]);
227c1d1cd59SPaul Walmsley 
228c1d1cd59SPaul Walmsley 	/* XXX pass along return value here? */
229c1d1cd59SPaul Walmsley 	return 0;
230c1d1cd59SPaul Walmsley }
231c1d1cd59SPaul Walmsley 
232c1d1cd59SPaul Walmsley /**
233c1d1cd59SPaul Walmsley  * _omap_device_idle_hwmods - call omap_hwmod_idle() on all hwmods
234c1d1cd59SPaul Walmsley  * @od: struct omap_device *od
235c1d1cd59SPaul Walmsley  *
236c1d1cd59SPaul Walmsley  * Idle all underlying hwmods.  Returns 0.
237c1d1cd59SPaul Walmsley  */
238c1d1cd59SPaul Walmsley static int _omap_device_idle_hwmods(struct omap_device *od)
239c1d1cd59SPaul Walmsley {
240c1d1cd59SPaul Walmsley 	int i;
241c1d1cd59SPaul Walmsley 
242c1d1cd59SPaul Walmsley 	for (i = 0; i < od->hwmods_cnt; i++)
243c1d1cd59SPaul Walmsley 		omap_hwmod_idle(od->hwmods[i]);
244c1d1cd59SPaul Walmsley 
245c1d1cd59SPaul Walmsley 	/* XXX pass along return value here? */
246c1d1cd59SPaul Walmsley 	return 0;
247c1d1cd59SPaul Walmsley }
24825c7d49eSTony Lindgren 
24925c7d49eSTony Lindgren /* Public functions for use by core code */
25025c7d49eSTony Lindgren 
25125c7d49eSTony Lindgren /**
25225c7d49eSTony Lindgren  * omap_device_get_context_loss_count - get lost context count
25325c7d49eSTony Lindgren  * @od: struct omap_device *
25425c7d49eSTony Lindgren  *
25525c7d49eSTony Lindgren  * Using the primary hwmod, query the context loss count for this
25625c7d49eSTony Lindgren  * device.
25725c7d49eSTony Lindgren  *
25825c7d49eSTony Lindgren  * Callers should consider context for this device lost any time this
25925c7d49eSTony Lindgren  * function returns a value different than the value the caller got
26025c7d49eSTony Lindgren  * the last time it called this function.
26125c7d49eSTony Lindgren  *
26225c7d49eSTony Lindgren  * If any hwmods exist for the omap_device assoiated with @pdev,
26325c7d49eSTony Lindgren  * return the context loss counter for that hwmod, otherwise return
26425c7d49eSTony Lindgren  * zero.
26525c7d49eSTony Lindgren  */
26625c7d49eSTony Lindgren int omap_device_get_context_loss_count(struct platform_device *pdev)
26725c7d49eSTony Lindgren {
26825c7d49eSTony Lindgren 	struct omap_device *od;
26925c7d49eSTony Lindgren 	u32 ret = 0;
27025c7d49eSTony Lindgren 
27125c7d49eSTony Lindgren 	od = to_omap_device(pdev);
27225c7d49eSTony Lindgren 
27325c7d49eSTony Lindgren 	if (od->hwmods_cnt)
27425c7d49eSTony Lindgren 		ret = omap_hwmod_get_context_loss_count(od->hwmods[0]);
27525c7d49eSTony Lindgren 
27625c7d49eSTony Lindgren 	return ret;
27725c7d49eSTony Lindgren }
27825c7d49eSTony Lindgren 
27925c7d49eSTony Lindgren /**
28025c7d49eSTony Lindgren  * omap_device_count_resources - count number of struct resource entries needed
28125c7d49eSTony Lindgren  * @od: struct omap_device *
282dad4191dSPeter Ujfalusi  * @flags: Type of resources to include when counting (IRQ/DMA/MEM)
28325c7d49eSTony Lindgren  *
28425c7d49eSTony Lindgren  * Count the number of struct resource entries needed for this
28525c7d49eSTony Lindgren  * omap_device @od.  Used by omap_device_build_ss() to determine how
28625c7d49eSTony Lindgren  * much memory to allocate before calling
28725c7d49eSTony Lindgren  * omap_device_fill_resources().  Returns the count.
28825c7d49eSTony Lindgren  */
289dad4191dSPeter Ujfalusi static int omap_device_count_resources(struct omap_device *od,
290dad4191dSPeter Ujfalusi 				       unsigned long flags)
29125c7d49eSTony Lindgren {
29225c7d49eSTony Lindgren 	int c = 0;
29325c7d49eSTony Lindgren 	int i;
29425c7d49eSTony Lindgren 
29525c7d49eSTony Lindgren 	for (i = 0; i < od->hwmods_cnt; i++)
296dad4191dSPeter Ujfalusi 		c += omap_hwmod_count_resources(od->hwmods[i], flags);
29725c7d49eSTony Lindgren 
29825c7d49eSTony Lindgren 	pr_debug("omap_device: %s: counted %d total resources across %d hwmods\n",
29925c7d49eSTony Lindgren 		 od->pdev->name, c, od->hwmods_cnt);
30025c7d49eSTony Lindgren 
30125c7d49eSTony Lindgren 	return c;
30225c7d49eSTony Lindgren }
30325c7d49eSTony Lindgren 
30425c7d49eSTony Lindgren /**
30525c7d49eSTony Lindgren  * omap_device_fill_resources - fill in array of struct resource
30625c7d49eSTony Lindgren  * @od: struct omap_device *
30725c7d49eSTony Lindgren  * @res: pointer to an array of struct resource to be filled in
30825c7d49eSTony Lindgren  *
30925c7d49eSTony Lindgren  * Populate one or more empty struct resource pointed to by @res with
31025c7d49eSTony Lindgren  * the resource data for this omap_device @od.  Used by
31125c7d49eSTony Lindgren  * omap_device_build_ss() after calling omap_device_count_resources().
31225c7d49eSTony Lindgren  * Ideally this function would not be needed at all.  If omap_device
31325c7d49eSTony Lindgren  * replaces platform_device, then we can specify our own
31425c7d49eSTony Lindgren  * get_resource()/ get_irq()/etc functions that use the underlying
31525c7d49eSTony Lindgren  * omap_hwmod information.  Or if platform_device is extended to use
31625c7d49eSTony Lindgren  * subarchitecture-specific function pointers, the various
31725c7d49eSTony Lindgren  * platform_device functions can simply call omap_device internal
31825c7d49eSTony Lindgren  * functions to get device resources.  Hacking around the existing
31925c7d49eSTony Lindgren  * platform_device code wastes memory.  Returns 0.
32025c7d49eSTony Lindgren  */
32125c7d49eSTony Lindgren static int omap_device_fill_resources(struct omap_device *od,
32225c7d49eSTony Lindgren 				      struct resource *res)
32325c7d49eSTony Lindgren {
32425c7d49eSTony Lindgren 	int i, r;
32525c7d49eSTony Lindgren 
32625c7d49eSTony Lindgren 	for (i = 0; i < od->hwmods_cnt; i++) {
32725c7d49eSTony Lindgren 		r = omap_hwmod_fill_resources(od->hwmods[i], res);
32825c7d49eSTony Lindgren 		res += r;
32925c7d49eSTony Lindgren 	}
33025c7d49eSTony Lindgren 
33125c7d49eSTony Lindgren 	return 0;
33225c7d49eSTony Lindgren }
33325c7d49eSTony Lindgren 
33425c7d49eSTony Lindgren /**
33525c7d49eSTony Lindgren  * _od_fill_dma_resources - fill in array of struct resource with dma resources
33625c7d49eSTony Lindgren  * @od: struct omap_device *
33725c7d49eSTony Lindgren  * @res: pointer to an array of struct resource to be filled in
33825c7d49eSTony Lindgren  *
33925c7d49eSTony Lindgren  * Populate one or more empty struct resource pointed to by @res with
34025c7d49eSTony Lindgren  * the dma resource data for this omap_device @od.  Used by
34125c7d49eSTony Lindgren  * omap_device_alloc() after calling omap_device_count_resources().
34225c7d49eSTony Lindgren  *
34325c7d49eSTony Lindgren  * Ideally this function would not be needed at all.  If we have
34425c7d49eSTony Lindgren  * mechanism to get dma resources from DT.
34525c7d49eSTony Lindgren  *
34625c7d49eSTony Lindgren  * Returns 0.
34725c7d49eSTony Lindgren  */
34825c7d49eSTony Lindgren static int _od_fill_dma_resources(struct omap_device *od,
34925c7d49eSTony Lindgren 				      struct resource *res)
35025c7d49eSTony Lindgren {
35125c7d49eSTony Lindgren 	int i, r;
35225c7d49eSTony Lindgren 
35325c7d49eSTony Lindgren 	for (i = 0; i < od->hwmods_cnt; i++) {
35425c7d49eSTony Lindgren 		r = omap_hwmod_fill_dma_resources(od->hwmods[i], res);
35525c7d49eSTony Lindgren 		res += r;
35625c7d49eSTony Lindgren 	}
35725c7d49eSTony Lindgren 
35825c7d49eSTony Lindgren 	return 0;
35925c7d49eSTony Lindgren }
36025c7d49eSTony Lindgren 
36125c7d49eSTony Lindgren /**
36225c7d49eSTony Lindgren  * omap_device_alloc - allocate an omap_device
36325c7d49eSTony Lindgren  * @pdev: platform_device that will be included in this omap_device
36425c7d49eSTony Lindgren  * @oh: ptr to the single omap_hwmod that backs this omap_device
36525c7d49eSTony Lindgren  * @pdata: platform_data ptr to associate with the platform_device
36625c7d49eSTony Lindgren  * @pdata_len: amount of memory pointed to by @pdata
36725c7d49eSTony Lindgren  *
36825c7d49eSTony Lindgren  * Convenience function for allocating an omap_device structure and filling
369c1d1cd59SPaul Walmsley  * hwmods, and resources.
37025c7d49eSTony Lindgren  *
37125c7d49eSTony Lindgren  * Returns an struct omap_device pointer or ERR_PTR() on error;
37225c7d49eSTony Lindgren  */
37325c7d49eSTony Lindgren struct omap_device *omap_device_alloc(struct platform_device *pdev,
374c1d1cd59SPaul Walmsley 					struct omap_hwmod **ohs, int oh_cnt)
37525c7d49eSTony Lindgren {
37625c7d49eSTony Lindgren 	int ret = -ENOMEM;
37725c7d49eSTony Lindgren 	struct omap_device *od;
37825c7d49eSTony Lindgren 	struct resource *res = NULL;
37925c7d49eSTony Lindgren 	int i, res_count;
38025c7d49eSTony Lindgren 	struct omap_hwmod **hwmods;
38125c7d49eSTony Lindgren 
38225c7d49eSTony Lindgren 	od = kzalloc(sizeof(struct omap_device), GFP_KERNEL);
38325c7d49eSTony Lindgren 	if (!od) {
38425c7d49eSTony Lindgren 		ret = -ENOMEM;
38525c7d49eSTony Lindgren 		goto oda_exit1;
38625c7d49eSTony Lindgren 	}
38725c7d49eSTony Lindgren 	od->hwmods_cnt = oh_cnt;
38825c7d49eSTony Lindgren 
38925c7d49eSTony Lindgren 	hwmods = kmemdup(ohs, sizeof(struct omap_hwmod *) * oh_cnt, GFP_KERNEL);
39025c7d49eSTony Lindgren 	if (!hwmods)
39125c7d49eSTony Lindgren 		goto oda_exit2;
39225c7d49eSTony Lindgren 
39325c7d49eSTony Lindgren 	od->hwmods = hwmods;
39425c7d49eSTony Lindgren 	od->pdev = pdev;
39525c7d49eSTony Lindgren 
39625c7d49eSTony Lindgren 	/*
397c567b058SPeter Ujfalusi 	 * Non-DT Boot:
398c567b058SPeter Ujfalusi 	 *   Here, pdev->num_resources = 0, and we should get all the
399c567b058SPeter Ujfalusi 	 *   resources from hwmod.
400c567b058SPeter Ujfalusi 	 *
40125c7d49eSTony Lindgren 	 * DT Boot:
40225c7d49eSTony Lindgren 	 *   OF framework will construct the resource structure (currently
40325c7d49eSTony Lindgren 	 *   does for MEM & IRQ resource) and we should respect/use these
40425c7d49eSTony Lindgren 	 *   resources, killing hwmod dependency.
40525c7d49eSTony Lindgren 	 *   If pdev->num_resources > 0, we assume that MEM & IRQ resources
40625c7d49eSTony Lindgren 	 *   have been allocated by OF layer already (through DTB).
407c567b058SPeter Ujfalusi 	 *   As preparation for the future we examine the OF provided resources
408c567b058SPeter Ujfalusi 	 *   to see if we have DMA resources provided already. In this case
409c567b058SPeter Ujfalusi 	 *   there is no need to update the resources for the device, we use the
410c567b058SPeter Ujfalusi 	 *   OF provided ones.
41125c7d49eSTony Lindgren 	 *
41225c7d49eSTony Lindgren 	 * TODO: Once DMA resource is available from OF layer, we should
41325c7d49eSTony Lindgren 	 *   kill filling any resources from hwmod.
41425c7d49eSTony Lindgren 	 */
415c567b058SPeter Ujfalusi 	if (!pdev->num_resources) {
416c567b058SPeter Ujfalusi 		/* Count all resources for the device */
417c567b058SPeter Ujfalusi 		res_count = omap_device_count_resources(od, IORESOURCE_IRQ |
418c567b058SPeter Ujfalusi 							    IORESOURCE_DMA |
419c567b058SPeter Ujfalusi 							    IORESOURCE_MEM);
420c567b058SPeter Ujfalusi 	} else {
421c567b058SPeter Ujfalusi 		/* Take a look if we already have DMA resource via DT */
422c567b058SPeter Ujfalusi 		for (i = 0; i < pdev->num_resources; i++) {
423c567b058SPeter Ujfalusi 			struct resource *r = &pdev->resource[i];
424c567b058SPeter Ujfalusi 
425c567b058SPeter Ujfalusi 			/* We have it, no need to touch the resources */
426c567b058SPeter Ujfalusi 			if (r->flags == IORESOURCE_DMA)
427c567b058SPeter Ujfalusi 				goto have_everything;
428c567b058SPeter Ujfalusi 		}
429c567b058SPeter Ujfalusi 		/* Count only DMA resources for the device */
430c567b058SPeter Ujfalusi 		res_count = omap_device_count_resources(od, IORESOURCE_DMA);
431c567b058SPeter Ujfalusi 		/* The device has no DMA resource, no need for update */
432c567b058SPeter Ujfalusi 		if (!res_count)
433c567b058SPeter Ujfalusi 			goto have_everything;
434c567b058SPeter Ujfalusi 
435c567b058SPeter Ujfalusi 		res_count += pdev->num_resources;
436c567b058SPeter Ujfalusi 	}
437c567b058SPeter Ujfalusi 
43825c7d49eSTony Lindgren 	/* Allocate resources memory to account for new resources */
43925c7d49eSTony Lindgren 	res = kzalloc(sizeof(struct resource) * res_count, GFP_KERNEL);
44025c7d49eSTony Lindgren 	if (!res)
44125c7d49eSTony Lindgren 		goto oda_exit3;
44225c7d49eSTony Lindgren 
443c567b058SPeter Ujfalusi 	if (!pdev->num_resources) {
444c567b058SPeter Ujfalusi 		dev_dbg(&pdev->dev, "%s: using %d resources from hwmod\n",
44525c7d49eSTony Lindgren 			__func__, res_count);
446c567b058SPeter Ujfalusi 		omap_device_fill_resources(od, res);
447c567b058SPeter Ujfalusi 	} else {
448c567b058SPeter Ujfalusi 		dev_dbg(&pdev->dev,
449c567b058SPeter Ujfalusi 			"%s: appending %d DMA resources from hwmod\n",
450c567b058SPeter Ujfalusi 			__func__, res_count - pdev->num_resources);
45125c7d49eSTony Lindgren 		memcpy(res, pdev->resource,
45225c7d49eSTony Lindgren 		       sizeof(struct resource) * pdev->num_resources);
45325c7d49eSTony Lindgren 		_od_fill_dma_resources(od, &res[pdev->num_resources]);
45425c7d49eSTony Lindgren 	}
45525c7d49eSTony Lindgren 
45625c7d49eSTony Lindgren 	ret = platform_device_add_resources(pdev, res, res_count);
45725c7d49eSTony Lindgren 	kfree(res);
45825c7d49eSTony Lindgren 
45925c7d49eSTony Lindgren 	if (ret)
46025c7d49eSTony Lindgren 		goto oda_exit3;
46125c7d49eSTony Lindgren 
462c567b058SPeter Ujfalusi have_everything:
46325c7d49eSTony Lindgren 	pdev->archdata.od = od;
46425c7d49eSTony Lindgren 
46525c7d49eSTony Lindgren 	for (i = 0; i < oh_cnt; i++) {
46625c7d49eSTony Lindgren 		hwmods[i]->od = od;
46725c7d49eSTony Lindgren 		_add_hwmod_clocks_clkdev(od, hwmods[i]);
46825c7d49eSTony Lindgren 	}
46925c7d49eSTony Lindgren 
47025c7d49eSTony Lindgren 	return od;
47125c7d49eSTony Lindgren 
47225c7d49eSTony Lindgren oda_exit3:
47325c7d49eSTony Lindgren 	kfree(hwmods);
47425c7d49eSTony Lindgren oda_exit2:
47525c7d49eSTony Lindgren 	kfree(od);
47625c7d49eSTony Lindgren oda_exit1:
47725c7d49eSTony Lindgren 	dev_err(&pdev->dev, "omap_device: build failed (%d)\n", ret);
47825c7d49eSTony Lindgren 
47925c7d49eSTony Lindgren 	return ERR_PTR(ret);
48025c7d49eSTony Lindgren }
48125c7d49eSTony Lindgren 
48225c7d49eSTony Lindgren void omap_device_delete(struct omap_device *od)
48325c7d49eSTony Lindgren {
48425c7d49eSTony Lindgren 	if (!od)
48525c7d49eSTony Lindgren 		return;
48625c7d49eSTony Lindgren 
48725c7d49eSTony Lindgren 	od->pdev->archdata.od = NULL;
48825c7d49eSTony Lindgren 	kfree(od->hwmods);
48925c7d49eSTony Lindgren 	kfree(od);
49025c7d49eSTony Lindgren }
49125c7d49eSTony Lindgren 
49225c7d49eSTony Lindgren /**
49325c7d49eSTony Lindgren  * omap_device_build - build and register an omap_device with one omap_hwmod
49425c7d49eSTony Lindgren  * @pdev_name: name of the platform_device driver to use
49525c7d49eSTony Lindgren  * @pdev_id: this platform_device's connection ID
49625c7d49eSTony Lindgren  * @oh: ptr to the single omap_hwmod that backs this omap_device
49725c7d49eSTony Lindgren  * @pdata: platform_data ptr to associate with the platform_device
49825c7d49eSTony Lindgren  * @pdata_len: amount of memory pointed to by @pdata
49925c7d49eSTony Lindgren  *
50025c7d49eSTony Lindgren  * Convenience function for building and registering a single
50125c7d49eSTony Lindgren  * omap_device record, which in turn builds and registers a
50225c7d49eSTony Lindgren  * platform_device record.  See omap_device_build_ss() for more
50325c7d49eSTony Lindgren  * information.  Returns ERR_PTR(-EINVAL) if @oh is NULL; otherwise,
50425c7d49eSTony Lindgren  * passes along the return value of omap_device_build_ss().
50525c7d49eSTony Lindgren  */
506c1d1cd59SPaul Walmsley struct platform_device __init *omap_device_build(const char *pdev_name,
507c1d1cd59SPaul Walmsley 						 int pdev_id,
508c1d1cd59SPaul Walmsley 						 struct omap_hwmod *oh,
509c1d1cd59SPaul Walmsley 						 void *pdata, int pdata_len)
51025c7d49eSTony Lindgren {
51125c7d49eSTony Lindgren 	struct omap_hwmod *ohs[] = { oh };
51225c7d49eSTony Lindgren 
51325c7d49eSTony Lindgren 	if (!oh)
51425c7d49eSTony Lindgren 		return ERR_PTR(-EINVAL);
51525c7d49eSTony Lindgren 
51625c7d49eSTony Lindgren 	return omap_device_build_ss(pdev_name, pdev_id, ohs, 1, pdata,
517c1d1cd59SPaul Walmsley 				    pdata_len);
51825c7d49eSTony Lindgren }
51925c7d49eSTony Lindgren 
52025c7d49eSTony Lindgren /**
52125c7d49eSTony Lindgren  * omap_device_build_ss - build and register an omap_device with multiple hwmods
52225c7d49eSTony Lindgren  * @pdev_name: name of the platform_device driver to use
52325c7d49eSTony Lindgren  * @pdev_id: this platform_device's connection ID
52425c7d49eSTony Lindgren  * @oh: ptr to the single omap_hwmod that backs this omap_device
52525c7d49eSTony Lindgren  * @pdata: platform_data ptr to associate with the platform_device
52625c7d49eSTony Lindgren  * @pdata_len: amount of memory pointed to by @pdata
52725c7d49eSTony Lindgren  *
52825c7d49eSTony Lindgren  * Convenience function for building and registering an omap_device
52925c7d49eSTony Lindgren  * subsystem record.  Subsystem records consist of multiple
53025c7d49eSTony Lindgren  * omap_hwmods.  This function in turn builds and registers a
53125c7d49eSTony Lindgren  * platform_device record.  Returns an ERR_PTR() on error, or passes
53225c7d49eSTony Lindgren  * along the return value of omap_device_register().
53325c7d49eSTony Lindgren  */
534c1d1cd59SPaul Walmsley struct platform_device __init *omap_device_build_ss(const char *pdev_name,
535c1d1cd59SPaul Walmsley 						    int pdev_id,
536c1d1cd59SPaul Walmsley 						    struct omap_hwmod **ohs,
537c1d1cd59SPaul Walmsley 						    int oh_cnt, void *pdata,
538c1d1cd59SPaul Walmsley 						    int pdata_len)
53925c7d49eSTony Lindgren {
54025c7d49eSTony Lindgren 	int ret = -ENOMEM;
54125c7d49eSTony Lindgren 	struct platform_device *pdev;
54225c7d49eSTony Lindgren 	struct omap_device *od;
54325c7d49eSTony Lindgren 
54425c7d49eSTony Lindgren 	if (!ohs || oh_cnt == 0 || !pdev_name)
54525c7d49eSTony Lindgren 		return ERR_PTR(-EINVAL);
54625c7d49eSTony Lindgren 
54725c7d49eSTony Lindgren 	if (!pdata && pdata_len > 0)
54825c7d49eSTony Lindgren 		return ERR_PTR(-EINVAL);
54925c7d49eSTony Lindgren 
55025c7d49eSTony Lindgren 	pdev = platform_device_alloc(pdev_name, pdev_id);
55125c7d49eSTony Lindgren 	if (!pdev) {
55225c7d49eSTony Lindgren 		ret = -ENOMEM;
55325c7d49eSTony Lindgren 		goto odbs_exit;
55425c7d49eSTony Lindgren 	}
55525c7d49eSTony Lindgren 
55625c7d49eSTony Lindgren 	/* Set the dev_name early to allow dev_xxx in omap_device_alloc */
55725c7d49eSTony Lindgren 	if (pdev->id != -1)
55825c7d49eSTony Lindgren 		dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
55925c7d49eSTony Lindgren 	else
56025c7d49eSTony Lindgren 		dev_set_name(&pdev->dev, "%s", pdev->name);
56125c7d49eSTony Lindgren 
562c1d1cd59SPaul Walmsley 	od = omap_device_alloc(pdev, ohs, oh_cnt);
56325c7d49eSTony Lindgren 	if (IS_ERR(od))
56425c7d49eSTony Lindgren 		goto odbs_exit1;
56525c7d49eSTony Lindgren 
56625c7d49eSTony Lindgren 	ret = platform_device_add_data(pdev, pdata, pdata_len);
56725c7d49eSTony Lindgren 	if (ret)
56825c7d49eSTony Lindgren 		goto odbs_exit2;
56925c7d49eSTony Lindgren 
57025c7d49eSTony Lindgren 	ret = omap_device_register(pdev);
57125c7d49eSTony Lindgren 	if (ret)
57225c7d49eSTony Lindgren 		goto odbs_exit2;
57325c7d49eSTony Lindgren 
57425c7d49eSTony Lindgren 	return pdev;
57525c7d49eSTony Lindgren 
57625c7d49eSTony Lindgren odbs_exit2:
57725c7d49eSTony Lindgren 	omap_device_delete(od);
57825c7d49eSTony Lindgren odbs_exit1:
57925c7d49eSTony Lindgren 	platform_device_put(pdev);
58025c7d49eSTony Lindgren odbs_exit:
58125c7d49eSTony Lindgren 
58225c7d49eSTony Lindgren 	pr_err("omap_device: %s: build failed (%d)\n", pdev_name, ret);
58325c7d49eSTony Lindgren 
58425c7d49eSTony Lindgren 	return ERR_PTR(ret);
58525c7d49eSTony Lindgren }
58625c7d49eSTony Lindgren 
58725c7d49eSTony Lindgren #ifdef CONFIG_PM_RUNTIME
58825c7d49eSTony Lindgren static int _od_runtime_suspend(struct device *dev)
58925c7d49eSTony Lindgren {
59025c7d49eSTony Lindgren 	struct platform_device *pdev = to_platform_device(dev);
59125c7d49eSTony Lindgren 	int ret;
59225c7d49eSTony Lindgren 
59325c7d49eSTony Lindgren 	ret = pm_generic_runtime_suspend(dev);
59425c7d49eSTony Lindgren 
59525c7d49eSTony Lindgren 	if (!ret)
59625c7d49eSTony Lindgren 		omap_device_idle(pdev);
59725c7d49eSTony Lindgren 
59825c7d49eSTony Lindgren 	return ret;
59925c7d49eSTony Lindgren }
60025c7d49eSTony Lindgren 
60125c7d49eSTony Lindgren static int _od_runtime_resume(struct device *dev)
60225c7d49eSTony Lindgren {
60325c7d49eSTony Lindgren 	struct platform_device *pdev = to_platform_device(dev);
60425c7d49eSTony Lindgren 
60525c7d49eSTony Lindgren 	omap_device_enable(pdev);
60625c7d49eSTony Lindgren 
60725c7d49eSTony Lindgren 	return pm_generic_runtime_resume(dev);
60825c7d49eSTony Lindgren }
60925c7d49eSTony Lindgren #endif
61025c7d49eSTony Lindgren 
61125c7d49eSTony Lindgren #ifdef CONFIG_SUSPEND
61225c7d49eSTony Lindgren static int _od_suspend_noirq(struct device *dev)
61325c7d49eSTony Lindgren {
61425c7d49eSTony Lindgren 	struct platform_device *pdev = to_platform_device(dev);
61525c7d49eSTony Lindgren 	struct omap_device *od = to_omap_device(pdev);
61625c7d49eSTony Lindgren 	int ret;
61725c7d49eSTony Lindgren 
61825c7d49eSTony Lindgren 	/* Don't attempt late suspend on a driver that is not bound */
61925c7d49eSTony Lindgren 	if (od->_driver_status != BUS_NOTIFY_BOUND_DRIVER)
62025c7d49eSTony Lindgren 		return 0;
62125c7d49eSTony Lindgren 
62225c7d49eSTony Lindgren 	ret = pm_generic_suspend_noirq(dev);
62325c7d49eSTony Lindgren 
62425c7d49eSTony Lindgren 	if (!ret && !pm_runtime_status_suspended(dev)) {
62525c7d49eSTony Lindgren 		if (pm_generic_runtime_suspend(dev) == 0) {
6263522bf7bSNishanth Menon 			pm_runtime_set_suspended(dev);
62725c7d49eSTony Lindgren 			omap_device_idle(pdev);
62825c7d49eSTony Lindgren 			od->flags |= OMAP_DEVICE_SUSPENDED;
62925c7d49eSTony Lindgren 		}
63025c7d49eSTony Lindgren 	}
63125c7d49eSTony Lindgren 
63225c7d49eSTony Lindgren 	return ret;
63325c7d49eSTony Lindgren }
63425c7d49eSTony Lindgren 
63525c7d49eSTony Lindgren static int _od_resume_noirq(struct device *dev)
63625c7d49eSTony Lindgren {
63725c7d49eSTony Lindgren 	struct platform_device *pdev = to_platform_device(dev);
63825c7d49eSTony Lindgren 	struct omap_device *od = to_omap_device(pdev);
63925c7d49eSTony Lindgren 
6403522bf7bSNishanth Menon 	if (od->flags & OMAP_DEVICE_SUSPENDED) {
64125c7d49eSTony Lindgren 		od->flags &= ~OMAP_DEVICE_SUSPENDED;
64225c7d49eSTony Lindgren 		omap_device_enable(pdev);
6433522bf7bSNishanth Menon 		/*
6443522bf7bSNishanth Menon 		 * XXX: we run before core runtime pm has resumed itself. At
6453522bf7bSNishanth Menon 		 * this point in time, we just restore the runtime pm state and
6463522bf7bSNishanth Menon 		 * considering symmetric operations in resume, we donot expect
6473522bf7bSNishanth Menon 		 * to fail. If we failed, something changed in core runtime_pm
6483522bf7bSNishanth Menon 		 * framework OR some device driver messed things up, hence, WARN
6493522bf7bSNishanth Menon 		 */
6503522bf7bSNishanth Menon 		WARN(pm_runtime_set_active(dev),
6513522bf7bSNishanth Menon 		     "Could not set %s runtime state active\n", dev_name(dev));
65225c7d49eSTony Lindgren 		pm_generic_runtime_resume(dev);
65325c7d49eSTony Lindgren 	}
65425c7d49eSTony Lindgren 
65525c7d49eSTony Lindgren 	return pm_generic_resume_noirq(dev);
65625c7d49eSTony Lindgren }
65725c7d49eSTony Lindgren #else
65825c7d49eSTony Lindgren #define _od_suspend_noirq NULL
65925c7d49eSTony Lindgren #define _od_resume_noirq NULL
66025c7d49eSTony Lindgren #endif
66125c7d49eSTony Lindgren 
66225c7d49eSTony Lindgren struct dev_pm_domain omap_device_pm_domain = {
66325c7d49eSTony Lindgren 	.ops = {
66425c7d49eSTony Lindgren 		SET_RUNTIME_PM_OPS(_od_runtime_suspend, _od_runtime_resume,
66545f0a85cSRafael J. Wysocki 				   NULL)
66625c7d49eSTony Lindgren 		USE_PLATFORM_PM_SLEEP_OPS
66725c7d49eSTony Lindgren 		.suspend_noirq = _od_suspend_noirq,
66825c7d49eSTony Lindgren 		.resume_noirq = _od_resume_noirq,
66925c7d49eSTony Lindgren 	}
67025c7d49eSTony Lindgren };
67125c7d49eSTony Lindgren 
67225c7d49eSTony Lindgren /**
67325c7d49eSTony Lindgren  * omap_device_register - register an omap_device with one omap_hwmod
67425c7d49eSTony Lindgren  * @od: struct omap_device * to register
67525c7d49eSTony Lindgren  *
67625c7d49eSTony Lindgren  * Register the omap_device structure.  This currently just calls
67725c7d49eSTony Lindgren  * platform_device_register() on the underlying platform_device.
67825c7d49eSTony Lindgren  * Returns the return value of platform_device_register().
67925c7d49eSTony Lindgren  */
68025c7d49eSTony Lindgren int omap_device_register(struct platform_device *pdev)
68125c7d49eSTony Lindgren {
68225c7d49eSTony Lindgren 	pr_debug("omap_device: %s: registering\n", pdev->name);
68325c7d49eSTony Lindgren 
68425c7d49eSTony Lindgren 	pdev->dev.pm_domain = &omap_device_pm_domain;
68525c7d49eSTony Lindgren 	return platform_device_add(pdev);
68625c7d49eSTony Lindgren }
68725c7d49eSTony Lindgren 
68825c7d49eSTony Lindgren 
68925c7d49eSTony Lindgren /* Public functions for use by device drivers through struct platform_data */
69025c7d49eSTony Lindgren 
69125c7d49eSTony Lindgren /**
69225c7d49eSTony Lindgren  * omap_device_enable - fully activate an omap_device
69325c7d49eSTony Lindgren  * @od: struct omap_device * to activate
69425c7d49eSTony Lindgren  *
69525c7d49eSTony Lindgren  * Do whatever is necessary for the hwmods underlying omap_device @od
69625c7d49eSTony Lindgren  * to be accessible and ready to operate.  This generally involves
69725c7d49eSTony Lindgren  * enabling clocks, setting SYSCONFIG registers; and in the future may
69825c7d49eSTony Lindgren  * involve remuxing pins.  Device drivers should call this function
699c1d1cd59SPaul Walmsley  * indirectly via pm_runtime_get*().  Returns -EINVAL if called when
700c1d1cd59SPaul Walmsley  * the omap_device is already enabled, or passes along the return
701c1d1cd59SPaul Walmsley  * value of _omap_device_enable_hwmods().
70225c7d49eSTony Lindgren  */
70325c7d49eSTony Lindgren int omap_device_enable(struct platform_device *pdev)
70425c7d49eSTony Lindgren {
70525c7d49eSTony Lindgren 	int ret;
70625c7d49eSTony Lindgren 	struct omap_device *od;
70725c7d49eSTony Lindgren 
70825c7d49eSTony Lindgren 	od = to_omap_device(pdev);
70925c7d49eSTony Lindgren 
71025c7d49eSTony Lindgren 	if (od->_state == OMAP_DEVICE_STATE_ENABLED) {
71125c7d49eSTony Lindgren 		dev_warn(&pdev->dev,
71225c7d49eSTony Lindgren 			 "omap_device: %s() called from invalid state %d\n",
71325c7d49eSTony Lindgren 			 __func__, od->_state);
71425c7d49eSTony Lindgren 		return -EINVAL;
71525c7d49eSTony Lindgren 	}
71625c7d49eSTony Lindgren 
717c1d1cd59SPaul Walmsley 	ret = _omap_device_enable_hwmods(od);
71825c7d49eSTony Lindgren 
71925c7d49eSTony Lindgren 	od->_state = OMAP_DEVICE_STATE_ENABLED;
72025c7d49eSTony Lindgren 
72125c7d49eSTony Lindgren 	return ret;
72225c7d49eSTony Lindgren }
72325c7d49eSTony Lindgren 
72425c7d49eSTony Lindgren /**
72525c7d49eSTony Lindgren  * omap_device_idle - idle an omap_device
72625c7d49eSTony Lindgren  * @od: struct omap_device * to idle
72725c7d49eSTony Lindgren  *
728c1d1cd59SPaul Walmsley  * Idle omap_device @od.  Device drivers call this function indirectly
729c1d1cd59SPaul Walmsley  * via pm_runtime_put*().  Returns -EINVAL if the omap_device is not
73025c7d49eSTony Lindgren  * currently enabled, or passes along the return value of
731c1d1cd59SPaul Walmsley  * _omap_device_idle_hwmods().
73225c7d49eSTony Lindgren  */
73325c7d49eSTony Lindgren int omap_device_idle(struct platform_device *pdev)
73425c7d49eSTony Lindgren {
73525c7d49eSTony Lindgren 	int ret;
73625c7d49eSTony Lindgren 	struct omap_device *od;
73725c7d49eSTony Lindgren 
73825c7d49eSTony Lindgren 	od = to_omap_device(pdev);
73925c7d49eSTony Lindgren 
74025c7d49eSTony Lindgren 	if (od->_state != OMAP_DEVICE_STATE_ENABLED) {
74125c7d49eSTony Lindgren 		dev_warn(&pdev->dev,
74225c7d49eSTony Lindgren 			 "omap_device: %s() called from invalid state %d\n",
74325c7d49eSTony Lindgren 			 __func__, od->_state);
74425c7d49eSTony Lindgren 		return -EINVAL;
74525c7d49eSTony Lindgren 	}
74625c7d49eSTony Lindgren 
747c1d1cd59SPaul Walmsley 	ret = _omap_device_idle_hwmods(od);
74825c7d49eSTony Lindgren 
74925c7d49eSTony Lindgren 	od->_state = OMAP_DEVICE_STATE_IDLE;
75025c7d49eSTony Lindgren 
75125c7d49eSTony Lindgren 	return ret;
75225c7d49eSTony Lindgren }
75325c7d49eSTony Lindgren 
75425c7d49eSTony Lindgren /**
75525c7d49eSTony Lindgren  * omap_device_assert_hardreset - set a device's hardreset line
75625c7d49eSTony Lindgren  * @pdev: struct platform_device * to reset
75725c7d49eSTony Lindgren  * @name: const char * name of the reset line
75825c7d49eSTony Lindgren  *
75925c7d49eSTony Lindgren  * Set the hardreset line identified by @name on the IP blocks
76025c7d49eSTony Lindgren  * associated with the hwmods backing the platform_device @pdev.  All
76125c7d49eSTony Lindgren  * of the hwmods associated with @pdev must have the same hardreset
76225c7d49eSTony Lindgren  * line linked to them for this to work.  Passes along the return value
76325c7d49eSTony Lindgren  * of omap_hwmod_assert_hardreset() in the event of any failure, or
76425c7d49eSTony Lindgren  * returns 0 upon success.
76525c7d49eSTony Lindgren  */
76625c7d49eSTony Lindgren int omap_device_assert_hardreset(struct platform_device *pdev, const char *name)
76725c7d49eSTony Lindgren {
76825c7d49eSTony Lindgren 	struct omap_device *od = to_omap_device(pdev);
76925c7d49eSTony Lindgren 	int ret = 0;
77025c7d49eSTony Lindgren 	int i;
77125c7d49eSTony Lindgren 
77225c7d49eSTony Lindgren 	for (i = 0; i < od->hwmods_cnt; i++) {
77325c7d49eSTony Lindgren 		ret = omap_hwmod_assert_hardreset(od->hwmods[i], name);
77425c7d49eSTony Lindgren 		if (ret)
77525c7d49eSTony Lindgren 			break;
77625c7d49eSTony Lindgren 	}
77725c7d49eSTony Lindgren 
77825c7d49eSTony Lindgren 	return ret;
77925c7d49eSTony Lindgren }
78025c7d49eSTony Lindgren 
78125c7d49eSTony Lindgren /**
78225c7d49eSTony Lindgren  * omap_device_deassert_hardreset - release a device's hardreset line
78325c7d49eSTony Lindgren  * @pdev: struct platform_device * to reset
78425c7d49eSTony Lindgren  * @name: const char * name of the reset line
78525c7d49eSTony Lindgren  *
78625c7d49eSTony Lindgren  * Release the hardreset line identified by @name on the IP blocks
78725c7d49eSTony Lindgren  * associated with the hwmods backing the platform_device @pdev.  All
78825c7d49eSTony Lindgren  * of the hwmods associated with @pdev must have the same hardreset
78925c7d49eSTony Lindgren  * line linked to them for this to work.  Passes along the return
79025c7d49eSTony Lindgren  * value of omap_hwmod_deassert_hardreset() in the event of any
79125c7d49eSTony Lindgren  * failure, or returns 0 upon success.
79225c7d49eSTony Lindgren  */
79325c7d49eSTony Lindgren int omap_device_deassert_hardreset(struct platform_device *pdev,
79425c7d49eSTony Lindgren 				   const char *name)
79525c7d49eSTony Lindgren {
79625c7d49eSTony Lindgren 	struct omap_device *od = to_omap_device(pdev);
79725c7d49eSTony Lindgren 	int ret = 0;
79825c7d49eSTony Lindgren 	int i;
79925c7d49eSTony Lindgren 
80025c7d49eSTony Lindgren 	for (i = 0; i < od->hwmods_cnt; i++) {
80125c7d49eSTony Lindgren 		ret = omap_hwmod_deassert_hardreset(od->hwmods[i], name);
80225c7d49eSTony Lindgren 		if (ret)
80325c7d49eSTony Lindgren 			break;
80425c7d49eSTony Lindgren 	}
80525c7d49eSTony Lindgren 
80625c7d49eSTony Lindgren 	return ret;
80725c7d49eSTony Lindgren }
80825c7d49eSTony Lindgren 
80925c7d49eSTony Lindgren /**
81025c7d49eSTony Lindgren  * omap_device_get_by_hwmod_name() - convert a hwmod name to
81125c7d49eSTony Lindgren  * device pointer.
81225c7d49eSTony Lindgren  * @oh_name: name of the hwmod device
81325c7d49eSTony Lindgren  *
81425c7d49eSTony Lindgren  * Returns back a struct device * pointer associated with a hwmod
81525c7d49eSTony Lindgren  * device represented by a hwmod_name
81625c7d49eSTony Lindgren  */
81725c7d49eSTony Lindgren struct device *omap_device_get_by_hwmod_name(const char *oh_name)
81825c7d49eSTony Lindgren {
81925c7d49eSTony Lindgren 	struct omap_hwmod *oh;
82025c7d49eSTony Lindgren 
82125c7d49eSTony Lindgren 	if (!oh_name) {
82225c7d49eSTony Lindgren 		WARN(1, "%s: no hwmod name!\n", __func__);
82325c7d49eSTony Lindgren 		return ERR_PTR(-EINVAL);
82425c7d49eSTony Lindgren 	}
82525c7d49eSTony Lindgren 
82625c7d49eSTony Lindgren 	oh = omap_hwmod_lookup(oh_name);
827857835c6SRussell King 	if (!oh) {
82825c7d49eSTony Lindgren 		WARN(1, "%s: no hwmod for %s\n", __func__,
82925c7d49eSTony Lindgren 			oh_name);
830857835c6SRussell King 		return ERR_PTR(-ENODEV);
83125c7d49eSTony Lindgren 	}
832857835c6SRussell King 	if (!oh->od) {
83325c7d49eSTony Lindgren 		WARN(1, "%s: no omap_device for %s\n", __func__,
83425c7d49eSTony Lindgren 			oh_name);
835857835c6SRussell King 		return ERR_PTR(-ENODEV);
83625c7d49eSTony Lindgren 	}
83725c7d49eSTony Lindgren 
83825c7d49eSTony Lindgren 	return &oh->od->pdev->dev;
83925c7d49eSTony Lindgren }
84025c7d49eSTony Lindgren 
84125c7d49eSTony Lindgren static struct notifier_block platform_nb = {
84225c7d49eSTony Lindgren 	.notifier_call = _omap_device_notifier_call,
84325c7d49eSTony Lindgren };
84425c7d49eSTony Lindgren 
84525c7d49eSTony Lindgren static int __init omap_device_init(void)
84625c7d49eSTony Lindgren {
84725c7d49eSTony Lindgren 	bus_register_notifier(&platform_bus_type, &platform_nb);
84825c7d49eSTony Lindgren 	return 0;
84925c7d49eSTony Lindgren }
850b76c8b19STony Lindgren omap_core_initcall(omap_device_init);
85125c7d49eSTony Lindgren 
85225c7d49eSTony Lindgren /**
85325c7d49eSTony Lindgren  * omap_device_late_idle - idle devices without drivers
85425c7d49eSTony Lindgren  * @dev: struct device * associated with omap_device
85525c7d49eSTony Lindgren  * @data: unused
85625c7d49eSTony Lindgren  *
85725c7d49eSTony Lindgren  * Check the driver bound status of this device, and idle it
85825c7d49eSTony Lindgren  * if there is no driver attached.
85925c7d49eSTony Lindgren  */
86025c7d49eSTony Lindgren static int __init omap_device_late_idle(struct device *dev, void *data)
86125c7d49eSTony Lindgren {
86225c7d49eSTony Lindgren 	struct platform_device *pdev = to_platform_device(dev);
86325c7d49eSTony Lindgren 	struct omap_device *od = to_omap_device(pdev);
864f66e329dSRajendra Nayak 	int i;
86525c7d49eSTony Lindgren 
86625c7d49eSTony Lindgren 	if (!od)
86725c7d49eSTony Lindgren 		return 0;
86825c7d49eSTony Lindgren 
86925c7d49eSTony Lindgren 	/*
87025c7d49eSTony Lindgren 	 * If omap_device state is enabled, but has no driver bound,
87125c7d49eSTony Lindgren 	 * idle it.
87225c7d49eSTony Lindgren 	 */
873f66e329dSRajendra Nayak 
874f66e329dSRajendra Nayak 	/*
875f66e329dSRajendra Nayak 	 * Some devices (like memory controllers) are always kept
876f66e329dSRajendra Nayak 	 * enabled, and should not be idled even with no drivers.
877f66e329dSRajendra Nayak 	 */
878f66e329dSRajendra Nayak 	for (i = 0; i < od->hwmods_cnt; i++)
879f66e329dSRajendra Nayak 		if (od->hwmods[i]->flags & HWMOD_INIT_NO_IDLE)
880f66e329dSRajendra Nayak 			return 0;
881f66e329dSRajendra Nayak 
88225c7d49eSTony Lindgren 	if (od->_driver_status != BUS_NOTIFY_BOUND_DRIVER) {
88325c7d49eSTony Lindgren 		if (od->_state == OMAP_DEVICE_STATE_ENABLED) {
88425c7d49eSTony Lindgren 			dev_warn(dev, "%s: enabled but no driver.  Idling\n",
88525c7d49eSTony Lindgren 				 __func__);
88625c7d49eSTony Lindgren 			omap_device_idle(pdev);
88725c7d49eSTony Lindgren 		}
88825c7d49eSTony Lindgren 	}
88925c7d49eSTony Lindgren 
89025c7d49eSTony Lindgren 	return 0;
89125c7d49eSTony Lindgren }
89225c7d49eSTony Lindgren 
89325c7d49eSTony Lindgren static int __init omap_device_late_init(void)
89425c7d49eSTony Lindgren {
89525c7d49eSTony Lindgren 	bus_for_each_dev(&platform_bus_type, NULL, NULL, omap_device_late_idle);
89625c7d49eSTony Lindgren 	return 0;
89725c7d49eSTony Lindgren }
898e7e17c53SKevin Hilman omap_late_initcall_sync(omap_device_late_init);
899