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
42*6aeb51c1SArnd Bergmann static struct omap_device *omap_device_alloc(struct platform_device *pdev,
43*6aeb51c1SArnd Bergmann struct omap_hwmod **ohs, int oh_cnt);
44*6aeb51c1SArnd Bergmann static void omap_device_delete(struct omap_device *od);
45*6aeb51c1SArnd Bergmann static struct dev_pm_domain omap_device_fail_pm_domain;
46*6aeb51c1SArnd Bergmann static struct dev_pm_domain omap_device_pm_domain;
47*6aeb51c1SArnd Bergmann
4825c7d49eSTony Lindgren /* Private functions */
4925c7d49eSTony Lindgren
_add_clkdev(struct omap_device * od,const char * clk_alias,const char * clk_name)5025c7d49eSTony Lindgren static void _add_clkdev(struct omap_device *od, const char *clk_alias,
5125c7d49eSTony Lindgren const char *clk_name)
5225c7d49eSTony Lindgren {
5325c7d49eSTony Lindgren struct clk *r;
541ca90bd4SRussell King int rc;
5525c7d49eSTony Lindgren
5625c7d49eSTony Lindgren if (!clk_alias || !clk_name)
5725c7d49eSTony Lindgren return;
5825c7d49eSTony Lindgren
5925c7d49eSTony Lindgren dev_dbg(&od->pdev->dev, "Creating %s -> %s\n", clk_alias, clk_name);
6025c7d49eSTony Lindgren
6125c7d49eSTony Lindgren r = clk_get_sys(dev_name(&od->pdev->dev), clk_alias);
6225c7d49eSTony Lindgren if (!IS_ERR(r)) {
639a02ae4eSMarkus Pargmann dev_dbg(&od->pdev->dev,
6425c7d49eSTony Lindgren "alias %s already exists\n", clk_alias);
6525c7d49eSTony Lindgren clk_put(r);
6625c7d49eSTony Lindgren return;
6725c7d49eSTony Lindgren }
6825c7d49eSTony Lindgren
6959dcfc48STero Kristo r = clk_get_sys(NULL, clk_name);
7059dcfc48STero Kristo
711aa8f0cbSTony Lindgren if (IS_ERR(r)) {
7259dcfc48STero Kristo struct of_phandle_args clkspec;
7359dcfc48STero Kristo
7459dcfc48STero Kristo clkspec.np = of_find_node_by_name(NULL, clk_name);
7559dcfc48STero Kristo
7659dcfc48STero Kristo r = of_clk_get_from_provider(&clkspec);
7759dcfc48STero Kristo
7859dcfc48STero Kristo rc = clk_register_clkdev(r, clk_alias,
7959dcfc48STero Kristo dev_name(&od->pdev->dev));
8059dcfc48STero Kristo } else {
8159dcfc48STero Kristo rc = clk_add_alias(clk_alias, dev_name(&od->pdev->dev),
8259dcfc48STero Kristo clk_name, NULL);
8359dcfc48STero Kristo }
8459dcfc48STero Kristo
851ca90bd4SRussell King if (rc) {
861ca90bd4SRussell King if (rc == -ENODEV || rc == -ENOMEM)
8725c7d49eSTony Lindgren dev_err(&od->pdev->dev,
8825c7d49eSTony Lindgren "clkdev_alloc for %s failed\n", clk_alias);
891ca90bd4SRussell King else
901ca90bd4SRussell King dev_err(&od->pdev->dev,
911ca90bd4SRussell King "clk_get for %s failed\n", clk_name);
9225c7d49eSTony Lindgren }
9325c7d49eSTony Lindgren }
9425c7d49eSTony Lindgren
9525c7d49eSTony Lindgren /**
9625c7d49eSTony Lindgren * _add_hwmod_clocks_clkdev - Add clkdev entry for hwmod optional clocks
9725c7d49eSTony Lindgren * and main clock
9825c7d49eSTony Lindgren * @od: struct omap_device *od
9925c7d49eSTony Lindgren * @oh: struct omap_hwmod *oh
10025c7d49eSTony Lindgren *
10125c7d49eSTony Lindgren * For the main clock and every optional clock present per hwmod per
10225c7d49eSTony Lindgren * omap_device, this function adds an entry in the clkdev table of the
10325c7d49eSTony Lindgren * form <dev-id=dev_name, con-id=role> if it does not exist already.
10425c7d49eSTony Lindgren *
10525c7d49eSTony Lindgren * This allows drivers to get a pointer to its optional clocks based on its role
10625c7d49eSTony Lindgren * by calling clk_get(<dev*>, <role>).
10725c7d49eSTony Lindgren * In the case of the main clock, a "fck" alias is used.
10825c7d49eSTony Lindgren *
10925c7d49eSTony Lindgren * No return value.
11025c7d49eSTony Lindgren */
_add_hwmod_clocks_clkdev(struct omap_device * od,struct omap_hwmod * oh)11125c7d49eSTony Lindgren static void _add_hwmod_clocks_clkdev(struct omap_device *od,
11225c7d49eSTony Lindgren struct omap_hwmod *oh)
11325c7d49eSTony Lindgren {
11425c7d49eSTony Lindgren int i;
11525c7d49eSTony Lindgren
11625c7d49eSTony Lindgren _add_clkdev(od, "fck", oh->main_clk);
11725c7d49eSTony Lindgren
11825c7d49eSTony Lindgren for (i = 0; i < oh->opt_clks_cnt; i++)
11925c7d49eSTony Lindgren _add_clkdev(od, oh->opt_clks[i].role, oh->opt_clks[i].clk);
12025c7d49eSTony Lindgren }
12125c7d49eSTony Lindgren
12225c7d49eSTony Lindgren
12325c7d49eSTony Lindgren /**
12425c7d49eSTony Lindgren * omap_device_build_from_dt - build an omap_device with multiple hwmods
1257b6560b4SBen Dooks (Codethink) * @pdev: The platform device to update.
12625c7d49eSTony Lindgren *
12725c7d49eSTony Lindgren * Function for building an omap_device already registered from device-tree
12825c7d49eSTony Lindgren *
12925c7d49eSTony Lindgren * Returns 0 or PTR_ERR() on error.
13025c7d49eSTony Lindgren */
omap_device_build_from_dt(struct platform_device * pdev)13125c7d49eSTony Lindgren static int omap_device_build_from_dt(struct platform_device *pdev)
13225c7d49eSTony Lindgren {
13325c7d49eSTony Lindgren struct omap_hwmod **hwmods;
13425c7d49eSTony Lindgren struct omap_device *od;
13525c7d49eSTony Lindgren struct omap_hwmod *oh;
13625c7d49eSTony Lindgren struct device_node *node = pdev->dev.of_node;
137695eea3dSTony Lindgren struct resource res;
13825c7d49eSTony Lindgren const char *oh_name;
13925c7d49eSTony Lindgren int oh_cnt, i, ret = 0;
14071941002STony Lindgren bool device_active = false, skip_pm_domain = false;
14125c7d49eSTony Lindgren
14225c7d49eSTony Lindgren oh_cnt = of_property_count_strings(node, "ti,hwmods");
143c48cd659SRussell King if (oh_cnt <= 0) {
14425c7d49eSTony Lindgren dev_dbg(&pdev->dev, "No 'hwmods' to build omap_device\n");
14525c7d49eSTony Lindgren return -ENODEV;
14625c7d49eSTony Lindgren }
14725c7d49eSTony Lindgren
14871941002STony Lindgren /* SDMA still needs special handling for omap_device_build() */
14971941002STony Lindgren ret = of_property_read_string_index(node, "ti,hwmods", 0, &oh_name);
15071941002STony Lindgren if (!ret && (!strncmp("dma_system", oh_name, 10) ||
15171941002STony Lindgren !strncmp("dma", oh_name, 3)))
15271941002STony Lindgren skip_pm_domain = true;
15371941002STony Lindgren
154695eea3dSTony Lindgren /* Use ti-sysc driver instead of omap_device? */
15571941002STony Lindgren if (!skip_pm_domain &&
15671941002STony Lindgren !omap_hwmod_parse_module_range(NULL, node, &res))
157695eea3dSTony Lindgren return -ENODEV;
158695eea3dSTony Lindgren
1596396bb22SKees Cook hwmods = kcalloc(oh_cnt, sizeof(struct omap_hwmod *), GFP_KERNEL);
16025c7d49eSTony Lindgren if (!hwmods) {
16125c7d49eSTony Lindgren ret = -ENOMEM;
16225c7d49eSTony Lindgren goto odbfd_exit;
16325c7d49eSTony Lindgren }
16425c7d49eSTony Lindgren
16525c7d49eSTony Lindgren for (i = 0; i < oh_cnt; i++) {
16625c7d49eSTony Lindgren of_property_read_string_index(node, "ti,hwmods", i, &oh_name);
16725c7d49eSTony Lindgren oh = omap_hwmod_lookup(oh_name);
16825c7d49eSTony Lindgren if (!oh) {
16925c7d49eSTony Lindgren dev_err(&pdev->dev, "Cannot lookup hwmod '%s'\n",
17025c7d49eSTony Lindgren oh_name);
17125c7d49eSTony Lindgren ret = -EINVAL;
17225c7d49eSTony Lindgren goto odbfd_exit1;
17325c7d49eSTony Lindgren }
17425c7d49eSTony Lindgren hwmods[i] = oh;
1757268032dSRajendra Nayak if (oh->flags & HWMOD_INIT_NO_IDLE)
1767268032dSRajendra Nayak device_active = true;
17725c7d49eSTony Lindgren }
17825c7d49eSTony Lindgren
179c1d1cd59SPaul Walmsley od = omap_device_alloc(pdev, hwmods, oh_cnt);
1804cf9cf89SWei Yongjun if (IS_ERR(od)) {
18125c7d49eSTony Lindgren dev_err(&pdev->dev, "Cannot allocate omap_device for :%s\n",
18225c7d49eSTony Lindgren oh_name);
18325c7d49eSTony Lindgren ret = PTR_ERR(od);
18425c7d49eSTony Lindgren goto odbfd_exit1;
18525c7d49eSTony Lindgren }
18625c7d49eSTony Lindgren
18725c7d49eSTony Lindgren /* Fix up missing resource names */
18825c7d49eSTony Lindgren for (i = 0; i < pdev->num_resources; i++) {
18925c7d49eSTony Lindgren struct resource *r = &pdev->resource[i];
19025c7d49eSTony Lindgren
19125c7d49eSTony Lindgren if (r->name == NULL)
19225c7d49eSTony Lindgren r->name = dev_name(&pdev->dev);
19325c7d49eSTony Lindgren }
19425c7d49eSTony Lindgren
19571941002STony Lindgren if (!skip_pm_domain) {
196989561deSTomeu Vizoso dev_pm_domain_set(&pdev->dev, &omap_device_pm_domain);
1977268032dSRajendra Nayak if (device_active) {
1987268032dSRajendra Nayak omap_device_enable(pdev);
1997268032dSRajendra Nayak pm_runtime_set_active(&pdev->dev);
2007268032dSRajendra Nayak }
20171941002STony Lindgren }
2027268032dSRajendra Nayak
20325c7d49eSTony Lindgren odbfd_exit1:
20425c7d49eSTony Lindgren kfree(hwmods);
20525c7d49eSTony Lindgren odbfd_exit:
206f5c33b07SNishanth Menon /* if data/we are at fault.. load up a fail handler */
207f5c33b07SNishanth Menon if (ret)
208989561deSTomeu Vizoso dev_pm_domain_set(&pdev->dev, &omap_device_fail_pm_domain);
209f5c33b07SNishanth Menon
21025c7d49eSTony Lindgren return ret;
21125c7d49eSTony Lindgren }
21225c7d49eSTony Lindgren
_omap_device_notifier_call(struct notifier_block * nb,unsigned long event,void * dev)21325c7d49eSTony Lindgren static int _omap_device_notifier_call(struct notifier_block *nb,
21425c7d49eSTony Lindgren unsigned long event, void *dev)
21525c7d49eSTony Lindgren {
21625c7d49eSTony Lindgren struct platform_device *pdev = to_platform_device(dev);
21725c7d49eSTony Lindgren struct omap_device *od;
218cf26f113STony Lindgren int err;
21925c7d49eSTony Lindgren
22025c7d49eSTony Lindgren switch (event) {
221213fa10dSGrygorii Strashko case BUS_NOTIFY_REMOVED_DEVICE:
22225c7d49eSTony Lindgren if (pdev->archdata.od)
22325c7d49eSTony Lindgren omap_device_delete(pdev->archdata.od);
22425c7d49eSTony Lindgren break;
225cf26f113STony Lindgren case BUS_NOTIFY_UNBOUND_DRIVER:
226cf26f113STony Lindgren od = to_omap_device(pdev);
227cf26f113STony Lindgren if (od && (od->_state == OMAP_DEVICE_STATE_ENABLED)) {
228cf26f113STony Lindgren dev_info(dev, "enabled after unload, idling\n");
229cf26f113STony Lindgren err = omap_device_idle(pdev);
230cf26f113STony Lindgren if (err)
231cf26f113STony Lindgren dev_err(dev, "failed to idle\n");
232cf26f113STony Lindgren }
233cf26f113STony Lindgren break;
23404abaf07SDave Gerlach case BUS_NOTIFY_BIND_DRIVER:
23504abaf07SDave Gerlach od = to_omap_device(pdev);
236ec76c2eeSAndreas Kemnade if (od) {
23704abaf07SDave Gerlach od->_driver_status = BUS_NOTIFY_BIND_DRIVER;
238ec76c2eeSAndreas Kemnade if (od->_state == OMAP_DEVICE_STATE_ENABLED &&
239ec76c2eeSAndreas Kemnade pm_runtime_status_suspended(dev)) {
24004abaf07SDave Gerlach pm_runtime_set_active(dev);
24104abaf07SDave Gerlach }
242ec76c2eeSAndreas Kemnade }
24304abaf07SDave Gerlach break;
24425c7d49eSTony Lindgren case BUS_NOTIFY_ADD_DEVICE:
24525c7d49eSTony Lindgren if (pdev->dev.of_node)
24625c7d49eSTony Lindgren omap_device_build_from_dt(pdev);
247df561f66SGustavo A. R. Silva fallthrough;
24825c7d49eSTony Lindgren default:
24925c7d49eSTony Lindgren od = to_omap_device(pdev);
25025c7d49eSTony Lindgren if (od)
25125c7d49eSTony Lindgren od->_driver_status = event;
25225c7d49eSTony Lindgren }
25325c7d49eSTony Lindgren
25425c7d49eSTony Lindgren return NOTIFY_DONE;
25525c7d49eSTony Lindgren }
25625c7d49eSTony Lindgren
257c1d1cd59SPaul Walmsley /**
258c1d1cd59SPaul Walmsley * _omap_device_enable_hwmods - call omap_hwmod_enable() on all hwmods
259c1d1cd59SPaul Walmsley * @od: struct omap_device *od
260c1d1cd59SPaul Walmsley *
261c1d1cd59SPaul Walmsley * Enable all underlying hwmods. Returns 0.
262c1d1cd59SPaul Walmsley */
_omap_device_enable_hwmods(struct omap_device * od)263c1d1cd59SPaul Walmsley static int _omap_device_enable_hwmods(struct omap_device *od)
264c1d1cd59SPaul Walmsley {
2656da23358SPali Rohár int ret = 0;
266c1d1cd59SPaul Walmsley int i;
267c1d1cd59SPaul Walmsley
268c1d1cd59SPaul Walmsley for (i = 0; i < od->hwmods_cnt; i++)
2696da23358SPali Rohár ret |= omap_hwmod_enable(od->hwmods[i]);
270c1d1cd59SPaul Walmsley
2716da23358SPali Rohár return ret;
272c1d1cd59SPaul Walmsley }
273c1d1cd59SPaul Walmsley
274c1d1cd59SPaul Walmsley /**
275c1d1cd59SPaul Walmsley * _omap_device_idle_hwmods - call omap_hwmod_idle() on all hwmods
276c1d1cd59SPaul Walmsley * @od: struct omap_device *od
277c1d1cd59SPaul Walmsley *
278c1d1cd59SPaul Walmsley * Idle all underlying hwmods. Returns 0.
279c1d1cd59SPaul Walmsley */
_omap_device_idle_hwmods(struct omap_device * od)280c1d1cd59SPaul Walmsley static int _omap_device_idle_hwmods(struct omap_device *od)
281c1d1cd59SPaul Walmsley {
2826da23358SPali Rohár int ret = 0;
283c1d1cd59SPaul Walmsley int i;
284c1d1cd59SPaul Walmsley
285c1d1cd59SPaul Walmsley for (i = 0; i < od->hwmods_cnt; i++)
2866da23358SPali Rohár ret |= omap_hwmod_idle(od->hwmods[i]);
287c1d1cd59SPaul Walmsley
2886da23358SPali Rohár return ret;
289c1d1cd59SPaul Walmsley }
29025c7d49eSTony Lindgren
29125c7d49eSTony Lindgren /* Public functions for use by core code */
29225c7d49eSTony Lindgren
29325c7d49eSTony Lindgren /**
29425c7d49eSTony Lindgren * omap_device_alloc - allocate an omap_device
29525c7d49eSTony Lindgren * @pdev: platform_device that will be included in this omap_device
2967b6560b4SBen Dooks (Codethink) * @ohs: ptr to the omap_hwmod for this omap_device
2977b6560b4SBen Dooks (Codethink) * @oh_cnt: the size of the ohs list
29825c7d49eSTony Lindgren *
29925c7d49eSTony Lindgren * Convenience function for allocating an omap_device structure and filling
300c1d1cd59SPaul Walmsley * hwmods, and resources.
30125c7d49eSTony Lindgren *
30225c7d49eSTony Lindgren * Returns an struct omap_device pointer or ERR_PTR() on error;
30325c7d49eSTony Lindgren */
omap_device_alloc(struct platform_device * pdev,struct omap_hwmod ** ohs,int oh_cnt)304*6aeb51c1SArnd Bergmann static struct omap_device *omap_device_alloc(struct platform_device *pdev,
305c1d1cd59SPaul Walmsley struct omap_hwmod **ohs, int oh_cnt)
30625c7d49eSTony Lindgren {
30725c7d49eSTony Lindgren int ret = -ENOMEM;
30825c7d49eSTony Lindgren struct omap_device *od;
309c2b84a9bSTony Lindgren int i;
31025c7d49eSTony Lindgren struct omap_hwmod **hwmods;
31125c7d49eSTony Lindgren
31225c7d49eSTony Lindgren od = kzalloc(sizeof(struct omap_device), GFP_KERNEL);
3136d9be937SJing Xiangfeng if (!od)
31425c7d49eSTony Lindgren goto oda_exit1;
3156d9be937SJing Xiangfeng
31625c7d49eSTony Lindgren od->hwmods_cnt = oh_cnt;
31725c7d49eSTony Lindgren
31825c7d49eSTony Lindgren hwmods = kmemdup(ohs, sizeof(struct omap_hwmod *) * oh_cnt, GFP_KERNEL);
31925c7d49eSTony Lindgren if (!hwmods)
32025c7d49eSTony Lindgren goto oda_exit2;
32125c7d49eSTony Lindgren
32225c7d49eSTony Lindgren od->hwmods = hwmods;
32325c7d49eSTony Lindgren od->pdev = pdev;
32425c7d49eSTony Lindgren pdev->archdata.od = od;
32525c7d49eSTony Lindgren
32625c7d49eSTony Lindgren for (i = 0; i < oh_cnt; i++) {
32725c7d49eSTony Lindgren hwmods[i]->od = od;
32825c7d49eSTony Lindgren _add_hwmod_clocks_clkdev(od, hwmods[i]);
32925c7d49eSTony Lindgren }
33025c7d49eSTony Lindgren
33125c7d49eSTony Lindgren return od;
33225c7d49eSTony Lindgren
33325c7d49eSTony Lindgren oda_exit2:
33425c7d49eSTony Lindgren kfree(od);
33525c7d49eSTony Lindgren oda_exit1:
33625c7d49eSTony Lindgren dev_err(&pdev->dev, "omap_device: build failed (%d)\n", ret);
33725c7d49eSTony Lindgren
33825c7d49eSTony Lindgren return ERR_PTR(ret);
33925c7d49eSTony Lindgren }
34025c7d49eSTony Lindgren
omap_device_delete(struct omap_device * od)341*6aeb51c1SArnd Bergmann static void omap_device_delete(struct omap_device *od)
34225c7d49eSTony Lindgren {
34325c7d49eSTony Lindgren if (!od)
34425c7d49eSTony Lindgren return;
34525c7d49eSTony Lindgren
34625c7d49eSTony Lindgren od->pdev->archdata.od = NULL;
34725c7d49eSTony Lindgren kfree(od->hwmods);
34825c7d49eSTony Lindgren kfree(od);
34925c7d49eSTony Lindgren }
35025c7d49eSTony Lindgren
351bf7c5449SRafael J. Wysocki #ifdef CONFIG_PM
_od_runtime_suspend(struct device * dev)35225c7d49eSTony Lindgren static int _od_runtime_suspend(struct device *dev)
35325c7d49eSTony Lindgren {
35425c7d49eSTony Lindgren struct platform_device *pdev = to_platform_device(dev);
35525c7d49eSTony Lindgren int ret;
35625c7d49eSTony Lindgren
35725c7d49eSTony Lindgren ret = pm_generic_runtime_suspend(dev);
3586da23358SPali Rohár if (ret)
35925c7d49eSTony Lindgren return ret;
3606da23358SPali Rohár
3616da23358SPali Rohár return omap_device_idle(pdev);
36225c7d49eSTony Lindgren }
36325c7d49eSTony Lindgren
_od_runtime_resume(struct device * dev)36425c7d49eSTony Lindgren static int _od_runtime_resume(struct device *dev)
36525c7d49eSTony Lindgren {
36625c7d49eSTony Lindgren struct platform_device *pdev = to_platform_device(dev);
3676da23358SPali Rohár int ret;
36825c7d49eSTony Lindgren
3696da23358SPali Rohár ret = omap_device_enable(pdev);
37008c78e9dSTony Lindgren if (ret) {
37108c78e9dSTony Lindgren dev_err(dev, "use pm_runtime_put_sync_suspend() in driver?\n");
3726da23358SPali Rohár return ret;
37308c78e9dSTony Lindgren }
37425c7d49eSTony Lindgren
37525c7d49eSTony Lindgren return pm_generic_runtime_resume(dev);
37625c7d49eSTony Lindgren }
377f5c33b07SNishanth Menon
_od_fail_runtime_suspend(struct device * dev)378f5c33b07SNishanth Menon static int _od_fail_runtime_suspend(struct device *dev)
379f5c33b07SNishanth Menon {
380f5c33b07SNishanth Menon dev_warn(dev, "%s: FIXME: missing hwmod/omap_dev info\n", __func__);
381f5c33b07SNishanth Menon return -ENODEV;
382f5c33b07SNishanth Menon }
383f5c33b07SNishanth Menon
_od_fail_runtime_resume(struct device * dev)384f5c33b07SNishanth Menon static int _od_fail_runtime_resume(struct device *dev)
385f5c33b07SNishanth Menon {
386f5c33b07SNishanth Menon dev_warn(dev, "%s: FIXME: missing hwmod/omap_dev info\n", __func__);
387f5c33b07SNishanth Menon return -ENODEV;
388f5c33b07SNishanth Menon }
389f5c33b07SNishanth Menon
39025c7d49eSTony Lindgren #endif
39125c7d49eSTony Lindgren
39225c7d49eSTony Lindgren #ifdef CONFIG_SUSPEND
_od_suspend_noirq(struct device * dev)39325c7d49eSTony Lindgren static int _od_suspend_noirq(struct device *dev)
39425c7d49eSTony Lindgren {
39525c7d49eSTony Lindgren struct platform_device *pdev = to_platform_device(dev);
39625c7d49eSTony Lindgren struct omap_device *od = to_omap_device(pdev);
39725c7d49eSTony Lindgren int ret;
39825c7d49eSTony Lindgren
39925c7d49eSTony Lindgren /* Don't attempt late suspend on a driver that is not bound */
40025c7d49eSTony Lindgren if (od->_driver_status != BUS_NOTIFY_BOUND_DRIVER)
40125c7d49eSTony Lindgren return 0;
40225c7d49eSTony Lindgren
40325c7d49eSTony Lindgren ret = pm_generic_suspend_noirq(dev);
40425c7d49eSTony Lindgren
40525c7d49eSTony Lindgren if (!ret && !pm_runtime_status_suspended(dev)) {
40625c7d49eSTony Lindgren if (pm_generic_runtime_suspend(dev) == 0) {
40725c7d49eSTony Lindgren omap_device_idle(pdev);
40825c7d49eSTony Lindgren od->flags |= OMAP_DEVICE_SUSPENDED;
40925c7d49eSTony Lindgren }
41025c7d49eSTony Lindgren }
41125c7d49eSTony Lindgren
41225c7d49eSTony Lindgren return ret;
41325c7d49eSTony Lindgren }
41425c7d49eSTony Lindgren
_od_resume_noirq(struct device * dev)41525c7d49eSTony Lindgren static int _od_resume_noirq(struct device *dev)
41625c7d49eSTony Lindgren {
41725c7d49eSTony Lindgren struct platform_device *pdev = to_platform_device(dev);
41825c7d49eSTony Lindgren struct omap_device *od = to_omap_device(pdev);
41925c7d49eSTony Lindgren
4203522bf7bSNishanth Menon if (od->flags & OMAP_DEVICE_SUSPENDED) {
42125c7d49eSTony Lindgren od->flags &= ~OMAP_DEVICE_SUSPENDED;
42225c7d49eSTony Lindgren omap_device_enable(pdev);
42325c7d49eSTony Lindgren pm_generic_runtime_resume(dev);
42425c7d49eSTony Lindgren }
42525c7d49eSTony Lindgren
42625c7d49eSTony Lindgren return pm_generic_resume_noirq(dev);
42725c7d49eSTony Lindgren }
42825c7d49eSTony Lindgren #else
42925c7d49eSTony Lindgren #define _od_suspend_noirq NULL
43025c7d49eSTony Lindgren #define _od_resume_noirq NULL
43125c7d49eSTony Lindgren #endif
43225c7d49eSTony Lindgren
433*6aeb51c1SArnd Bergmann static struct dev_pm_domain omap_device_fail_pm_domain = {
434f5c33b07SNishanth Menon .ops = {
435f5c33b07SNishanth Menon SET_RUNTIME_PM_OPS(_od_fail_runtime_suspend,
436f5c33b07SNishanth Menon _od_fail_runtime_resume, NULL)
437f5c33b07SNishanth Menon }
438f5c33b07SNishanth Menon };
439f5c33b07SNishanth Menon
440*6aeb51c1SArnd Bergmann static struct dev_pm_domain omap_device_pm_domain = {
44125c7d49eSTony Lindgren .ops = {
44225c7d49eSTony Lindgren SET_RUNTIME_PM_OPS(_od_runtime_suspend, _od_runtime_resume,
44345f0a85cSRafael J. Wysocki NULL)
44425c7d49eSTony Lindgren USE_PLATFORM_PM_SLEEP_OPS
445c381f229SGrygorii Strashko SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(_od_suspend_noirq,
446c381f229SGrygorii Strashko _od_resume_noirq)
44725c7d49eSTony Lindgren }
44825c7d49eSTony Lindgren };
44925c7d49eSTony Lindgren
45025c7d49eSTony Lindgren /* Public functions for use by device drivers through struct platform_data */
45125c7d49eSTony Lindgren
45225c7d49eSTony Lindgren /**
45325c7d49eSTony Lindgren * omap_device_enable - fully activate an omap_device
4547b6560b4SBen Dooks (Codethink) * @pdev: the platform device to activate
45525c7d49eSTony Lindgren *
45625c7d49eSTony Lindgren * Do whatever is necessary for the hwmods underlying omap_device @od
45725c7d49eSTony Lindgren * to be accessible and ready to operate. This generally involves
45825c7d49eSTony Lindgren * enabling clocks, setting SYSCONFIG registers; and in the future may
45925c7d49eSTony Lindgren * involve remuxing pins. Device drivers should call this function
460c1d1cd59SPaul Walmsley * indirectly via pm_runtime_get*(). Returns -EINVAL if called when
461c1d1cd59SPaul Walmsley * the omap_device is already enabled, or passes along the return
462c1d1cd59SPaul Walmsley * value of _omap_device_enable_hwmods().
46325c7d49eSTony Lindgren */
omap_device_enable(struct platform_device * pdev)46425c7d49eSTony Lindgren int omap_device_enable(struct platform_device *pdev)
46525c7d49eSTony Lindgren {
46625c7d49eSTony Lindgren int ret;
46725c7d49eSTony Lindgren struct omap_device *od;
46825c7d49eSTony Lindgren
46925c7d49eSTony Lindgren od = to_omap_device(pdev);
47025c7d49eSTony Lindgren
47125c7d49eSTony Lindgren if (od->_state == OMAP_DEVICE_STATE_ENABLED) {
47225c7d49eSTony Lindgren dev_warn(&pdev->dev,
47325c7d49eSTony Lindgren "omap_device: %s() called from invalid state %d\n",
47425c7d49eSTony Lindgren __func__, od->_state);
47525c7d49eSTony Lindgren return -EINVAL;
47625c7d49eSTony Lindgren }
47725c7d49eSTony Lindgren
478c1d1cd59SPaul Walmsley ret = _omap_device_enable_hwmods(od);
47925c7d49eSTony Lindgren
4806da23358SPali Rohár if (ret == 0)
48125c7d49eSTony Lindgren od->_state = OMAP_DEVICE_STATE_ENABLED;
48225c7d49eSTony Lindgren
48325c7d49eSTony Lindgren return ret;
48425c7d49eSTony Lindgren }
48525c7d49eSTony Lindgren
48625c7d49eSTony Lindgren /**
48725c7d49eSTony Lindgren * omap_device_idle - idle an omap_device
4887b6560b4SBen Dooks (Codethink) * @pdev: The platform_device (omap_device) to idle
48925c7d49eSTony Lindgren *
490c1d1cd59SPaul Walmsley * Idle omap_device @od. Device drivers call this function indirectly
491c1d1cd59SPaul Walmsley * via pm_runtime_put*(). Returns -EINVAL if the omap_device is not
49225c7d49eSTony Lindgren * currently enabled, or passes along the return value of
493c1d1cd59SPaul Walmsley * _omap_device_idle_hwmods().
49425c7d49eSTony Lindgren */
omap_device_idle(struct platform_device * pdev)49525c7d49eSTony Lindgren int omap_device_idle(struct platform_device *pdev)
49625c7d49eSTony Lindgren {
49725c7d49eSTony Lindgren int ret;
49825c7d49eSTony Lindgren struct omap_device *od;
49925c7d49eSTony Lindgren
50025c7d49eSTony Lindgren od = to_omap_device(pdev);
50125c7d49eSTony Lindgren
50225c7d49eSTony Lindgren if (od->_state != OMAP_DEVICE_STATE_ENABLED) {
50325c7d49eSTony Lindgren dev_warn(&pdev->dev,
50425c7d49eSTony Lindgren "omap_device: %s() called from invalid state %d\n",
50525c7d49eSTony Lindgren __func__, od->_state);
50625c7d49eSTony Lindgren return -EINVAL;
50725c7d49eSTony Lindgren }
50825c7d49eSTony Lindgren
509c1d1cd59SPaul Walmsley ret = _omap_device_idle_hwmods(od);
51025c7d49eSTony Lindgren
5116da23358SPali Rohár if (ret == 0)
51225c7d49eSTony Lindgren od->_state = OMAP_DEVICE_STATE_IDLE;
51325c7d49eSTony Lindgren
51425c7d49eSTony Lindgren return ret;
51525c7d49eSTony Lindgren }
51625c7d49eSTony Lindgren
51725c7d49eSTony Lindgren /**
51825c7d49eSTony Lindgren * omap_device_assert_hardreset - set a device's hardreset line
51925c7d49eSTony Lindgren * @pdev: struct platform_device * to reset
52025c7d49eSTony Lindgren * @name: const char * name of the reset line
52125c7d49eSTony Lindgren *
52225c7d49eSTony Lindgren * Set the hardreset line identified by @name on the IP blocks
52325c7d49eSTony Lindgren * associated with the hwmods backing the platform_device @pdev. All
52425c7d49eSTony Lindgren * of the hwmods associated with @pdev must have the same hardreset
52525c7d49eSTony Lindgren * line linked to them for this to work. Passes along the return value
52625c7d49eSTony Lindgren * of omap_hwmod_assert_hardreset() in the event of any failure, or
52725c7d49eSTony Lindgren * returns 0 upon success.
52825c7d49eSTony Lindgren */
omap_device_assert_hardreset(struct platform_device * pdev,const char * name)52925c7d49eSTony Lindgren int omap_device_assert_hardreset(struct platform_device *pdev, const char *name)
53025c7d49eSTony Lindgren {
53125c7d49eSTony Lindgren struct omap_device *od = to_omap_device(pdev);
53225c7d49eSTony Lindgren int ret = 0;
53325c7d49eSTony Lindgren int i;
53425c7d49eSTony Lindgren
53525c7d49eSTony Lindgren for (i = 0; i < od->hwmods_cnt; i++) {
53625c7d49eSTony Lindgren ret = omap_hwmod_assert_hardreset(od->hwmods[i], name);
53725c7d49eSTony Lindgren if (ret)
53825c7d49eSTony Lindgren break;
53925c7d49eSTony Lindgren }
54025c7d49eSTony Lindgren
54125c7d49eSTony Lindgren return ret;
54225c7d49eSTony Lindgren }
54325c7d49eSTony Lindgren
54425c7d49eSTony Lindgren /**
54525c7d49eSTony Lindgren * omap_device_deassert_hardreset - release a device's hardreset line
54625c7d49eSTony Lindgren * @pdev: struct platform_device * to reset
54725c7d49eSTony Lindgren * @name: const char * name of the reset line
54825c7d49eSTony Lindgren *
54925c7d49eSTony Lindgren * Release the hardreset line identified by @name on the IP blocks
55025c7d49eSTony Lindgren * associated with the hwmods backing the platform_device @pdev. All
55125c7d49eSTony Lindgren * of the hwmods associated with @pdev must have the same hardreset
55225c7d49eSTony Lindgren * line linked to them for this to work. Passes along the return
55325c7d49eSTony Lindgren * value of omap_hwmod_deassert_hardreset() in the event of any
55425c7d49eSTony Lindgren * failure, or returns 0 upon success.
55525c7d49eSTony Lindgren */
omap_device_deassert_hardreset(struct platform_device * pdev,const char * name)55625c7d49eSTony Lindgren int omap_device_deassert_hardreset(struct platform_device *pdev,
55725c7d49eSTony Lindgren const char *name)
55825c7d49eSTony Lindgren {
55925c7d49eSTony Lindgren struct omap_device *od = to_omap_device(pdev);
56025c7d49eSTony Lindgren int ret = 0;
56125c7d49eSTony Lindgren int i;
56225c7d49eSTony Lindgren
56325c7d49eSTony Lindgren for (i = 0; i < od->hwmods_cnt; i++) {
56425c7d49eSTony Lindgren ret = omap_hwmod_deassert_hardreset(od->hwmods[i], name);
56525c7d49eSTony Lindgren if (ret)
56625c7d49eSTony Lindgren break;
56725c7d49eSTony Lindgren }
56825c7d49eSTony Lindgren
56925c7d49eSTony Lindgren return ret;
57025c7d49eSTony Lindgren }
57125c7d49eSTony Lindgren
57225c7d49eSTony Lindgren static struct notifier_block platform_nb = {
57325c7d49eSTony Lindgren .notifier_call = _omap_device_notifier_call,
57425c7d49eSTony Lindgren };
57525c7d49eSTony Lindgren
omap_device_init(void)57625c7d49eSTony Lindgren static int __init omap_device_init(void)
57725c7d49eSTony Lindgren {
57825c7d49eSTony Lindgren bus_register_notifier(&platform_bus_type, &platform_nb);
57925c7d49eSTony Lindgren return 0;
58025c7d49eSTony Lindgren }
5818dd5ea72STony Lindgren omap_postcore_initcall(omap_device_init);
58225c7d49eSTony Lindgren
58325c7d49eSTony Lindgren /**
58425c7d49eSTony Lindgren * omap_device_late_idle - idle devices without drivers
58525c7d49eSTony Lindgren * @dev: struct device * associated with omap_device
58625c7d49eSTony Lindgren * @data: unused
58725c7d49eSTony Lindgren *
58825c7d49eSTony Lindgren * Check the driver bound status of this device, and idle it
58925c7d49eSTony Lindgren * if there is no driver attached.
59025c7d49eSTony Lindgren */
omap_device_late_idle(struct device * dev,void * data)59125c7d49eSTony Lindgren static int __init omap_device_late_idle(struct device *dev, void *data)
59225c7d49eSTony Lindgren {
59325c7d49eSTony Lindgren struct platform_device *pdev = to_platform_device(dev);
59425c7d49eSTony Lindgren struct omap_device *od = to_omap_device(pdev);
595f66e329dSRajendra Nayak int i;
59625c7d49eSTony Lindgren
59725c7d49eSTony Lindgren if (!od)
59825c7d49eSTony Lindgren return 0;
59925c7d49eSTony Lindgren
60025c7d49eSTony Lindgren /*
60125c7d49eSTony Lindgren * If omap_device state is enabled, but has no driver bound,
60225c7d49eSTony Lindgren * idle it.
60325c7d49eSTony Lindgren */
604f66e329dSRajendra Nayak
605f66e329dSRajendra Nayak /*
606f66e329dSRajendra Nayak * Some devices (like memory controllers) are always kept
607f66e329dSRajendra Nayak * enabled, and should not be idled even with no drivers.
608f66e329dSRajendra Nayak */
609f66e329dSRajendra Nayak for (i = 0; i < od->hwmods_cnt; i++)
610f66e329dSRajendra Nayak if (od->hwmods[i]->flags & HWMOD_INIT_NO_IDLE)
611f66e329dSRajendra Nayak return 0;
612f66e329dSRajendra Nayak
613fe8291e8SGrygorii Strashko if (od->_driver_status != BUS_NOTIFY_BOUND_DRIVER &&
614fe8291e8SGrygorii Strashko od->_driver_status != BUS_NOTIFY_BIND_DRIVER) {
61525c7d49eSTony Lindgren if (od->_state == OMAP_DEVICE_STATE_ENABLED) {
61625c7d49eSTony Lindgren dev_warn(dev, "%s: enabled but no driver. Idling\n",
61725c7d49eSTony Lindgren __func__);
61825c7d49eSTony Lindgren omap_device_idle(pdev);
61925c7d49eSTony Lindgren }
62025c7d49eSTony Lindgren }
62125c7d49eSTony Lindgren
62225c7d49eSTony Lindgren return 0;
62325c7d49eSTony Lindgren }
62425c7d49eSTony Lindgren
omap_device_late_init(void)62525c7d49eSTony Lindgren static int __init omap_device_late_init(void)
62625c7d49eSTony Lindgren {
62725c7d49eSTony Lindgren bus_for_each_dev(&platform_bus_type, NULL, NULL, omap_device_late_idle);
6284b91f7f3STony Lindgren
62925c7d49eSTony Lindgren return 0;
63025c7d49eSTony Lindgren }
631e7e17c53SKevin Hilman omap_late_initcall_sync(omap_device_late_init);
632