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