162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * omap_device implementation 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2009-2010 Nokia Corporation 662306a36Sopenharmony_ci * Paul Walmsley, Kevin Hilman 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Developed in collaboration with (alphabetical order): Benoit 962306a36Sopenharmony_ci * Cousson, Thara Gopinath, Tony Lindgren, Rajendra Nayak, Vikram 1062306a36Sopenharmony_ci * Pandita, Sakari Poussa, Anand Sawant, Santosh Shilimkar, Richard 1162306a36Sopenharmony_ci * Woodruff 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * This code provides a consistent interface for OMAP device drivers 1462306a36Sopenharmony_ci * to control power management and interconnect properties of their 1562306a36Sopenharmony_ci * devices. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * In the medium- to long-term, this code should be implemented as a 1862306a36Sopenharmony_ci * proper omap_bus/omap_device in Linux, no more platform_data func 1962306a36Sopenharmony_ci * pointers 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci#undef DEBUG 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <linux/kernel.h> 2462306a36Sopenharmony_ci#include <linux/platform_device.h> 2562306a36Sopenharmony_ci#include <linux/slab.h> 2662306a36Sopenharmony_ci#include <linux/err.h> 2762306a36Sopenharmony_ci#include <linux/io.h> 2862306a36Sopenharmony_ci#include <linux/clk.h> 2962306a36Sopenharmony_ci#include <linux/clkdev.h> 3062306a36Sopenharmony_ci#include <linux/pm_domain.h> 3162306a36Sopenharmony_ci#include <linux/pm_runtime.h> 3262306a36Sopenharmony_ci#include <linux/of.h> 3362306a36Sopenharmony_ci#include <linux/of_address.h> 3462306a36Sopenharmony_ci#include <linux/of_irq.h> 3562306a36Sopenharmony_ci#include <linux/notifier.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include "common.h" 3862306a36Sopenharmony_ci#include "soc.h" 3962306a36Sopenharmony_ci#include "omap_device.h" 4062306a36Sopenharmony_ci#include "omap_hwmod.h" 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic struct omap_device *omap_device_alloc(struct platform_device *pdev, 4362306a36Sopenharmony_ci struct omap_hwmod **ohs, int oh_cnt); 4462306a36Sopenharmony_cistatic void omap_device_delete(struct omap_device *od); 4562306a36Sopenharmony_cistatic struct dev_pm_domain omap_device_fail_pm_domain; 4662306a36Sopenharmony_cistatic struct dev_pm_domain omap_device_pm_domain; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* Private functions */ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic void _add_clkdev(struct omap_device *od, const char *clk_alias, 5162306a36Sopenharmony_ci const char *clk_name) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci struct clk *r; 5462306a36Sopenharmony_ci int rc; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (!clk_alias || !clk_name) 5762306a36Sopenharmony_ci return; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci dev_dbg(&od->pdev->dev, "Creating %s -> %s\n", clk_alias, clk_name); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci r = clk_get_sys(dev_name(&od->pdev->dev), clk_alias); 6262306a36Sopenharmony_ci if (!IS_ERR(r)) { 6362306a36Sopenharmony_ci dev_dbg(&od->pdev->dev, 6462306a36Sopenharmony_ci "alias %s already exists\n", clk_alias); 6562306a36Sopenharmony_ci clk_put(r); 6662306a36Sopenharmony_ci return; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci r = clk_get_sys(NULL, clk_name); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (IS_ERR(r)) { 7262306a36Sopenharmony_ci struct of_phandle_args clkspec; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci clkspec.np = of_find_node_by_name(NULL, clk_name); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci r = of_clk_get_from_provider(&clkspec); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci rc = clk_register_clkdev(r, clk_alias, 7962306a36Sopenharmony_ci dev_name(&od->pdev->dev)); 8062306a36Sopenharmony_ci } else { 8162306a36Sopenharmony_ci rc = clk_add_alias(clk_alias, dev_name(&od->pdev->dev), 8262306a36Sopenharmony_ci clk_name, NULL); 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (rc) { 8662306a36Sopenharmony_ci if (rc == -ENODEV || rc == -ENOMEM) 8762306a36Sopenharmony_ci dev_err(&od->pdev->dev, 8862306a36Sopenharmony_ci "clkdev_alloc for %s failed\n", clk_alias); 8962306a36Sopenharmony_ci else 9062306a36Sopenharmony_ci dev_err(&od->pdev->dev, 9162306a36Sopenharmony_ci "clk_get for %s failed\n", clk_name); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/** 9662306a36Sopenharmony_ci * _add_hwmod_clocks_clkdev - Add clkdev entry for hwmod optional clocks 9762306a36Sopenharmony_ci * and main clock 9862306a36Sopenharmony_ci * @od: struct omap_device *od 9962306a36Sopenharmony_ci * @oh: struct omap_hwmod *oh 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * For the main clock and every optional clock present per hwmod per 10262306a36Sopenharmony_ci * omap_device, this function adds an entry in the clkdev table of the 10362306a36Sopenharmony_ci * form <dev-id=dev_name, con-id=role> if it does not exist already. 10462306a36Sopenharmony_ci * 10562306a36Sopenharmony_ci * This allows drivers to get a pointer to its optional clocks based on its role 10662306a36Sopenharmony_ci * by calling clk_get(<dev*>, <role>). 10762306a36Sopenharmony_ci * In the case of the main clock, a "fck" alias is used. 10862306a36Sopenharmony_ci * 10962306a36Sopenharmony_ci * No return value. 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_cistatic void _add_hwmod_clocks_clkdev(struct omap_device *od, 11262306a36Sopenharmony_ci struct omap_hwmod *oh) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci int i; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci _add_clkdev(od, "fck", oh->main_clk); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci for (i = 0; i < oh->opt_clks_cnt; i++) 11962306a36Sopenharmony_ci _add_clkdev(od, oh->opt_clks[i].role, oh->opt_clks[i].clk); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/** 12462306a36Sopenharmony_ci * omap_device_build_from_dt - build an omap_device with multiple hwmods 12562306a36Sopenharmony_ci * @pdev: The platform device to update. 12662306a36Sopenharmony_ci * 12762306a36Sopenharmony_ci * Function for building an omap_device already registered from device-tree 12862306a36Sopenharmony_ci * 12962306a36Sopenharmony_ci * Returns 0 or PTR_ERR() on error. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_cistatic int omap_device_build_from_dt(struct platform_device *pdev) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct omap_hwmod **hwmods; 13462306a36Sopenharmony_ci struct omap_device *od; 13562306a36Sopenharmony_ci struct omap_hwmod *oh; 13662306a36Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 13762306a36Sopenharmony_ci struct resource res; 13862306a36Sopenharmony_ci const char *oh_name; 13962306a36Sopenharmony_ci int oh_cnt, i, ret = 0; 14062306a36Sopenharmony_ci bool device_active = false, skip_pm_domain = false; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci oh_cnt = of_property_count_strings(node, "ti,hwmods"); 14362306a36Sopenharmony_ci if (oh_cnt <= 0) { 14462306a36Sopenharmony_ci dev_dbg(&pdev->dev, "No 'hwmods' to build omap_device\n"); 14562306a36Sopenharmony_ci return -ENODEV; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* SDMA still needs special handling for omap_device_build() */ 14962306a36Sopenharmony_ci ret = of_property_read_string_index(node, "ti,hwmods", 0, &oh_name); 15062306a36Sopenharmony_ci if (!ret && (!strncmp("dma_system", oh_name, 10) || 15162306a36Sopenharmony_ci !strncmp("dma", oh_name, 3))) 15262306a36Sopenharmony_ci skip_pm_domain = true; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* Use ti-sysc driver instead of omap_device? */ 15562306a36Sopenharmony_ci if (!skip_pm_domain && 15662306a36Sopenharmony_ci !omap_hwmod_parse_module_range(NULL, node, &res)) 15762306a36Sopenharmony_ci return -ENODEV; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci hwmods = kcalloc(oh_cnt, sizeof(struct omap_hwmod *), GFP_KERNEL); 16062306a36Sopenharmony_ci if (!hwmods) { 16162306a36Sopenharmony_ci ret = -ENOMEM; 16262306a36Sopenharmony_ci goto odbfd_exit; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci for (i = 0; i < oh_cnt; i++) { 16662306a36Sopenharmony_ci of_property_read_string_index(node, "ti,hwmods", i, &oh_name); 16762306a36Sopenharmony_ci oh = omap_hwmod_lookup(oh_name); 16862306a36Sopenharmony_ci if (!oh) { 16962306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot lookup hwmod '%s'\n", 17062306a36Sopenharmony_ci oh_name); 17162306a36Sopenharmony_ci ret = -EINVAL; 17262306a36Sopenharmony_ci goto odbfd_exit1; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci hwmods[i] = oh; 17562306a36Sopenharmony_ci if (oh->flags & HWMOD_INIT_NO_IDLE) 17662306a36Sopenharmony_ci device_active = true; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci od = omap_device_alloc(pdev, hwmods, oh_cnt); 18062306a36Sopenharmony_ci if (IS_ERR(od)) { 18162306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot allocate omap_device for :%s\n", 18262306a36Sopenharmony_ci oh_name); 18362306a36Sopenharmony_ci ret = PTR_ERR(od); 18462306a36Sopenharmony_ci goto odbfd_exit1; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* Fix up missing resource names */ 18862306a36Sopenharmony_ci for (i = 0; i < pdev->num_resources; i++) { 18962306a36Sopenharmony_ci struct resource *r = &pdev->resource[i]; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (r->name == NULL) 19262306a36Sopenharmony_ci r->name = dev_name(&pdev->dev); 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (!skip_pm_domain) { 19662306a36Sopenharmony_ci dev_pm_domain_set(&pdev->dev, &omap_device_pm_domain); 19762306a36Sopenharmony_ci if (device_active) { 19862306a36Sopenharmony_ci omap_device_enable(pdev); 19962306a36Sopenharmony_ci pm_runtime_set_active(&pdev->dev); 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ciodbfd_exit1: 20462306a36Sopenharmony_ci kfree(hwmods); 20562306a36Sopenharmony_ciodbfd_exit: 20662306a36Sopenharmony_ci /* if data/we are at fault.. load up a fail handler */ 20762306a36Sopenharmony_ci if (ret) 20862306a36Sopenharmony_ci dev_pm_domain_set(&pdev->dev, &omap_device_fail_pm_domain); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return ret; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic int _omap_device_notifier_call(struct notifier_block *nb, 21462306a36Sopenharmony_ci unsigned long event, void *dev) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 21762306a36Sopenharmony_ci struct omap_device *od; 21862306a36Sopenharmony_ci int err; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci switch (event) { 22162306a36Sopenharmony_ci case BUS_NOTIFY_REMOVED_DEVICE: 22262306a36Sopenharmony_ci if (pdev->archdata.od) 22362306a36Sopenharmony_ci omap_device_delete(pdev->archdata.od); 22462306a36Sopenharmony_ci break; 22562306a36Sopenharmony_ci case BUS_NOTIFY_UNBOUND_DRIVER: 22662306a36Sopenharmony_ci od = to_omap_device(pdev); 22762306a36Sopenharmony_ci if (od && (od->_state == OMAP_DEVICE_STATE_ENABLED)) { 22862306a36Sopenharmony_ci dev_info(dev, "enabled after unload, idling\n"); 22962306a36Sopenharmony_ci err = omap_device_idle(pdev); 23062306a36Sopenharmony_ci if (err) 23162306a36Sopenharmony_ci dev_err(dev, "failed to idle\n"); 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci break; 23462306a36Sopenharmony_ci case BUS_NOTIFY_BIND_DRIVER: 23562306a36Sopenharmony_ci od = to_omap_device(pdev); 23662306a36Sopenharmony_ci if (od) { 23762306a36Sopenharmony_ci od->_driver_status = BUS_NOTIFY_BIND_DRIVER; 23862306a36Sopenharmony_ci if (od->_state == OMAP_DEVICE_STATE_ENABLED && 23962306a36Sopenharmony_ci pm_runtime_status_suspended(dev)) { 24062306a36Sopenharmony_ci pm_runtime_set_active(dev); 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci break; 24462306a36Sopenharmony_ci case BUS_NOTIFY_ADD_DEVICE: 24562306a36Sopenharmony_ci if (pdev->dev.of_node) 24662306a36Sopenharmony_ci omap_device_build_from_dt(pdev); 24762306a36Sopenharmony_ci fallthrough; 24862306a36Sopenharmony_ci default: 24962306a36Sopenharmony_ci od = to_omap_device(pdev); 25062306a36Sopenharmony_ci if (od) 25162306a36Sopenharmony_ci od->_driver_status = event; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return NOTIFY_DONE; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci/** 25862306a36Sopenharmony_ci * _omap_device_enable_hwmods - call omap_hwmod_enable() on all hwmods 25962306a36Sopenharmony_ci * @od: struct omap_device *od 26062306a36Sopenharmony_ci * 26162306a36Sopenharmony_ci * Enable all underlying hwmods. Returns 0. 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_cistatic int _omap_device_enable_hwmods(struct omap_device *od) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci int ret = 0; 26662306a36Sopenharmony_ci int i; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci for (i = 0; i < od->hwmods_cnt; i++) 26962306a36Sopenharmony_ci ret |= omap_hwmod_enable(od->hwmods[i]); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci return ret; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci/** 27562306a36Sopenharmony_ci * _omap_device_idle_hwmods - call omap_hwmod_idle() on all hwmods 27662306a36Sopenharmony_ci * @od: struct omap_device *od 27762306a36Sopenharmony_ci * 27862306a36Sopenharmony_ci * Idle all underlying hwmods. Returns 0. 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_cistatic int _omap_device_idle_hwmods(struct omap_device *od) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci int ret = 0; 28362306a36Sopenharmony_ci int i; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci for (i = 0; i < od->hwmods_cnt; i++) 28662306a36Sopenharmony_ci ret |= omap_hwmod_idle(od->hwmods[i]); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci return ret; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci/* Public functions for use by core code */ 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci/** 29462306a36Sopenharmony_ci * omap_device_alloc - allocate an omap_device 29562306a36Sopenharmony_ci * @pdev: platform_device that will be included in this omap_device 29662306a36Sopenharmony_ci * @ohs: ptr to the omap_hwmod for this omap_device 29762306a36Sopenharmony_ci * @oh_cnt: the size of the ohs list 29862306a36Sopenharmony_ci * 29962306a36Sopenharmony_ci * Convenience function for allocating an omap_device structure and filling 30062306a36Sopenharmony_ci * hwmods, and resources. 30162306a36Sopenharmony_ci * 30262306a36Sopenharmony_ci * Returns an struct omap_device pointer or ERR_PTR() on error; 30362306a36Sopenharmony_ci */ 30462306a36Sopenharmony_cistatic struct omap_device *omap_device_alloc(struct platform_device *pdev, 30562306a36Sopenharmony_ci struct omap_hwmod **ohs, int oh_cnt) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci int ret = -ENOMEM; 30862306a36Sopenharmony_ci struct omap_device *od; 30962306a36Sopenharmony_ci int i; 31062306a36Sopenharmony_ci struct omap_hwmod **hwmods; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci od = kzalloc(sizeof(struct omap_device), GFP_KERNEL); 31362306a36Sopenharmony_ci if (!od) 31462306a36Sopenharmony_ci goto oda_exit1; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci od->hwmods_cnt = oh_cnt; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci hwmods = kmemdup(ohs, sizeof(struct omap_hwmod *) * oh_cnt, GFP_KERNEL); 31962306a36Sopenharmony_ci if (!hwmods) 32062306a36Sopenharmony_ci goto oda_exit2; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci od->hwmods = hwmods; 32362306a36Sopenharmony_ci od->pdev = pdev; 32462306a36Sopenharmony_ci pdev->archdata.od = od; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci for (i = 0; i < oh_cnt; i++) { 32762306a36Sopenharmony_ci hwmods[i]->od = od; 32862306a36Sopenharmony_ci _add_hwmod_clocks_clkdev(od, hwmods[i]); 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci return od; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cioda_exit2: 33462306a36Sopenharmony_ci kfree(od); 33562306a36Sopenharmony_cioda_exit1: 33662306a36Sopenharmony_ci dev_err(&pdev->dev, "omap_device: build failed (%d)\n", ret); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return ERR_PTR(ret); 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic void omap_device_delete(struct omap_device *od) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci if (!od) 34462306a36Sopenharmony_ci return; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci od->pdev->archdata.od = NULL; 34762306a36Sopenharmony_ci kfree(od->hwmods); 34862306a36Sopenharmony_ci kfree(od); 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci#ifdef CONFIG_PM 35262306a36Sopenharmony_cistatic int _od_runtime_suspend(struct device *dev) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 35562306a36Sopenharmony_ci int ret; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci ret = pm_generic_runtime_suspend(dev); 35862306a36Sopenharmony_ci if (ret) 35962306a36Sopenharmony_ci return ret; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci return omap_device_idle(pdev); 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic int _od_runtime_resume(struct device *dev) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 36762306a36Sopenharmony_ci int ret; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci ret = omap_device_enable(pdev); 37062306a36Sopenharmony_ci if (ret) { 37162306a36Sopenharmony_ci dev_err(dev, "use pm_runtime_put_sync_suspend() in driver?\n"); 37262306a36Sopenharmony_ci return ret; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci return pm_generic_runtime_resume(dev); 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic int _od_fail_runtime_suspend(struct device *dev) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci dev_warn(dev, "%s: FIXME: missing hwmod/omap_dev info\n", __func__); 38162306a36Sopenharmony_ci return -ENODEV; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic int _od_fail_runtime_resume(struct device *dev) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci dev_warn(dev, "%s: FIXME: missing hwmod/omap_dev info\n", __func__); 38762306a36Sopenharmony_ci return -ENODEV; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci#endif 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci#ifdef CONFIG_SUSPEND 39362306a36Sopenharmony_cistatic int _od_suspend_noirq(struct device *dev) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 39662306a36Sopenharmony_ci struct omap_device *od = to_omap_device(pdev); 39762306a36Sopenharmony_ci int ret; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* Don't attempt late suspend on a driver that is not bound */ 40062306a36Sopenharmony_ci if (od->_driver_status != BUS_NOTIFY_BOUND_DRIVER) 40162306a36Sopenharmony_ci return 0; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci ret = pm_generic_suspend_noirq(dev); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (!ret && !pm_runtime_status_suspended(dev)) { 40662306a36Sopenharmony_ci if (pm_generic_runtime_suspend(dev) == 0) { 40762306a36Sopenharmony_ci omap_device_idle(pdev); 40862306a36Sopenharmony_ci od->flags |= OMAP_DEVICE_SUSPENDED; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci return ret; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic int _od_resume_noirq(struct device *dev) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 41862306a36Sopenharmony_ci struct omap_device *od = to_omap_device(pdev); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (od->flags & OMAP_DEVICE_SUSPENDED) { 42162306a36Sopenharmony_ci od->flags &= ~OMAP_DEVICE_SUSPENDED; 42262306a36Sopenharmony_ci omap_device_enable(pdev); 42362306a36Sopenharmony_ci pm_generic_runtime_resume(dev); 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci return pm_generic_resume_noirq(dev); 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci#else 42962306a36Sopenharmony_ci#define _od_suspend_noirq NULL 43062306a36Sopenharmony_ci#define _od_resume_noirq NULL 43162306a36Sopenharmony_ci#endif 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic struct dev_pm_domain omap_device_fail_pm_domain = { 43462306a36Sopenharmony_ci .ops = { 43562306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(_od_fail_runtime_suspend, 43662306a36Sopenharmony_ci _od_fail_runtime_resume, NULL) 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci}; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic struct dev_pm_domain omap_device_pm_domain = { 44162306a36Sopenharmony_ci .ops = { 44262306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(_od_runtime_suspend, _od_runtime_resume, 44362306a36Sopenharmony_ci NULL) 44462306a36Sopenharmony_ci USE_PLATFORM_PM_SLEEP_OPS 44562306a36Sopenharmony_ci SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(_od_suspend_noirq, 44662306a36Sopenharmony_ci _od_resume_noirq) 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci}; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci/* Public functions for use by device drivers through struct platform_data */ 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci/** 45362306a36Sopenharmony_ci * omap_device_enable - fully activate an omap_device 45462306a36Sopenharmony_ci * @pdev: the platform device to activate 45562306a36Sopenharmony_ci * 45662306a36Sopenharmony_ci * Do whatever is necessary for the hwmods underlying omap_device @od 45762306a36Sopenharmony_ci * to be accessible and ready to operate. This generally involves 45862306a36Sopenharmony_ci * enabling clocks, setting SYSCONFIG registers; and in the future may 45962306a36Sopenharmony_ci * involve remuxing pins. Device drivers should call this function 46062306a36Sopenharmony_ci * indirectly via pm_runtime_get*(). Returns -EINVAL if called when 46162306a36Sopenharmony_ci * the omap_device is already enabled, or passes along the return 46262306a36Sopenharmony_ci * value of _omap_device_enable_hwmods(). 46362306a36Sopenharmony_ci */ 46462306a36Sopenharmony_ciint omap_device_enable(struct platform_device *pdev) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci int ret; 46762306a36Sopenharmony_ci struct omap_device *od; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci od = to_omap_device(pdev); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (od->_state == OMAP_DEVICE_STATE_ENABLED) { 47262306a36Sopenharmony_ci dev_warn(&pdev->dev, 47362306a36Sopenharmony_ci "omap_device: %s() called from invalid state %d\n", 47462306a36Sopenharmony_ci __func__, od->_state); 47562306a36Sopenharmony_ci return -EINVAL; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci ret = _omap_device_enable_hwmods(od); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (ret == 0) 48162306a36Sopenharmony_ci od->_state = OMAP_DEVICE_STATE_ENABLED; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci return ret; 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci/** 48762306a36Sopenharmony_ci * omap_device_idle - idle an omap_device 48862306a36Sopenharmony_ci * @pdev: The platform_device (omap_device) to idle 48962306a36Sopenharmony_ci * 49062306a36Sopenharmony_ci * Idle omap_device @od. Device drivers call this function indirectly 49162306a36Sopenharmony_ci * via pm_runtime_put*(). Returns -EINVAL if the omap_device is not 49262306a36Sopenharmony_ci * currently enabled, or passes along the return value of 49362306a36Sopenharmony_ci * _omap_device_idle_hwmods(). 49462306a36Sopenharmony_ci */ 49562306a36Sopenharmony_ciint omap_device_idle(struct platform_device *pdev) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci int ret; 49862306a36Sopenharmony_ci struct omap_device *od; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci od = to_omap_device(pdev); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (od->_state != OMAP_DEVICE_STATE_ENABLED) { 50362306a36Sopenharmony_ci dev_warn(&pdev->dev, 50462306a36Sopenharmony_ci "omap_device: %s() called from invalid state %d\n", 50562306a36Sopenharmony_ci __func__, od->_state); 50662306a36Sopenharmony_ci return -EINVAL; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci ret = _omap_device_idle_hwmods(od); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (ret == 0) 51262306a36Sopenharmony_ci od->_state = OMAP_DEVICE_STATE_IDLE; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci return ret; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci/** 51862306a36Sopenharmony_ci * omap_device_assert_hardreset - set a device's hardreset line 51962306a36Sopenharmony_ci * @pdev: struct platform_device * to reset 52062306a36Sopenharmony_ci * @name: const char * name of the reset line 52162306a36Sopenharmony_ci * 52262306a36Sopenharmony_ci * Set the hardreset line identified by @name on the IP blocks 52362306a36Sopenharmony_ci * associated with the hwmods backing the platform_device @pdev. All 52462306a36Sopenharmony_ci * of the hwmods associated with @pdev must have the same hardreset 52562306a36Sopenharmony_ci * line linked to them for this to work. Passes along the return value 52662306a36Sopenharmony_ci * of omap_hwmod_assert_hardreset() in the event of any failure, or 52762306a36Sopenharmony_ci * returns 0 upon success. 52862306a36Sopenharmony_ci */ 52962306a36Sopenharmony_ciint omap_device_assert_hardreset(struct platform_device *pdev, const char *name) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci struct omap_device *od = to_omap_device(pdev); 53262306a36Sopenharmony_ci int ret = 0; 53362306a36Sopenharmony_ci int i; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci for (i = 0; i < od->hwmods_cnt; i++) { 53662306a36Sopenharmony_ci ret = omap_hwmod_assert_hardreset(od->hwmods[i], name); 53762306a36Sopenharmony_ci if (ret) 53862306a36Sopenharmony_ci break; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci return ret; 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci/** 54562306a36Sopenharmony_ci * omap_device_deassert_hardreset - release a device's hardreset line 54662306a36Sopenharmony_ci * @pdev: struct platform_device * to reset 54762306a36Sopenharmony_ci * @name: const char * name of the reset line 54862306a36Sopenharmony_ci * 54962306a36Sopenharmony_ci * Release the hardreset line identified by @name on the IP blocks 55062306a36Sopenharmony_ci * associated with the hwmods backing the platform_device @pdev. All 55162306a36Sopenharmony_ci * of the hwmods associated with @pdev must have the same hardreset 55262306a36Sopenharmony_ci * line linked to them for this to work. Passes along the return 55362306a36Sopenharmony_ci * value of omap_hwmod_deassert_hardreset() in the event of any 55462306a36Sopenharmony_ci * failure, or returns 0 upon success. 55562306a36Sopenharmony_ci */ 55662306a36Sopenharmony_ciint omap_device_deassert_hardreset(struct platform_device *pdev, 55762306a36Sopenharmony_ci const char *name) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci struct omap_device *od = to_omap_device(pdev); 56062306a36Sopenharmony_ci int ret = 0; 56162306a36Sopenharmony_ci int i; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci for (i = 0; i < od->hwmods_cnt; i++) { 56462306a36Sopenharmony_ci ret = omap_hwmod_deassert_hardreset(od->hwmods[i], name); 56562306a36Sopenharmony_ci if (ret) 56662306a36Sopenharmony_ci break; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci return ret; 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic struct notifier_block platform_nb = { 57362306a36Sopenharmony_ci .notifier_call = _omap_device_notifier_call, 57462306a36Sopenharmony_ci}; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_cistatic int __init omap_device_init(void) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci bus_register_notifier(&platform_bus_type, &platform_nb); 57962306a36Sopenharmony_ci return 0; 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ciomap_postcore_initcall(omap_device_init); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci/** 58462306a36Sopenharmony_ci * omap_device_late_idle - idle devices without drivers 58562306a36Sopenharmony_ci * @dev: struct device * associated with omap_device 58662306a36Sopenharmony_ci * @data: unused 58762306a36Sopenharmony_ci * 58862306a36Sopenharmony_ci * Check the driver bound status of this device, and idle it 58962306a36Sopenharmony_ci * if there is no driver attached. 59062306a36Sopenharmony_ci */ 59162306a36Sopenharmony_cistatic int __init omap_device_late_idle(struct device *dev, void *data) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 59462306a36Sopenharmony_ci struct omap_device *od = to_omap_device(pdev); 59562306a36Sopenharmony_ci int i; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (!od) 59862306a36Sopenharmony_ci return 0; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci /* 60162306a36Sopenharmony_ci * If omap_device state is enabled, but has no driver bound, 60262306a36Sopenharmony_ci * idle it. 60362306a36Sopenharmony_ci */ 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci /* 60662306a36Sopenharmony_ci * Some devices (like memory controllers) are always kept 60762306a36Sopenharmony_ci * enabled, and should not be idled even with no drivers. 60862306a36Sopenharmony_ci */ 60962306a36Sopenharmony_ci for (i = 0; i < od->hwmods_cnt; i++) 61062306a36Sopenharmony_ci if (od->hwmods[i]->flags & HWMOD_INIT_NO_IDLE) 61162306a36Sopenharmony_ci return 0; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (od->_driver_status != BUS_NOTIFY_BOUND_DRIVER && 61462306a36Sopenharmony_ci od->_driver_status != BUS_NOTIFY_BIND_DRIVER) { 61562306a36Sopenharmony_ci if (od->_state == OMAP_DEVICE_STATE_ENABLED) { 61662306a36Sopenharmony_ci dev_warn(dev, "%s: enabled but no driver. Idling\n", 61762306a36Sopenharmony_ci __func__); 61862306a36Sopenharmony_ci omap_device_idle(pdev); 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci return 0; 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic int __init omap_device_late_init(void) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci bus_for_each_dev(&platform_bus_type, NULL, NULL, omap_device_late_idle); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci return 0; 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ciomap_late_initcall_sync(omap_device_late_init); 632