125c7d49eSTony Lindgren /* 225c7d49eSTony Lindgren * omap_device implementation 325c7d49eSTony Lindgren * 425c7d49eSTony Lindgren * Copyright (C) 2009-2010 Nokia Corporation 525c7d49eSTony Lindgren * Paul Walmsley, Kevin Hilman 625c7d49eSTony Lindgren * 725c7d49eSTony Lindgren * Developed in collaboration with (alphabetical order): Benoit 825c7d49eSTony Lindgren * Cousson, Thara Gopinath, Tony Lindgren, Rajendra Nayak, Vikram 925c7d49eSTony Lindgren * Pandita, Sakari Poussa, Anand Sawant, Santosh Shilimkar, Richard 1025c7d49eSTony Lindgren * Woodruff 1125c7d49eSTony Lindgren * 1225c7d49eSTony Lindgren * This program is free software; you can redistribute it and/or modify 1325c7d49eSTony Lindgren * it under the terms of the GNU General Public License version 2 as 1425c7d49eSTony Lindgren * published by the Free Software Foundation. 1525c7d49eSTony Lindgren * 1625c7d49eSTony Lindgren * This code provides a consistent interface for OMAP device drivers 1725c7d49eSTony Lindgren * to control power management and interconnect properties of their 1825c7d49eSTony Lindgren * devices. 1925c7d49eSTony Lindgren * 2025c7d49eSTony Lindgren * In the medium- to long-term, this code should either be 2125c7d49eSTony Lindgren * a) implemented via arch-specific pointers in platform_data 2225c7d49eSTony Lindgren * or 2325c7d49eSTony Lindgren * b) implemented as a proper omap_bus/omap_device in Linux, no more 2425c7d49eSTony Lindgren * platform_data func pointers 2525c7d49eSTony Lindgren * 2625c7d49eSTony Lindgren * 2725c7d49eSTony Lindgren * Guidelines for usage by driver authors: 2825c7d49eSTony Lindgren * 2925c7d49eSTony Lindgren * 1. These functions are intended to be used by device drivers via 3025c7d49eSTony Lindgren * function pointers in struct platform_data. As an example, 3125c7d49eSTony Lindgren * omap_device_enable() should be passed to the driver as 3225c7d49eSTony Lindgren * 3325c7d49eSTony Lindgren * struct foo_driver_platform_data { 3425c7d49eSTony Lindgren * ... 3525c7d49eSTony Lindgren * int (*device_enable)(struct platform_device *pdev); 3625c7d49eSTony Lindgren * ... 3725c7d49eSTony Lindgren * } 3825c7d49eSTony Lindgren * 3925c7d49eSTony Lindgren * Note that the generic "device_enable" name is used, rather than 4025c7d49eSTony Lindgren * "omap_device_enable". This is so other architectures can pass in their 4125c7d49eSTony Lindgren * own enable/disable functions here. 4225c7d49eSTony Lindgren * 4325c7d49eSTony Lindgren * This should be populated during device setup: 4425c7d49eSTony Lindgren * 4525c7d49eSTony Lindgren * ... 4625c7d49eSTony Lindgren * pdata->device_enable = omap_device_enable; 4725c7d49eSTony Lindgren * ... 4825c7d49eSTony Lindgren * 4925c7d49eSTony Lindgren * 2. Drivers should first check to ensure the function pointer is not null 5025c7d49eSTony Lindgren * before calling it, as in: 5125c7d49eSTony Lindgren * 5225c7d49eSTony Lindgren * if (pdata->device_enable) 5325c7d49eSTony Lindgren * pdata->device_enable(pdev); 5425c7d49eSTony Lindgren * 5525c7d49eSTony Lindgren * This allows other architectures that don't use similar device_enable()/ 5625c7d49eSTony Lindgren * device_shutdown() functions to execute normally. 5725c7d49eSTony Lindgren * 5825c7d49eSTony Lindgren * ... 5925c7d49eSTony Lindgren * 6025c7d49eSTony Lindgren * Suggested usage by device drivers: 6125c7d49eSTony Lindgren * 6225c7d49eSTony Lindgren * During device initialization: 6325c7d49eSTony Lindgren * device_enable() 6425c7d49eSTony Lindgren * 6525c7d49eSTony Lindgren * During device idle: 6625c7d49eSTony Lindgren * (save remaining device context if necessary) 6725c7d49eSTony Lindgren * device_idle(); 6825c7d49eSTony Lindgren * 6925c7d49eSTony Lindgren * During device resume: 7025c7d49eSTony Lindgren * device_enable(); 7125c7d49eSTony Lindgren * (restore context if necessary) 7225c7d49eSTony Lindgren * 7325c7d49eSTony Lindgren * During device shutdown: 7425c7d49eSTony Lindgren * device_shutdown() 7525c7d49eSTony Lindgren * (device must be reinitialized at this point to use it again) 7625c7d49eSTony Lindgren * 7725c7d49eSTony Lindgren */ 7825c7d49eSTony Lindgren #undef DEBUG 7925c7d49eSTony Lindgren 8025c7d49eSTony Lindgren #include <linux/kernel.h> 8125c7d49eSTony Lindgren #include <linux/export.h> 8225c7d49eSTony Lindgren #include <linux/platform_device.h> 8325c7d49eSTony Lindgren #include <linux/slab.h> 8425c7d49eSTony Lindgren #include <linux/err.h> 8525c7d49eSTony Lindgren #include <linux/io.h> 8625c7d49eSTony Lindgren #include <linux/clk.h> 8725c7d49eSTony Lindgren #include <linux/clkdev.h> 8825c7d49eSTony Lindgren #include <linux/pm_runtime.h> 8925c7d49eSTony Lindgren #include <linux/of.h> 9025c7d49eSTony Lindgren #include <linux/notifier.h> 9125c7d49eSTony Lindgren 9225c7d49eSTony Lindgren #include "omap_device.h" 932a296c8fSTony Lindgren #include "omap_hwmod.h" 9425c7d49eSTony Lindgren #include <plat/clock.h> 9525c7d49eSTony Lindgren 9625c7d49eSTony Lindgren /* These parameters are passed to _omap_device_{de,}activate() */ 9725c7d49eSTony Lindgren #define USE_WAKEUP_LAT 0 9825c7d49eSTony Lindgren #define IGNORE_WAKEUP_LAT 1 9925c7d49eSTony Lindgren 10025c7d49eSTony Lindgren static int omap_early_device_register(struct platform_device *pdev); 10125c7d49eSTony Lindgren 10225c7d49eSTony Lindgren static struct omap_device_pm_latency omap_default_latency[] = { 10325c7d49eSTony Lindgren { 10425c7d49eSTony Lindgren .deactivate_func = omap_device_idle_hwmods, 10525c7d49eSTony Lindgren .activate_func = omap_device_enable_hwmods, 10625c7d49eSTony Lindgren .flags = OMAP_DEVICE_LATENCY_AUTO_ADJUST, 10725c7d49eSTony Lindgren } 10825c7d49eSTony Lindgren }; 10925c7d49eSTony Lindgren 11025c7d49eSTony Lindgren /* Private functions */ 11125c7d49eSTony Lindgren 11225c7d49eSTony Lindgren /** 11325c7d49eSTony Lindgren * _omap_device_activate - increase device readiness 11425c7d49eSTony Lindgren * @od: struct omap_device * 11525c7d49eSTony Lindgren * @ignore_lat: increase to latency target (0) or full readiness (1)? 11625c7d49eSTony Lindgren * 11725c7d49eSTony Lindgren * Increase readiness of omap_device @od (thus decreasing device 11825c7d49eSTony Lindgren * wakeup latency, but consuming more power). If @ignore_lat is 11925c7d49eSTony Lindgren * IGNORE_WAKEUP_LAT, make the omap_device fully active. Otherwise, 12025c7d49eSTony Lindgren * if @ignore_lat is USE_WAKEUP_LAT, and the device's maximum wakeup 12125c7d49eSTony Lindgren * latency is greater than the requested maximum wakeup latency, step 12225c7d49eSTony Lindgren * backwards in the omap_device_pm_latency table to ensure the 12325c7d49eSTony Lindgren * device's maximum wakeup latency is less than or equal to the 12425c7d49eSTony Lindgren * requested maximum wakeup latency. Returns 0. 12525c7d49eSTony Lindgren */ 12625c7d49eSTony Lindgren static int _omap_device_activate(struct omap_device *od, u8 ignore_lat) 12725c7d49eSTony Lindgren { 12825c7d49eSTony Lindgren struct timespec a, b, c; 12925c7d49eSTony Lindgren 13025c7d49eSTony Lindgren dev_dbg(&od->pdev->dev, "omap_device: activating\n"); 13125c7d49eSTony Lindgren 13225c7d49eSTony Lindgren while (od->pm_lat_level > 0) { 13325c7d49eSTony Lindgren struct omap_device_pm_latency *odpl; 13425c7d49eSTony Lindgren unsigned long long act_lat = 0; 13525c7d49eSTony Lindgren 13625c7d49eSTony Lindgren od->pm_lat_level--; 13725c7d49eSTony Lindgren 13825c7d49eSTony Lindgren odpl = od->pm_lats + od->pm_lat_level; 13925c7d49eSTony Lindgren 14025c7d49eSTony Lindgren if (!ignore_lat && 14125c7d49eSTony Lindgren (od->dev_wakeup_lat <= od->_dev_wakeup_lat_limit)) 14225c7d49eSTony Lindgren break; 14325c7d49eSTony Lindgren 14425c7d49eSTony Lindgren read_persistent_clock(&a); 14525c7d49eSTony Lindgren 14625c7d49eSTony Lindgren /* XXX check return code */ 14725c7d49eSTony Lindgren odpl->activate_func(od); 14825c7d49eSTony Lindgren 14925c7d49eSTony Lindgren read_persistent_clock(&b); 15025c7d49eSTony Lindgren 15125c7d49eSTony Lindgren c = timespec_sub(b, a); 15225c7d49eSTony Lindgren act_lat = timespec_to_ns(&c); 15325c7d49eSTony Lindgren 15425c7d49eSTony Lindgren dev_dbg(&od->pdev->dev, 15525c7d49eSTony Lindgren "omap_device: pm_lat %d: activate: elapsed time %llu nsec\n", 15625c7d49eSTony Lindgren od->pm_lat_level, act_lat); 15725c7d49eSTony Lindgren 15825c7d49eSTony Lindgren if (act_lat > odpl->activate_lat) { 15925c7d49eSTony Lindgren odpl->activate_lat_worst = act_lat; 16025c7d49eSTony Lindgren if (odpl->flags & OMAP_DEVICE_LATENCY_AUTO_ADJUST) { 16125c7d49eSTony Lindgren odpl->activate_lat = act_lat; 16225c7d49eSTony Lindgren dev_dbg(&od->pdev->dev, 16325c7d49eSTony Lindgren "new worst case activate latency %d: %llu\n", 16425c7d49eSTony Lindgren od->pm_lat_level, act_lat); 16525c7d49eSTony Lindgren } else 16625c7d49eSTony Lindgren dev_warn(&od->pdev->dev, 16725c7d49eSTony Lindgren "activate latency %d higher than expected. (%llu > %d)\n", 16825c7d49eSTony Lindgren od->pm_lat_level, act_lat, 16925c7d49eSTony Lindgren odpl->activate_lat); 17025c7d49eSTony Lindgren } 17125c7d49eSTony Lindgren 17225c7d49eSTony Lindgren od->dev_wakeup_lat -= odpl->activate_lat; 17325c7d49eSTony Lindgren } 17425c7d49eSTony Lindgren 17525c7d49eSTony Lindgren return 0; 17625c7d49eSTony Lindgren } 17725c7d49eSTony Lindgren 17825c7d49eSTony Lindgren /** 17925c7d49eSTony Lindgren * _omap_device_deactivate - decrease device readiness 18025c7d49eSTony Lindgren * @od: struct omap_device * 18125c7d49eSTony Lindgren * @ignore_lat: decrease to latency target (0) or full inactivity (1)? 18225c7d49eSTony Lindgren * 18325c7d49eSTony Lindgren * Decrease readiness of omap_device @od (thus increasing device 18425c7d49eSTony Lindgren * wakeup latency, but conserving power). If @ignore_lat is 18525c7d49eSTony Lindgren * IGNORE_WAKEUP_LAT, make the omap_device fully inactive. Otherwise, 18625c7d49eSTony Lindgren * if @ignore_lat is USE_WAKEUP_LAT, and the device's maximum wakeup 18725c7d49eSTony Lindgren * latency is less than the requested maximum wakeup latency, step 18825c7d49eSTony Lindgren * forwards in the omap_device_pm_latency table to ensure the device's 18925c7d49eSTony Lindgren * maximum wakeup latency is less than or equal to the requested 19025c7d49eSTony Lindgren * maximum wakeup latency. Returns 0. 19125c7d49eSTony Lindgren */ 19225c7d49eSTony Lindgren static int _omap_device_deactivate(struct omap_device *od, u8 ignore_lat) 19325c7d49eSTony Lindgren { 19425c7d49eSTony Lindgren struct timespec a, b, c; 19525c7d49eSTony Lindgren 19625c7d49eSTony Lindgren dev_dbg(&od->pdev->dev, "omap_device: deactivating\n"); 19725c7d49eSTony Lindgren 19825c7d49eSTony Lindgren while (od->pm_lat_level < od->pm_lats_cnt) { 19925c7d49eSTony Lindgren struct omap_device_pm_latency *odpl; 20025c7d49eSTony Lindgren unsigned long long deact_lat = 0; 20125c7d49eSTony Lindgren 20225c7d49eSTony Lindgren odpl = od->pm_lats + od->pm_lat_level; 20325c7d49eSTony Lindgren 20425c7d49eSTony Lindgren if (!ignore_lat && 20525c7d49eSTony Lindgren ((od->dev_wakeup_lat + odpl->activate_lat) > 20625c7d49eSTony Lindgren od->_dev_wakeup_lat_limit)) 20725c7d49eSTony Lindgren break; 20825c7d49eSTony Lindgren 20925c7d49eSTony Lindgren read_persistent_clock(&a); 21025c7d49eSTony Lindgren 21125c7d49eSTony Lindgren /* XXX check return code */ 21225c7d49eSTony Lindgren odpl->deactivate_func(od); 21325c7d49eSTony Lindgren 21425c7d49eSTony Lindgren read_persistent_clock(&b); 21525c7d49eSTony Lindgren 21625c7d49eSTony Lindgren c = timespec_sub(b, a); 21725c7d49eSTony Lindgren deact_lat = timespec_to_ns(&c); 21825c7d49eSTony Lindgren 21925c7d49eSTony Lindgren dev_dbg(&od->pdev->dev, 22025c7d49eSTony Lindgren "omap_device: pm_lat %d: deactivate: elapsed time %llu nsec\n", 22125c7d49eSTony Lindgren od->pm_lat_level, deact_lat); 22225c7d49eSTony Lindgren 22325c7d49eSTony Lindgren if (deact_lat > odpl->deactivate_lat) { 22425c7d49eSTony Lindgren odpl->deactivate_lat_worst = deact_lat; 22525c7d49eSTony Lindgren if (odpl->flags & OMAP_DEVICE_LATENCY_AUTO_ADJUST) { 22625c7d49eSTony Lindgren odpl->deactivate_lat = deact_lat; 22725c7d49eSTony Lindgren dev_dbg(&od->pdev->dev, 22825c7d49eSTony Lindgren "new worst case deactivate latency %d: %llu\n", 22925c7d49eSTony Lindgren od->pm_lat_level, deact_lat); 23025c7d49eSTony Lindgren } else 23125c7d49eSTony Lindgren dev_warn(&od->pdev->dev, 23225c7d49eSTony Lindgren "deactivate latency %d higher than expected. (%llu > %d)\n", 23325c7d49eSTony Lindgren od->pm_lat_level, deact_lat, 23425c7d49eSTony Lindgren odpl->deactivate_lat); 23525c7d49eSTony Lindgren } 23625c7d49eSTony Lindgren 23725c7d49eSTony Lindgren od->dev_wakeup_lat += odpl->activate_lat; 23825c7d49eSTony Lindgren 23925c7d49eSTony Lindgren od->pm_lat_level++; 24025c7d49eSTony Lindgren } 24125c7d49eSTony Lindgren 24225c7d49eSTony Lindgren return 0; 24325c7d49eSTony Lindgren } 24425c7d49eSTony Lindgren 24525c7d49eSTony Lindgren static void _add_clkdev(struct omap_device *od, const char *clk_alias, 24625c7d49eSTony Lindgren const char *clk_name) 24725c7d49eSTony Lindgren { 24825c7d49eSTony Lindgren struct clk *r; 24925c7d49eSTony Lindgren struct clk_lookup *l; 25025c7d49eSTony Lindgren 25125c7d49eSTony Lindgren if (!clk_alias || !clk_name) 25225c7d49eSTony Lindgren return; 25325c7d49eSTony Lindgren 25425c7d49eSTony Lindgren dev_dbg(&od->pdev->dev, "Creating %s -> %s\n", clk_alias, clk_name); 25525c7d49eSTony Lindgren 25625c7d49eSTony Lindgren r = clk_get_sys(dev_name(&od->pdev->dev), clk_alias); 25725c7d49eSTony Lindgren if (!IS_ERR(r)) { 25825c7d49eSTony Lindgren dev_warn(&od->pdev->dev, 25925c7d49eSTony Lindgren "alias %s already exists\n", clk_alias); 26025c7d49eSTony Lindgren clk_put(r); 26125c7d49eSTony Lindgren return; 26225c7d49eSTony Lindgren } 26325c7d49eSTony Lindgren 26425c7d49eSTony Lindgren r = clk_get(NULL, clk_name); 26525c7d49eSTony Lindgren if (IS_ERR(r)) { 26625c7d49eSTony Lindgren dev_err(&od->pdev->dev, 26725c7d49eSTony Lindgren "clk_get for %s failed\n", clk_name); 26825c7d49eSTony Lindgren return; 26925c7d49eSTony Lindgren } 27025c7d49eSTony Lindgren 27125c7d49eSTony Lindgren l = clkdev_alloc(r, clk_alias, dev_name(&od->pdev->dev)); 27225c7d49eSTony Lindgren if (!l) { 27325c7d49eSTony Lindgren dev_err(&od->pdev->dev, 27425c7d49eSTony Lindgren "clkdev_alloc for %s failed\n", clk_alias); 27525c7d49eSTony Lindgren return; 27625c7d49eSTony Lindgren } 27725c7d49eSTony Lindgren 27825c7d49eSTony Lindgren clkdev_add(l); 27925c7d49eSTony Lindgren } 28025c7d49eSTony Lindgren 28125c7d49eSTony Lindgren /** 28225c7d49eSTony Lindgren * _add_hwmod_clocks_clkdev - Add clkdev entry for hwmod optional clocks 28325c7d49eSTony Lindgren * and main clock 28425c7d49eSTony Lindgren * @od: struct omap_device *od 28525c7d49eSTony Lindgren * @oh: struct omap_hwmod *oh 28625c7d49eSTony Lindgren * 28725c7d49eSTony Lindgren * For the main clock and every optional clock present per hwmod per 28825c7d49eSTony Lindgren * omap_device, this function adds an entry in the clkdev table of the 28925c7d49eSTony Lindgren * form <dev-id=dev_name, con-id=role> if it does not exist already. 29025c7d49eSTony Lindgren * 29125c7d49eSTony Lindgren * The function is called from inside omap_device_build_ss(), after 29225c7d49eSTony Lindgren * omap_device_register. 29325c7d49eSTony Lindgren * 29425c7d49eSTony Lindgren * This allows drivers to get a pointer to its optional clocks based on its role 29525c7d49eSTony Lindgren * by calling clk_get(<dev*>, <role>). 29625c7d49eSTony Lindgren * In the case of the main clock, a "fck" alias is used. 29725c7d49eSTony Lindgren * 29825c7d49eSTony Lindgren * No return value. 29925c7d49eSTony Lindgren */ 30025c7d49eSTony Lindgren static void _add_hwmod_clocks_clkdev(struct omap_device *od, 30125c7d49eSTony Lindgren struct omap_hwmod *oh) 30225c7d49eSTony Lindgren { 30325c7d49eSTony Lindgren int i; 30425c7d49eSTony Lindgren 30525c7d49eSTony Lindgren _add_clkdev(od, "fck", oh->main_clk); 30625c7d49eSTony Lindgren 30725c7d49eSTony Lindgren for (i = 0; i < oh->opt_clks_cnt; i++) 30825c7d49eSTony Lindgren _add_clkdev(od, oh->opt_clks[i].role, oh->opt_clks[i].clk); 30925c7d49eSTony Lindgren } 31025c7d49eSTony Lindgren 31125c7d49eSTony Lindgren 31225c7d49eSTony Lindgren /** 31325c7d49eSTony Lindgren * omap_device_build_from_dt - build an omap_device with multiple hwmods 31425c7d49eSTony Lindgren * @pdev_name: name of the platform_device driver to use 31525c7d49eSTony Lindgren * @pdev_id: this platform_device's connection ID 31625c7d49eSTony Lindgren * @oh: ptr to the single omap_hwmod that backs this omap_device 31725c7d49eSTony Lindgren * @pdata: platform_data ptr to associate with the platform_device 31825c7d49eSTony Lindgren * @pdata_len: amount of memory pointed to by @pdata 31925c7d49eSTony Lindgren * @pm_lats: pointer to a omap_device_pm_latency array for this device 32025c7d49eSTony Lindgren * @pm_lats_cnt: ARRAY_SIZE() of @pm_lats 32125c7d49eSTony Lindgren * @is_early_device: should the device be registered as an early device or not 32225c7d49eSTony Lindgren * 32325c7d49eSTony Lindgren * Function for building an omap_device already registered from device-tree 32425c7d49eSTony Lindgren * 32525c7d49eSTony Lindgren * Returns 0 or PTR_ERR() on error. 32625c7d49eSTony Lindgren */ 32725c7d49eSTony Lindgren static int omap_device_build_from_dt(struct platform_device *pdev) 32825c7d49eSTony Lindgren { 32925c7d49eSTony Lindgren struct omap_hwmod **hwmods; 33025c7d49eSTony Lindgren struct omap_device *od; 33125c7d49eSTony Lindgren struct omap_hwmod *oh; 33225c7d49eSTony Lindgren struct device_node *node = pdev->dev.of_node; 33325c7d49eSTony Lindgren const char *oh_name; 33425c7d49eSTony Lindgren int oh_cnt, i, ret = 0; 33525c7d49eSTony Lindgren 33625c7d49eSTony Lindgren oh_cnt = of_property_count_strings(node, "ti,hwmods"); 33725c7d49eSTony Lindgren if (!oh_cnt || IS_ERR_VALUE(oh_cnt)) { 33825c7d49eSTony Lindgren dev_dbg(&pdev->dev, "No 'hwmods' to build omap_device\n"); 33925c7d49eSTony Lindgren return -ENODEV; 34025c7d49eSTony Lindgren } 34125c7d49eSTony Lindgren 34225c7d49eSTony Lindgren hwmods = kzalloc(sizeof(struct omap_hwmod *) * oh_cnt, GFP_KERNEL); 34325c7d49eSTony Lindgren if (!hwmods) { 34425c7d49eSTony Lindgren ret = -ENOMEM; 34525c7d49eSTony Lindgren goto odbfd_exit; 34625c7d49eSTony Lindgren } 34725c7d49eSTony Lindgren 34825c7d49eSTony Lindgren for (i = 0; i < oh_cnt; i++) { 34925c7d49eSTony Lindgren of_property_read_string_index(node, "ti,hwmods", i, &oh_name); 35025c7d49eSTony Lindgren oh = omap_hwmod_lookup(oh_name); 35125c7d49eSTony Lindgren if (!oh) { 35225c7d49eSTony Lindgren dev_err(&pdev->dev, "Cannot lookup hwmod '%s'\n", 35325c7d49eSTony Lindgren oh_name); 35425c7d49eSTony Lindgren ret = -EINVAL; 35525c7d49eSTony Lindgren goto odbfd_exit1; 35625c7d49eSTony Lindgren } 35725c7d49eSTony Lindgren hwmods[i] = oh; 35825c7d49eSTony Lindgren } 35925c7d49eSTony Lindgren 36025c7d49eSTony Lindgren od = omap_device_alloc(pdev, hwmods, oh_cnt, NULL, 0); 36125c7d49eSTony Lindgren if (!od) { 36225c7d49eSTony Lindgren dev_err(&pdev->dev, "Cannot allocate omap_device for :%s\n", 36325c7d49eSTony Lindgren oh_name); 36425c7d49eSTony Lindgren ret = PTR_ERR(od); 36525c7d49eSTony Lindgren goto odbfd_exit1; 36625c7d49eSTony Lindgren } 36725c7d49eSTony Lindgren 36825c7d49eSTony Lindgren /* Fix up missing resource names */ 36925c7d49eSTony Lindgren for (i = 0; i < pdev->num_resources; i++) { 37025c7d49eSTony Lindgren struct resource *r = &pdev->resource[i]; 37125c7d49eSTony Lindgren 37225c7d49eSTony Lindgren if (r->name == NULL) 37325c7d49eSTony Lindgren r->name = dev_name(&pdev->dev); 37425c7d49eSTony Lindgren } 37525c7d49eSTony Lindgren 37625c7d49eSTony Lindgren if (of_get_property(node, "ti,no_idle_on_suspend", NULL)) 37725c7d49eSTony Lindgren omap_device_disable_idle_on_suspend(pdev); 37825c7d49eSTony Lindgren 37925c7d49eSTony Lindgren pdev->dev.pm_domain = &omap_device_pm_domain; 38025c7d49eSTony Lindgren 38125c7d49eSTony Lindgren odbfd_exit1: 38225c7d49eSTony Lindgren kfree(hwmods); 38325c7d49eSTony Lindgren odbfd_exit: 38425c7d49eSTony Lindgren return ret; 38525c7d49eSTony Lindgren } 38625c7d49eSTony Lindgren 38725c7d49eSTony Lindgren static int _omap_device_notifier_call(struct notifier_block *nb, 38825c7d49eSTony Lindgren unsigned long event, void *dev) 38925c7d49eSTony Lindgren { 39025c7d49eSTony Lindgren struct platform_device *pdev = to_platform_device(dev); 39125c7d49eSTony Lindgren struct omap_device *od; 39225c7d49eSTony Lindgren 39325c7d49eSTony Lindgren switch (event) { 39425c7d49eSTony Lindgren case BUS_NOTIFY_DEL_DEVICE: 39525c7d49eSTony Lindgren if (pdev->archdata.od) 39625c7d49eSTony Lindgren omap_device_delete(pdev->archdata.od); 39725c7d49eSTony Lindgren break; 39825c7d49eSTony Lindgren case BUS_NOTIFY_ADD_DEVICE: 39925c7d49eSTony Lindgren if (pdev->dev.of_node) 40025c7d49eSTony Lindgren omap_device_build_from_dt(pdev); 40125c7d49eSTony Lindgren /* fall through */ 40225c7d49eSTony Lindgren default: 40325c7d49eSTony Lindgren od = to_omap_device(pdev); 40425c7d49eSTony Lindgren if (od) 40525c7d49eSTony Lindgren od->_driver_status = event; 40625c7d49eSTony Lindgren } 40725c7d49eSTony Lindgren 40825c7d49eSTony Lindgren return NOTIFY_DONE; 40925c7d49eSTony Lindgren } 41025c7d49eSTony Lindgren 41125c7d49eSTony Lindgren 41225c7d49eSTony Lindgren /* Public functions for use by core code */ 41325c7d49eSTony Lindgren 41425c7d49eSTony Lindgren /** 41525c7d49eSTony Lindgren * omap_device_get_context_loss_count - get lost context count 41625c7d49eSTony Lindgren * @od: struct omap_device * 41725c7d49eSTony Lindgren * 41825c7d49eSTony Lindgren * Using the primary hwmod, query the context loss count for this 41925c7d49eSTony Lindgren * device. 42025c7d49eSTony Lindgren * 42125c7d49eSTony Lindgren * Callers should consider context for this device lost any time this 42225c7d49eSTony Lindgren * function returns a value different than the value the caller got 42325c7d49eSTony Lindgren * the last time it called this function. 42425c7d49eSTony Lindgren * 42525c7d49eSTony Lindgren * If any hwmods exist for the omap_device assoiated with @pdev, 42625c7d49eSTony Lindgren * return the context loss counter for that hwmod, otherwise return 42725c7d49eSTony Lindgren * zero. 42825c7d49eSTony Lindgren */ 42925c7d49eSTony Lindgren int omap_device_get_context_loss_count(struct platform_device *pdev) 43025c7d49eSTony Lindgren { 43125c7d49eSTony Lindgren struct omap_device *od; 43225c7d49eSTony Lindgren u32 ret = 0; 43325c7d49eSTony Lindgren 43425c7d49eSTony Lindgren od = to_omap_device(pdev); 43525c7d49eSTony Lindgren 43625c7d49eSTony Lindgren if (od->hwmods_cnt) 43725c7d49eSTony Lindgren ret = omap_hwmod_get_context_loss_count(od->hwmods[0]); 43825c7d49eSTony Lindgren 43925c7d49eSTony Lindgren return ret; 44025c7d49eSTony Lindgren } 44125c7d49eSTony Lindgren 44225c7d49eSTony Lindgren /** 44325c7d49eSTony Lindgren * omap_device_count_resources - count number of struct resource entries needed 44425c7d49eSTony Lindgren * @od: struct omap_device * 44525c7d49eSTony Lindgren * 44625c7d49eSTony Lindgren * Count the number of struct resource entries needed for this 44725c7d49eSTony Lindgren * omap_device @od. Used by omap_device_build_ss() to determine how 44825c7d49eSTony Lindgren * much memory to allocate before calling 44925c7d49eSTony Lindgren * omap_device_fill_resources(). Returns the count. 45025c7d49eSTony Lindgren */ 45125c7d49eSTony Lindgren static int omap_device_count_resources(struct omap_device *od) 45225c7d49eSTony Lindgren { 45325c7d49eSTony Lindgren int c = 0; 45425c7d49eSTony Lindgren int i; 45525c7d49eSTony Lindgren 45625c7d49eSTony Lindgren for (i = 0; i < od->hwmods_cnt; i++) 45725c7d49eSTony Lindgren c += omap_hwmod_count_resources(od->hwmods[i]); 45825c7d49eSTony Lindgren 45925c7d49eSTony Lindgren pr_debug("omap_device: %s: counted %d total resources across %d hwmods\n", 46025c7d49eSTony Lindgren od->pdev->name, c, od->hwmods_cnt); 46125c7d49eSTony Lindgren 46225c7d49eSTony Lindgren return c; 46325c7d49eSTony Lindgren } 46425c7d49eSTony Lindgren 46525c7d49eSTony Lindgren /** 46625c7d49eSTony Lindgren * omap_device_fill_resources - fill in array of struct resource 46725c7d49eSTony Lindgren * @od: struct omap_device * 46825c7d49eSTony Lindgren * @res: pointer to an array of struct resource to be filled in 46925c7d49eSTony Lindgren * 47025c7d49eSTony Lindgren * Populate one or more empty struct resource pointed to by @res with 47125c7d49eSTony Lindgren * the resource data for this omap_device @od. Used by 47225c7d49eSTony Lindgren * omap_device_build_ss() after calling omap_device_count_resources(). 47325c7d49eSTony Lindgren * Ideally this function would not be needed at all. If omap_device 47425c7d49eSTony Lindgren * replaces platform_device, then we can specify our own 47525c7d49eSTony Lindgren * get_resource()/ get_irq()/etc functions that use the underlying 47625c7d49eSTony Lindgren * omap_hwmod information. Or if platform_device is extended to use 47725c7d49eSTony Lindgren * subarchitecture-specific function pointers, the various 47825c7d49eSTony Lindgren * platform_device functions can simply call omap_device internal 47925c7d49eSTony Lindgren * functions to get device resources. Hacking around the existing 48025c7d49eSTony Lindgren * platform_device code wastes memory. Returns 0. 48125c7d49eSTony Lindgren */ 48225c7d49eSTony Lindgren static int omap_device_fill_resources(struct omap_device *od, 48325c7d49eSTony Lindgren struct resource *res) 48425c7d49eSTony Lindgren { 48525c7d49eSTony Lindgren int i, r; 48625c7d49eSTony Lindgren 48725c7d49eSTony Lindgren for (i = 0; i < od->hwmods_cnt; i++) { 48825c7d49eSTony Lindgren r = omap_hwmod_fill_resources(od->hwmods[i], res); 48925c7d49eSTony Lindgren res += r; 49025c7d49eSTony Lindgren } 49125c7d49eSTony Lindgren 49225c7d49eSTony Lindgren return 0; 49325c7d49eSTony Lindgren } 49425c7d49eSTony Lindgren 49525c7d49eSTony Lindgren /** 49625c7d49eSTony Lindgren * _od_fill_dma_resources - fill in array of struct resource with dma resources 49725c7d49eSTony Lindgren * @od: struct omap_device * 49825c7d49eSTony Lindgren * @res: pointer to an array of struct resource to be filled in 49925c7d49eSTony Lindgren * 50025c7d49eSTony Lindgren * Populate one or more empty struct resource pointed to by @res with 50125c7d49eSTony Lindgren * the dma resource data for this omap_device @od. Used by 50225c7d49eSTony Lindgren * omap_device_alloc() after calling omap_device_count_resources(). 50325c7d49eSTony Lindgren * 50425c7d49eSTony Lindgren * Ideally this function would not be needed at all. If we have 50525c7d49eSTony Lindgren * mechanism to get dma resources from DT. 50625c7d49eSTony Lindgren * 50725c7d49eSTony Lindgren * Returns 0. 50825c7d49eSTony Lindgren */ 50925c7d49eSTony Lindgren static int _od_fill_dma_resources(struct omap_device *od, 51025c7d49eSTony Lindgren struct resource *res) 51125c7d49eSTony Lindgren { 51225c7d49eSTony Lindgren int i, r; 51325c7d49eSTony Lindgren 51425c7d49eSTony Lindgren for (i = 0; i < od->hwmods_cnt; i++) { 51525c7d49eSTony Lindgren r = omap_hwmod_fill_dma_resources(od->hwmods[i], res); 51625c7d49eSTony Lindgren res += r; 51725c7d49eSTony Lindgren } 51825c7d49eSTony Lindgren 51925c7d49eSTony Lindgren return 0; 52025c7d49eSTony Lindgren } 52125c7d49eSTony Lindgren 52225c7d49eSTony Lindgren /** 52325c7d49eSTony Lindgren * omap_device_alloc - allocate an omap_device 52425c7d49eSTony Lindgren * @pdev: platform_device that will be included in this omap_device 52525c7d49eSTony Lindgren * @oh: ptr to the single omap_hwmod that backs this omap_device 52625c7d49eSTony Lindgren * @pdata: platform_data ptr to associate with the platform_device 52725c7d49eSTony Lindgren * @pdata_len: amount of memory pointed to by @pdata 52825c7d49eSTony Lindgren * @pm_lats: pointer to a omap_device_pm_latency array for this device 52925c7d49eSTony Lindgren * @pm_lats_cnt: ARRAY_SIZE() of @pm_lats 53025c7d49eSTony Lindgren * 53125c7d49eSTony Lindgren * Convenience function for allocating an omap_device structure and filling 53225c7d49eSTony Lindgren * hwmods, resources and pm_latency attributes. 53325c7d49eSTony Lindgren * 53425c7d49eSTony Lindgren * Returns an struct omap_device pointer or ERR_PTR() on error; 53525c7d49eSTony Lindgren */ 53625c7d49eSTony Lindgren struct omap_device *omap_device_alloc(struct platform_device *pdev, 53725c7d49eSTony Lindgren struct omap_hwmod **ohs, int oh_cnt, 53825c7d49eSTony Lindgren struct omap_device_pm_latency *pm_lats, 53925c7d49eSTony Lindgren int pm_lats_cnt) 54025c7d49eSTony Lindgren { 54125c7d49eSTony Lindgren int ret = -ENOMEM; 54225c7d49eSTony Lindgren struct omap_device *od; 54325c7d49eSTony Lindgren struct resource *res = NULL; 54425c7d49eSTony Lindgren int i, res_count; 54525c7d49eSTony Lindgren struct omap_hwmod **hwmods; 54625c7d49eSTony Lindgren 54725c7d49eSTony Lindgren od = kzalloc(sizeof(struct omap_device), GFP_KERNEL); 54825c7d49eSTony Lindgren if (!od) { 54925c7d49eSTony Lindgren ret = -ENOMEM; 55025c7d49eSTony Lindgren goto oda_exit1; 55125c7d49eSTony Lindgren } 55225c7d49eSTony Lindgren od->hwmods_cnt = oh_cnt; 55325c7d49eSTony Lindgren 55425c7d49eSTony Lindgren hwmods = kmemdup(ohs, sizeof(struct omap_hwmod *) * oh_cnt, GFP_KERNEL); 55525c7d49eSTony Lindgren if (!hwmods) 55625c7d49eSTony Lindgren goto oda_exit2; 55725c7d49eSTony Lindgren 55825c7d49eSTony Lindgren od->hwmods = hwmods; 55925c7d49eSTony Lindgren od->pdev = pdev; 56025c7d49eSTony Lindgren 56125c7d49eSTony Lindgren res_count = omap_device_count_resources(od); 56225c7d49eSTony Lindgren /* 56325c7d49eSTony Lindgren * DT Boot: 56425c7d49eSTony Lindgren * OF framework will construct the resource structure (currently 56525c7d49eSTony Lindgren * does for MEM & IRQ resource) and we should respect/use these 56625c7d49eSTony Lindgren * resources, killing hwmod dependency. 56725c7d49eSTony Lindgren * If pdev->num_resources > 0, we assume that MEM & IRQ resources 56825c7d49eSTony Lindgren * have been allocated by OF layer already (through DTB). 56925c7d49eSTony Lindgren * 57025c7d49eSTony Lindgren * Non-DT Boot: 57125c7d49eSTony Lindgren * Here, pdev->num_resources = 0, and we should get all the 57225c7d49eSTony Lindgren * resources from hwmod. 57325c7d49eSTony Lindgren * 57425c7d49eSTony Lindgren * TODO: Once DMA resource is available from OF layer, we should 57525c7d49eSTony Lindgren * kill filling any resources from hwmod. 57625c7d49eSTony Lindgren */ 57725c7d49eSTony Lindgren if (res_count > pdev->num_resources) { 57825c7d49eSTony Lindgren /* Allocate resources memory to account for new resources */ 57925c7d49eSTony Lindgren res = kzalloc(sizeof(struct resource) * res_count, GFP_KERNEL); 58025c7d49eSTony Lindgren if (!res) 58125c7d49eSTony Lindgren goto oda_exit3; 58225c7d49eSTony Lindgren 58325c7d49eSTony Lindgren /* 58425c7d49eSTony Lindgren * If pdev->num_resources > 0, then assume that, 58525c7d49eSTony Lindgren * MEM and IRQ resources will only come from DT and only 58625c7d49eSTony Lindgren * fill DMA resource from hwmod layer. 58725c7d49eSTony Lindgren */ 58825c7d49eSTony Lindgren if (pdev->num_resources && pdev->resource) { 58925c7d49eSTony Lindgren dev_dbg(&pdev->dev, "%s(): resources already allocated %d\n", 59025c7d49eSTony Lindgren __func__, res_count); 59125c7d49eSTony Lindgren memcpy(res, pdev->resource, 59225c7d49eSTony Lindgren sizeof(struct resource) * pdev->num_resources); 59325c7d49eSTony Lindgren _od_fill_dma_resources(od, &res[pdev->num_resources]); 59425c7d49eSTony Lindgren } else { 59525c7d49eSTony Lindgren dev_dbg(&pdev->dev, "%s(): using resources from hwmod %d\n", 59625c7d49eSTony Lindgren __func__, res_count); 59725c7d49eSTony Lindgren omap_device_fill_resources(od, res); 59825c7d49eSTony Lindgren } 59925c7d49eSTony Lindgren 60025c7d49eSTony Lindgren ret = platform_device_add_resources(pdev, res, res_count); 60125c7d49eSTony Lindgren kfree(res); 60225c7d49eSTony Lindgren 60325c7d49eSTony Lindgren if (ret) 60425c7d49eSTony Lindgren goto oda_exit3; 60525c7d49eSTony Lindgren } 60625c7d49eSTony Lindgren 60725c7d49eSTony Lindgren if (!pm_lats) { 60825c7d49eSTony Lindgren pm_lats = omap_default_latency; 60925c7d49eSTony Lindgren pm_lats_cnt = ARRAY_SIZE(omap_default_latency); 61025c7d49eSTony Lindgren } 61125c7d49eSTony Lindgren 61225c7d49eSTony Lindgren od->pm_lats_cnt = pm_lats_cnt; 61325c7d49eSTony Lindgren od->pm_lats = kmemdup(pm_lats, 61425c7d49eSTony Lindgren sizeof(struct omap_device_pm_latency) * pm_lats_cnt, 61525c7d49eSTony Lindgren GFP_KERNEL); 61625c7d49eSTony Lindgren if (!od->pm_lats) 61725c7d49eSTony Lindgren goto oda_exit3; 61825c7d49eSTony Lindgren 61925c7d49eSTony Lindgren pdev->archdata.od = od; 62025c7d49eSTony Lindgren 62125c7d49eSTony Lindgren for (i = 0; i < oh_cnt; i++) { 62225c7d49eSTony Lindgren hwmods[i]->od = od; 62325c7d49eSTony Lindgren _add_hwmod_clocks_clkdev(od, hwmods[i]); 62425c7d49eSTony Lindgren } 62525c7d49eSTony Lindgren 62625c7d49eSTony Lindgren return od; 62725c7d49eSTony Lindgren 62825c7d49eSTony Lindgren oda_exit3: 62925c7d49eSTony Lindgren kfree(hwmods); 63025c7d49eSTony Lindgren oda_exit2: 63125c7d49eSTony Lindgren kfree(od); 63225c7d49eSTony Lindgren oda_exit1: 63325c7d49eSTony Lindgren dev_err(&pdev->dev, "omap_device: build failed (%d)\n", ret); 63425c7d49eSTony Lindgren 63525c7d49eSTony Lindgren return ERR_PTR(ret); 63625c7d49eSTony Lindgren } 63725c7d49eSTony Lindgren 63825c7d49eSTony Lindgren void omap_device_delete(struct omap_device *od) 63925c7d49eSTony Lindgren { 64025c7d49eSTony Lindgren if (!od) 64125c7d49eSTony Lindgren return; 64225c7d49eSTony Lindgren 64325c7d49eSTony Lindgren od->pdev->archdata.od = NULL; 64425c7d49eSTony Lindgren kfree(od->pm_lats); 64525c7d49eSTony Lindgren kfree(od->hwmods); 64625c7d49eSTony Lindgren kfree(od); 64725c7d49eSTony Lindgren } 64825c7d49eSTony Lindgren 64925c7d49eSTony Lindgren /** 65025c7d49eSTony Lindgren * omap_device_build - build and register an omap_device with one omap_hwmod 65125c7d49eSTony Lindgren * @pdev_name: name of the platform_device driver to use 65225c7d49eSTony Lindgren * @pdev_id: this platform_device's connection ID 65325c7d49eSTony Lindgren * @oh: ptr to the single omap_hwmod that backs this omap_device 65425c7d49eSTony Lindgren * @pdata: platform_data ptr to associate with the platform_device 65525c7d49eSTony Lindgren * @pdata_len: amount of memory pointed to by @pdata 65625c7d49eSTony Lindgren * @pm_lats: pointer to a omap_device_pm_latency array for this device 65725c7d49eSTony Lindgren * @pm_lats_cnt: ARRAY_SIZE() of @pm_lats 65825c7d49eSTony Lindgren * @is_early_device: should the device be registered as an early device or not 65925c7d49eSTony Lindgren * 66025c7d49eSTony Lindgren * Convenience function for building and registering a single 66125c7d49eSTony Lindgren * omap_device record, which in turn builds and registers a 66225c7d49eSTony Lindgren * platform_device record. See omap_device_build_ss() for more 66325c7d49eSTony Lindgren * information. Returns ERR_PTR(-EINVAL) if @oh is NULL; otherwise, 66425c7d49eSTony Lindgren * passes along the return value of omap_device_build_ss(). 66525c7d49eSTony Lindgren */ 66625c7d49eSTony Lindgren struct platform_device __init *omap_device_build(const char *pdev_name, int pdev_id, 66725c7d49eSTony Lindgren struct omap_hwmod *oh, void *pdata, 66825c7d49eSTony Lindgren int pdata_len, 66925c7d49eSTony Lindgren struct omap_device_pm_latency *pm_lats, 67025c7d49eSTony Lindgren int pm_lats_cnt, int is_early_device) 67125c7d49eSTony Lindgren { 67225c7d49eSTony Lindgren struct omap_hwmod *ohs[] = { oh }; 67325c7d49eSTony Lindgren 67425c7d49eSTony Lindgren if (!oh) 67525c7d49eSTony Lindgren return ERR_PTR(-EINVAL); 67625c7d49eSTony Lindgren 67725c7d49eSTony Lindgren return omap_device_build_ss(pdev_name, pdev_id, ohs, 1, pdata, 67825c7d49eSTony Lindgren pdata_len, pm_lats, pm_lats_cnt, 67925c7d49eSTony Lindgren is_early_device); 68025c7d49eSTony Lindgren } 68125c7d49eSTony Lindgren 68225c7d49eSTony Lindgren /** 68325c7d49eSTony Lindgren * omap_device_build_ss - build and register an omap_device with multiple hwmods 68425c7d49eSTony Lindgren * @pdev_name: name of the platform_device driver to use 68525c7d49eSTony Lindgren * @pdev_id: this platform_device's connection ID 68625c7d49eSTony Lindgren * @oh: ptr to the single omap_hwmod that backs this omap_device 68725c7d49eSTony Lindgren * @pdata: platform_data ptr to associate with the platform_device 68825c7d49eSTony Lindgren * @pdata_len: amount of memory pointed to by @pdata 68925c7d49eSTony Lindgren * @pm_lats: pointer to a omap_device_pm_latency array for this device 69025c7d49eSTony Lindgren * @pm_lats_cnt: ARRAY_SIZE() of @pm_lats 69125c7d49eSTony Lindgren * @is_early_device: should the device be registered as an early device or not 69225c7d49eSTony Lindgren * 69325c7d49eSTony Lindgren * Convenience function for building and registering an omap_device 69425c7d49eSTony Lindgren * subsystem record. Subsystem records consist of multiple 69525c7d49eSTony Lindgren * omap_hwmods. This function in turn builds and registers a 69625c7d49eSTony Lindgren * platform_device record. Returns an ERR_PTR() on error, or passes 69725c7d49eSTony Lindgren * along the return value of omap_device_register(). 69825c7d49eSTony Lindgren */ 69925c7d49eSTony Lindgren struct platform_device __init *omap_device_build_ss(const char *pdev_name, int pdev_id, 70025c7d49eSTony Lindgren struct omap_hwmod **ohs, int oh_cnt, 70125c7d49eSTony Lindgren void *pdata, int pdata_len, 70225c7d49eSTony Lindgren struct omap_device_pm_latency *pm_lats, 70325c7d49eSTony Lindgren int pm_lats_cnt, int is_early_device) 70425c7d49eSTony Lindgren { 70525c7d49eSTony Lindgren int ret = -ENOMEM; 70625c7d49eSTony Lindgren struct platform_device *pdev; 70725c7d49eSTony Lindgren struct omap_device *od; 70825c7d49eSTony Lindgren 70925c7d49eSTony Lindgren if (!ohs || oh_cnt == 0 || !pdev_name) 71025c7d49eSTony Lindgren return ERR_PTR(-EINVAL); 71125c7d49eSTony Lindgren 71225c7d49eSTony Lindgren if (!pdata && pdata_len > 0) 71325c7d49eSTony Lindgren return ERR_PTR(-EINVAL); 71425c7d49eSTony Lindgren 71525c7d49eSTony Lindgren pdev = platform_device_alloc(pdev_name, pdev_id); 71625c7d49eSTony Lindgren if (!pdev) { 71725c7d49eSTony Lindgren ret = -ENOMEM; 71825c7d49eSTony Lindgren goto odbs_exit; 71925c7d49eSTony Lindgren } 72025c7d49eSTony Lindgren 72125c7d49eSTony Lindgren /* Set the dev_name early to allow dev_xxx in omap_device_alloc */ 72225c7d49eSTony Lindgren if (pdev->id != -1) 72325c7d49eSTony Lindgren dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); 72425c7d49eSTony Lindgren else 72525c7d49eSTony Lindgren dev_set_name(&pdev->dev, "%s", pdev->name); 72625c7d49eSTony Lindgren 72725c7d49eSTony Lindgren od = omap_device_alloc(pdev, ohs, oh_cnt, pm_lats, pm_lats_cnt); 72825c7d49eSTony Lindgren if (IS_ERR(od)) 72925c7d49eSTony Lindgren goto odbs_exit1; 73025c7d49eSTony Lindgren 73125c7d49eSTony Lindgren ret = platform_device_add_data(pdev, pdata, pdata_len); 73225c7d49eSTony Lindgren if (ret) 73325c7d49eSTony Lindgren goto odbs_exit2; 73425c7d49eSTony Lindgren 73525c7d49eSTony Lindgren if (is_early_device) 73625c7d49eSTony Lindgren ret = omap_early_device_register(pdev); 73725c7d49eSTony Lindgren else 73825c7d49eSTony Lindgren ret = omap_device_register(pdev); 73925c7d49eSTony Lindgren if (ret) 74025c7d49eSTony Lindgren goto odbs_exit2; 74125c7d49eSTony Lindgren 74225c7d49eSTony Lindgren return pdev; 74325c7d49eSTony Lindgren 74425c7d49eSTony Lindgren odbs_exit2: 74525c7d49eSTony Lindgren omap_device_delete(od); 74625c7d49eSTony Lindgren odbs_exit1: 74725c7d49eSTony Lindgren platform_device_put(pdev); 74825c7d49eSTony Lindgren odbs_exit: 74925c7d49eSTony Lindgren 75025c7d49eSTony Lindgren pr_err("omap_device: %s: build failed (%d)\n", pdev_name, ret); 75125c7d49eSTony Lindgren 75225c7d49eSTony Lindgren return ERR_PTR(ret); 75325c7d49eSTony Lindgren } 75425c7d49eSTony Lindgren 75525c7d49eSTony Lindgren /** 75625c7d49eSTony Lindgren * omap_early_device_register - register an omap_device as an early platform 75725c7d49eSTony Lindgren * device. 75825c7d49eSTony Lindgren * @od: struct omap_device * to register 75925c7d49eSTony Lindgren * 76025c7d49eSTony Lindgren * Register the omap_device structure. This currently just calls 76125c7d49eSTony Lindgren * platform_early_add_device() on the underlying platform_device. 76225c7d49eSTony Lindgren * Returns 0 by default. 76325c7d49eSTony Lindgren */ 76425c7d49eSTony Lindgren static int __init omap_early_device_register(struct platform_device *pdev) 76525c7d49eSTony Lindgren { 76625c7d49eSTony Lindgren struct platform_device *devices[1]; 76725c7d49eSTony Lindgren 76825c7d49eSTony Lindgren devices[0] = pdev; 76925c7d49eSTony Lindgren early_platform_add_devices(devices, 1); 77025c7d49eSTony Lindgren return 0; 77125c7d49eSTony Lindgren } 77225c7d49eSTony Lindgren 77325c7d49eSTony Lindgren #ifdef CONFIG_PM_RUNTIME 77425c7d49eSTony Lindgren static int _od_runtime_suspend(struct device *dev) 77525c7d49eSTony Lindgren { 77625c7d49eSTony Lindgren struct platform_device *pdev = to_platform_device(dev); 77725c7d49eSTony Lindgren int ret; 77825c7d49eSTony Lindgren 77925c7d49eSTony Lindgren ret = pm_generic_runtime_suspend(dev); 78025c7d49eSTony Lindgren 78125c7d49eSTony Lindgren if (!ret) 78225c7d49eSTony Lindgren omap_device_idle(pdev); 78325c7d49eSTony Lindgren 78425c7d49eSTony Lindgren return ret; 78525c7d49eSTony Lindgren } 78625c7d49eSTony Lindgren 78725c7d49eSTony Lindgren static int _od_runtime_idle(struct device *dev) 78825c7d49eSTony Lindgren { 78925c7d49eSTony Lindgren return pm_generic_runtime_idle(dev); 79025c7d49eSTony Lindgren } 79125c7d49eSTony Lindgren 79225c7d49eSTony Lindgren static int _od_runtime_resume(struct device *dev) 79325c7d49eSTony Lindgren { 79425c7d49eSTony Lindgren struct platform_device *pdev = to_platform_device(dev); 79525c7d49eSTony Lindgren 79625c7d49eSTony Lindgren omap_device_enable(pdev); 79725c7d49eSTony Lindgren 79825c7d49eSTony Lindgren return pm_generic_runtime_resume(dev); 79925c7d49eSTony Lindgren } 80025c7d49eSTony Lindgren #endif 80125c7d49eSTony Lindgren 80225c7d49eSTony Lindgren #ifdef CONFIG_SUSPEND 80325c7d49eSTony Lindgren static int _od_suspend_noirq(struct device *dev) 80425c7d49eSTony Lindgren { 80525c7d49eSTony Lindgren struct platform_device *pdev = to_platform_device(dev); 80625c7d49eSTony Lindgren struct omap_device *od = to_omap_device(pdev); 80725c7d49eSTony Lindgren int ret; 80825c7d49eSTony Lindgren 80925c7d49eSTony Lindgren /* Don't attempt late suspend on a driver that is not bound */ 81025c7d49eSTony Lindgren if (od->_driver_status != BUS_NOTIFY_BOUND_DRIVER) 81125c7d49eSTony Lindgren return 0; 81225c7d49eSTony Lindgren 81325c7d49eSTony Lindgren ret = pm_generic_suspend_noirq(dev); 81425c7d49eSTony Lindgren 81525c7d49eSTony Lindgren if (!ret && !pm_runtime_status_suspended(dev)) { 81625c7d49eSTony Lindgren if (pm_generic_runtime_suspend(dev) == 0) { 81725c7d49eSTony Lindgren if (!(od->flags & OMAP_DEVICE_NO_IDLE_ON_SUSPEND)) 81825c7d49eSTony Lindgren omap_device_idle(pdev); 81925c7d49eSTony Lindgren od->flags |= OMAP_DEVICE_SUSPENDED; 82025c7d49eSTony Lindgren } 82125c7d49eSTony Lindgren } 82225c7d49eSTony Lindgren 82325c7d49eSTony Lindgren return ret; 82425c7d49eSTony Lindgren } 82525c7d49eSTony Lindgren 82625c7d49eSTony Lindgren static int _od_resume_noirq(struct device *dev) 82725c7d49eSTony Lindgren { 82825c7d49eSTony Lindgren struct platform_device *pdev = to_platform_device(dev); 82925c7d49eSTony Lindgren struct omap_device *od = to_omap_device(pdev); 83025c7d49eSTony Lindgren 83125c7d49eSTony Lindgren if ((od->flags & OMAP_DEVICE_SUSPENDED) && 83225c7d49eSTony Lindgren !pm_runtime_status_suspended(dev)) { 83325c7d49eSTony Lindgren od->flags &= ~OMAP_DEVICE_SUSPENDED; 83425c7d49eSTony Lindgren if (!(od->flags & OMAP_DEVICE_NO_IDLE_ON_SUSPEND)) 83525c7d49eSTony Lindgren omap_device_enable(pdev); 83625c7d49eSTony Lindgren pm_generic_runtime_resume(dev); 83725c7d49eSTony Lindgren } 83825c7d49eSTony Lindgren 83925c7d49eSTony Lindgren return pm_generic_resume_noirq(dev); 84025c7d49eSTony Lindgren } 84125c7d49eSTony Lindgren #else 84225c7d49eSTony Lindgren #define _od_suspend_noirq NULL 84325c7d49eSTony Lindgren #define _od_resume_noirq NULL 84425c7d49eSTony Lindgren #endif 84525c7d49eSTony Lindgren 84625c7d49eSTony Lindgren struct dev_pm_domain omap_device_pm_domain = { 84725c7d49eSTony Lindgren .ops = { 84825c7d49eSTony Lindgren SET_RUNTIME_PM_OPS(_od_runtime_suspend, _od_runtime_resume, 84925c7d49eSTony Lindgren _od_runtime_idle) 85025c7d49eSTony Lindgren USE_PLATFORM_PM_SLEEP_OPS 85125c7d49eSTony Lindgren .suspend_noirq = _od_suspend_noirq, 85225c7d49eSTony Lindgren .resume_noirq = _od_resume_noirq, 85325c7d49eSTony Lindgren } 85425c7d49eSTony Lindgren }; 85525c7d49eSTony Lindgren 85625c7d49eSTony Lindgren /** 85725c7d49eSTony Lindgren * omap_device_register - register an omap_device with one omap_hwmod 85825c7d49eSTony Lindgren * @od: struct omap_device * to register 85925c7d49eSTony Lindgren * 86025c7d49eSTony Lindgren * Register the omap_device structure. This currently just calls 86125c7d49eSTony Lindgren * platform_device_register() on the underlying platform_device. 86225c7d49eSTony Lindgren * Returns the return value of platform_device_register(). 86325c7d49eSTony Lindgren */ 86425c7d49eSTony Lindgren int omap_device_register(struct platform_device *pdev) 86525c7d49eSTony Lindgren { 86625c7d49eSTony Lindgren pr_debug("omap_device: %s: registering\n", pdev->name); 86725c7d49eSTony Lindgren 86825c7d49eSTony Lindgren pdev->dev.pm_domain = &omap_device_pm_domain; 86925c7d49eSTony Lindgren return platform_device_add(pdev); 87025c7d49eSTony Lindgren } 87125c7d49eSTony Lindgren 87225c7d49eSTony Lindgren 87325c7d49eSTony Lindgren /* Public functions for use by device drivers through struct platform_data */ 87425c7d49eSTony Lindgren 87525c7d49eSTony Lindgren /** 87625c7d49eSTony Lindgren * omap_device_enable - fully activate an omap_device 87725c7d49eSTony Lindgren * @od: struct omap_device * to activate 87825c7d49eSTony Lindgren * 87925c7d49eSTony Lindgren * Do whatever is necessary for the hwmods underlying omap_device @od 88025c7d49eSTony Lindgren * to be accessible and ready to operate. This generally involves 88125c7d49eSTony Lindgren * enabling clocks, setting SYSCONFIG registers; and in the future may 88225c7d49eSTony Lindgren * involve remuxing pins. Device drivers should call this function 88325c7d49eSTony Lindgren * (through platform_data function pointers) where they would normally 88425c7d49eSTony Lindgren * enable clocks, etc. Returns -EINVAL if called when the omap_device 88525c7d49eSTony Lindgren * is already enabled, or passes along the return value of 88625c7d49eSTony Lindgren * _omap_device_activate(). 88725c7d49eSTony Lindgren */ 88825c7d49eSTony Lindgren int omap_device_enable(struct platform_device *pdev) 88925c7d49eSTony Lindgren { 89025c7d49eSTony Lindgren int ret; 89125c7d49eSTony Lindgren struct omap_device *od; 89225c7d49eSTony Lindgren 89325c7d49eSTony Lindgren od = to_omap_device(pdev); 89425c7d49eSTony Lindgren 89525c7d49eSTony Lindgren if (od->_state == OMAP_DEVICE_STATE_ENABLED) { 89625c7d49eSTony Lindgren dev_warn(&pdev->dev, 89725c7d49eSTony Lindgren "omap_device: %s() called from invalid state %d\n", 89825c7d49eSTony Lindgren __func__, od->_state); 89925c7d49eSTony Lindgren return -EINVAL; 90025c7d49eSTony Lindgren } 90125c7d49eSTony Lindgren 90225c7d49eSTony Lindgren /* Enable everything if we're enabling this device from scratch */ 90325c7d49eSTony Lindgren if (od->_state == OMAP_DEVICE_STATE_UNKNOWN) 90425c7d49eSTony Lindgren od->pm_lat_level = od->pm_lats_cnt; 90525c7d49eSTony Lindgren 90625c7d49eSTony Lindgren ret = _omap_device_activate(od, IGNORE_WAKEUP_LAT); 90725c7d49eSTony Lindgren 90825c7d49eSTony Lindgren od->dev_wakeup_lat = 0; 90925c7d49eSTony Lindgren od->_dev_wakeup_lat_limit = UINT_MAX; 91025c7d49eSTony Lindgren od->_state = OMAP_DEVICE_STATE_ENABLED; 91125c7d49eSTony Lindgren 91225c7d49eSTony Lindgren return ret; 91325c7d49eSTony Lindgren } 91425c7d49eSTony Lindgren 91525c7d49eSTony Lindgren /** 91625c7d49eSTony Lindgren * omap_device_idle - idle an omap_device 91725c7d49eSTony Lindgren * @od: struct omap_device * to idle 91825c7d49eSTony Lindgren * 91925c7d49eSTony Lindgren * Idle omap_device @od by calling as many .deactivate_func() entries 92025c7d49eSTony Lindgren * in the omap_device's pm_lats table as is possible without exceeding 92125c7d49eSTony Lindgren * the device's maximum wakeup latency limit, pm_lat_limit. Device 92225c7d49eSTony Lindgren * drivers should call this function (through platform_data function 92325c7d49eSTony Lindgren * pointers) where they would normally disable clocks after operations 92425c7d49eSTony Lindgren * complete, etc.. Returns -EINVAL if the omap_device is not 92525c7d49eSTony Lindgren * currently enabled, or passes along the return value of 92625c7d49eSTony Lindgren * _omap_device_deactivate(). 92725c7d49eSTony Lindgren */ 92825c7d49eSTony Lindgren int omap_device_idle(struct platform_device *pdev) 92925c7d49eSTony Lindgren { 93025c7d49eSTony Lindgren int ret; 93125c7d49eSTony Lindgren struct omap_device *od; 93225c7d49eSTony Lindgren 93325c7d49eSTony Lindgren od = to_omap_device(pdev); 93425c7d49eSTony Lindgren 93525c7d49eSTony Lindgren if (od->_state != OMAP_DEVICE_STATE_ENABLED) { 93625c7d49eSTony Lindgren dev_warn(&pdev->dev, 93725c7d49eSTony Lindgren "omap_device: %s() called from invalid state %d\n", 93825c7d49eSTony Lindgren __func__, od->_state); 93925c7d49eSTony Lindgren return -EINVAL; 94025c7d49eSTony Lindgren } 94125c7d49eSTony Lindgren 94225c7d49eSTony Lindgren ret = _omap_device_deactivate(od, USE_WAKEUP_LAT); 94325c7d49eSTony Lindgren 94425c7d49eSTony Lindgren od->_state = OMAP_DEVICE_STATE_IDLE; 94525c7d49eSTony Lindgren 94625c7d49eSTony Lindgren return ret; 94725c7d49eSTony Lindgren } 94825c7d49eSTony Lindgren 94925c7d49eSTony Lindgren /** 95025c7d49eSTony Lindgren * omap_device_shutdown - shut down an omap_device 95125c7d49eSTony Lindgren * @od: struct omap_device * to shut down 95225c7d49eSTony Lindgren * 95325c7d49eSTony Lindgren * Shut down omap_device @od by calling all .deactivate_func() entries 95425c7d49eSTony Lindgren * in the omap_device's pm_lats table and then shutting down all of 95525c7d49eSTony Lindgren * the underlying omap_hwmods. Used when a device is being "removed" 95625c7d49eSTony Lindgren * or a device driver is being unloaded. Returns -EINVAL if the 95725c7d49eSTony Lindgren * omap_device is not currently enabled or idle, or passes along the 95825c7d49eSTony Lindgren * return value of _omap_device_deactivate(). 95925c7d49eSTony Lindgren */ 96025c7d49eSTony Lindgren int omap_device_shutdown(struct platform_device *pdev) 96125c7d49eSTony Lindgren { 96225c7d49eSTony Lindgren int ret, i; 96325c7d49eSTony Lindgren struct omap_device *od; 96425c7d49eSTony Lindgren 96525c7d49eSTony Lindgren od = to_omap_device(pdev); 96625c7d49eSTony Lindgren 96725c7d49eSTony Lindgren if (od->_state != OMAP_DEVICE_STATE_ENABLED && 96825c7d49eSTony Lindgren od->_state != OMAP_DEVICE_STATE_IDLE) { 96925c7d49eSTony Lindgren dev_warn(&pdev->dev, 97025c7d49eSTony Lindgren "omap_device: %s() called from invalid state %d\n", 97125c7d49eSTony Lindgren __func__, od->_state); 97225c7d49eSTony Lindgren return -EINVAL; 97325c7d49eSTony Lindgren } 97425c7d49eSTony Lindgren 97525c7d49eSTony Lindgren ret = _omap_device_deactivate(od, IGNORE_WAKEUP_LAT); 97625c7d49eSTony Lindgren 97725c7d49eSTony Lindgren for (i = 0; i < od->hwmods_cnt; i++) 97825c7d49eSTony Lindgren omap_hwmod_shutdown(od->hwmods[i]); 97925c7d49eSTony Lindgren 98025c7d49eSTony Lindgren od->_state = OMAP_DEVICE_STATE_SHUTDOWN; 98125c7d49eSTony Lindgren 98225c7d49eSTony Lindgren return ret; 98325c7d49eSTony Lindgren } 98425c7d49eSTony Lindgren 98525c7d49eSTony Lindgren /** 98625c7d49eSTony Lindgren * omap_device_assert_hardreset - set a device's hardreset line 98725c7d49eSTony Lindgren * @pdev: struct platform_device * to reset 98825c7d49eSTony Lindgren * @name: const char * name of the reset line 98925c7d49eSTony Lindgren * 99025c7d49eSTony Lindgren * Set the hardreset line identified by @name on the IP blocks 99125c7d49eSTony Lindgren * associated with the hwmods backing the platform_device @pdev. All 99225c7d49eSTony Lindgren * of the hwmods associated with @pdev must have the same hardreset 99325c7d49eSTony Lindgren * line linked to them for this to work. Passes along the return value 99425c7d49eSTony Lindgren * of omap_hwmod_assert_hardreset() in the event of any failure, or 99525c7d49eSTony Lindgren * returns 0 upon success. 99625c7d49eSTony Lindgren */ 99725c7d49eSTony Lindgren int omap_device_assert_hardreset(struct platform_device *pdev, const char *name) 99825c7d49eSTony Lindgren { 99925c7d49eSTony Lindgren struct omap_device *od = to_omap_device(pdev); 100025c7d49eSTony Lindgren int ret = 0; 100125c7d49eSTony Lindgren int i; 100225c7d49eSTony Lindgren 100325c7d49eSTony Lindgren for (i = 0; i < od->hwmods_cnt; i++) { 100425c7d49eSTony Lindgren ret = omap_hwmod_assert_hardreset(od->hwmods[i], name); 100525c7d49eSTony Lindgren if (ret) 100625c7d49eSTony Lindgren break; 100725c7d49eSTony Lindgren } 100825c7d49eSTony Lindgren 100925c7d49eSTony Lindgren return ret; 101025c7d49eSTony Lindgren } 101125c7d49eSTony Lindgren 101225c7d49eSTony Lindgren /** 101325c7d49eSTony Lindgren * omap_device_deassert_hardreset - release a device's hardreset line 101425c7d49eSTony Lindgren * @pdev: struct platform_device * to reset 101525c7d49eSTony Lindgren * @name: const char * name of the reset line 101625c7d49eSTony Lindgren * 101725c7d49eSTony Lindgren * Release the hardreset line identified by @name on the IP blocks 101825c7d49eSTony Lindgren * associated with the hwmods backing the platform_device @pdev. All 101925c7d49eSTony Lindgren * of the hwmods associated with @pdev must have the same hardreset 102025c7d49eSTony Lindgren * line linked to them for this to work. Passes along the return 102125c7d49eSTony Lindgren * value of omap_hwmod_deassert_hardreset() in the event of any 102225c7d49eSTony Lindgren * failure, or returns 0 upon success. 102325c7d49eSTony Lindgren */ 102425c7d49eSTony Lindgren int omap_device_deassert_hardreset(struct platform_device *pdev, 102525c7d49eSTony Lindgren const char *name) 102625c7d49eSTony Lindgren { 102725c7d49eSTony Lindgren struct omap_device *od = to_omap_device(pdev); 102825c7d49eSTony Lindgren int ret = 0; 102925c7d49eSTony Lindgren int i; 103025c7d49eSTony Lindgren 103125c7d49eSTony Lindgren for (i = 0; i < od->hwmods_cnt; i++) { 103225c7d49eSTony Lindgren ret = omap_hwmod_deassert_hardreset(od->hwmods[i], name); 103325c7d49eSTony Lindgren if (ret) 103425c7d49eSTony Lindgren break; 103525c7d49eSTony Lindgren } 103625c7d49eSTony Lindgren 103725c7d49eSTony Lindgren return ret; 103825c7d49eSTony Lindgren } 103925c7d49eSTony Lindgren 104025c7d49eSTony Lindgren /** 104125c7d49eSTony Lindgren * omap_device_align_pm_lat - activate/deactivate device to match wakeup lat lim 104225c7d49eSTony Lindgren * @od: struct omap_device * 104325c7d49eSTony Lindgren * 104425c7d49eSTony Lindgren * When a device's maximum wakeup latency limit changes, call some of 104525c7d49eSTony Lindgren * the .activate_func or .deactivate_func function pointers in the 104625c7d49eSTony Lindgren * omap_device's pm_lats array to ensure that the device's maximum 104725c7d49eSTony Lindgren * wakeup latency is less than or equal to the new latency limit. 104825c7d49eSTony Lindgren * Intended to be called by OMAP PM code whenever a device's maximum 104925c7d49eSTony Lindgren * wakeup latency limit changes (e.g., via 105025c7d49eSTony Lindgren * omap_pm_set_dev_wakeup_lat()). Returns 0 if nothing needs to be 105125c7d49eSTony Lindgren * done (e.g., if the omap_device is not currently idle, or if the 105225c7d49eSTony Lindgren * wakeup latency is already current with the new limit) or passes 105325c7d49eSTony Lindgren * along the return value of _omap_device_deactivate() or 105425c7d49eSTony Lindgren * _omap_device_activate(). 105525c7d49eSTony Lindgren */ 105625c7d49eSTony Lindgren int omap_device_align_pm_lat(struct platform_device *pdev, 105725c7d49eSTony Lindgren u32 new_wakeup_lat_limit) 105825c7d49eSTony Lindgren { 105925c7d49eSTony Lindgren int ret = -EINVAL; 106025c7d49eSTony Lindgren struct omap_device *od; 106125c7d49eSTony Lindgren 106225c7d49eSTony Lindgren od = to_omap_device(pdev); 106325c7d49eSTony Lindgren 106425c7d49eSTony Lindgren if (new_wakeup_lat_limit == od->dev_wakeup_lat) 106525c7d49eSTony Lindgren return 0; 106625c7d49eSTony Lindgren 106725c7d49eSTony Lindgren od->_dev_wakeup_lat_limit = new_wakeup_lat_limit; 106825c7d49eSTony Lindgren 106925c7d49eSTony Lindgren if (od->_state != OMAP_DEVICE_STATE_IDLE) 107025c7d49eSTony Lindgren return 0; 107125c7d49eSTony Lindgren else if (new_wakeup_lat_limit > od->dev_wakeup_lat) 107225c7d49eSTony Lindgren ret = _omap_device_deactivate(od, USE_WAKEUP_LAT); 107325c7d49eSTony Lindgren else if (new_wakeup_lat_limit < od->dev_wakeup_lat) 107425c7d49eSTony Lindgren ret = _omap_device_activate(od, USE_WAKEUP_LAT); 107525c7d49eSTony Lindgren 107625c7d49eSTony Lindgren return ret; 107725c7d49eSTony Lindgren } 107825c7d49eSTony Lindgren 107925c7d49eSTony Lindgren /** 108025c7d49eSTony Lindgren * omap_device_get_pwrdm - return the powerdomain * associated with @od 108125c7d49eSTony Lindgren * @od: struct omap_device * 108225c7d49eSTony Lindgren * 108325c7d49eSTony Lindgren * Return the powerdomain associated with the first underlying 108425c7d49eSTony Lindgren * omap_hwmod for this omap_device. Intended for use by core OMAP PM 108525c7d49eSTony Lindgren * code. Returns NULL on error or a struct powerdomain * upon 108625c7d49eSTony Lindgren * success. 108725c7d49eSTony Lindgren */ 108825c7d49eSTony Lindgren struct powerdomain *omap_device_get_pwrdm(struct omap_device *od) 108925c7d49eSTony Lindgren { 109025c7d49eSTony Lindgren /* 109125c7d49eSTony Lindgren * XXX Assumes that all omap_hwmod powerdomains are identical. 109225c7d49eSTony Lindgren * This may not necessarily be true. There should be a sanity 109325c7d49eSTony Lindgren * check in here to WARN() if any difference appears. 109425c7d49eSTony Lindgren */ 109525c7d49eSTony Lindgren if (!od->hwmods_cnt) 109625c7d49eSTony Lindgren return NULL; 109725c7d49eSTony Lindgren 109825c7d49eSTony Lindgren return omap_hwmod_get_pwrdm(od->hwmods[0]); 109925c7d49eSTony Lindgren } 110025c7d49eSTony Lindgren 110125c7d49eSTony Lindgren /** 110225c7d49eSTony Lindgren * omap_device_get_mpu_rt_va - return the MPU's virtual addr for the hwmod base 110325c7d49eSTony Lindgren * @od: struct omap_device * 110425c7d49eSTony Lindgren * 110525c7d49eSTony Lindgren * Return the MPU's virtual address for the base of the hwmod, from 110625c7d49eSTony Lindgren * the ioremap() that the hwmod code does. Only valid if there is one 110725c7d49eSTony Lindgren * hwmod associated with this device. Returns NULL if there are zero 110825c7d49eSTony Lindgren * or more than one hwmods associated with this omap_device; 110925c7d49eSTony Lindgren * otherwise, passes along the return value from 111025c7d49eSTony Lindgren * omap_hwmod_get_mpu_rt_va(). 111125c7d49eSTony Lindgren */ 111225c7d49eSTony Lindgren void __iomem *omap_device_get_rt_va(struct omap_device *od) 111325c7d49eSTony Lindgren { 111425c7d49eSTony Lindgren if (od->hwmods_cnt != 1) 111525c7d49eSTony Lindgren return NULL; 111625c7d49eSTony Lindgren 111725c7d49eSTony Lindgren return omap_hwmod_get_mpu_rt_va(od->hwmods[0]); 111825c7d49eSTony Lindgren } 111925c7d49eSTony Lindgren 112025c7d49eSTony Lindgren /** 112125c7d49eSTony Lindgren * omap_device_get_by_hwmod_name() - convert a hwmod name to 112225c7d49eSTony Lindgren * device pointer. 112325c7d49eSTony Lindgren * @oh_name: name of the hwmod device 112425c7d49eSTony Lindgren * 112525c7d49eSTony Lindgren * Returns back a struct device * pointer associated with a hwmod 112625c7d49eSTony Lindgren * device represented by a hwmod_name 112725c7d49eSTony Lindgren */ 112825c7d49eSTony Lindgren struct device *omap_device_get_by_hwmod_name(const char *oh_name) 112925c7d49eSTony Lindgren { 113025c7d49eSTony Lindgren struct omap_hwmod *oh; 113125c7d49eSTony Lindgren 113225c7d49eSTony Lindgren if (!oh_name) { 113325c7d49eSTony Lindgren WARN(1, "%s: no hwmod name!\n", __func__); 113425c7d49eSTony Lindgren return ERR_PTR(-EINVAL); 113525c7d49eSTony Lindgren } 113625c7d49eSTony Lindgren 113725c7d49eSTony Lindgren oh = omap_hwmod_lookup(oh_name); 113825c7d49eSTony Lindgren if (IS_ERR_OR_NULL(oh)) { 113925c7d49eSTony Lindgren WARN(1, "%s: no hwmod for %s\n", __func__, 114025c7d49eSTony Lindgren oh_name); 114125c7d49eSTony Lindgren return ERR_PTR(oh ? PTR_ERR(oh) : -ENODEV); 114225c7d49eSTony Lindgren } 114325c7d49eSTony Lindgren if (IS_ERR_OR_NULL(oh->od)) { 114425c7d49eSTony Lindgren WARN(1, "%s: no omap_device for %s\n", __func__, 114525c7d49eSTony Lindgren oh_name); 114625c7d49eSTony Lindgren return ERR_PTR(oh->od ? PTR_ERR(oh->od) : -ENODEV); 114725c7d49eSTony Lindgren } 114825c7d49eSTony Lindgren 114925c7d49eSTony Lindgren if (IS_ERR_OR_NULL(oh->od->pdev)) 115025c7d49eSTony Lindgren return ERR_PTR(oh->od->pdev ? PTR_ERR(oh->od->pdev) : -ENODEV); 115125c7d49eSTony Lindgren 115225c7d49eSTony Lindgren return &oh->od->pdev->dev; 115325c7d49eSTony Lindgren } 115425c7d49eSTony Lindgren EXPORT_SYMBOL(omap_device_get_by_hwmod_name); 115525c7d49eSTony Lindgren 115625c7d49eSTony Lindgren /* 115725c7d49eSTony Lindgren * Public functions intended for use in omap_device_pm_latency 115825c7d49eSTony Lindgren * .activate_func and .deactivate_func function pointers 115925c7d49eSTony Lindgren */ 116025c7d49eSTony Lindgren 116125c7d49eSTony Lindgren /** 116225c7d49eSTony Lindgren * omap_device_enable_hwmods - call omap_hwmod_enable() on all hwmods 116325c7d49eSTony Lindgren * @od: struct omap_device *od 116425c7d49eSTony Lindgren * 116525c7d49eSTony Lindgren * Enable all underlying hwmods. Returns 0. 116625c7d49eSTony Lindgren */ 116725c7d49eSTony Lindgren int omap_device_enable_hwmods(struct omap_device *od) 116825c7d49eSTony Lindgren { 116925c7d49eSTony Lindgren int i; 117025c7d49eSTony Lindgren 117125c7d49eSTony Lindgren for (i = 0; i < od->hwmods_cnt; i++) 117225c7d49eSTony Lindgren omap_hwmod_enable(od->hwmods[i]); 117325c7d49eSTony Lindgren 117425c7d49eSTony Lindgren /* XXX pass along return value here? */ 117525c7d49eSTony Lindgren return 0; 117625c7d49eSTony Lindgren } 117725c7d49eSTony Lindgren 117825c7d49eSTony Lindgren /** 117925c7d49eSTony Lindgren * omap_device_idle_hwmods - call omap_hwmod_idle() on all hwmods 118025c7d49eSTony Lindgren * @od: struct omap_device *od 118125c7d49eSTony Lindgren * 118225c7d49eSTony Lindgren * Idle all underlying hwmods. Returns 0. 118325c7d49eSTony Lindgren */ 118425c7d49eSTony Lindgren int omap_device_idle_hwmods(struct omap_device *od) 118525c7d49eSTony Lindgren { 118625c7d49eSTony Lindgren int i; 118725c7d49eSTony Lindgren 118825c7d49eSTony Lindgren for (i = 0; i < od->hwmods_cnt; i++) 118925c7d49eSTony Lindgren omap_hwmod_idle(od->hwmods[i]); 119025c7d49eSTony Lindgren 119125c7d49eSTony Lindgren /* XXX pass along return value here? */ 119225c7d49eSTony Lindgren return 0; 119325c7d49eSTony Lindgren } 119425c7d49eSTony Lindgren 119525c7d49eSTony Lindgren /** 119625c7d49eSTony Lindgren * omap_device_disable_clocks - disable all main and interface clocks 119725c7d49eSTony Lindgren * @od: struct omap_device *od 119825c7d49eSTony Lindgren * 119925c7d49eSTony Lindgren * Disable the main functional clock and interface clock for all of the 120025c7d49eSTony Lindgren * omap_hwmods associated with the omap_device. Returns 0. 120125c7d49eSTony Lindgren */ 120225c7d49eSTony Lindgren int omap_device_disable_clocks(struct omap_device *od) 120325c7d49eSTony Lindgren { 120425c7d49eSTony Lindgren int i; 120525c7d49eSTony Lindgren 120625c7d49eSTony Lindgren for (i = 0; i < od->hwmods_cnt; i++) 120725c7d49eSTony Lindgren omap_hwmod_disable_clocks(od->hwmods[i]); 120825c7d49eSTony Lindgren 120925c7d49eSTony Lindgren /* XXX pass along return value here? */ 121025c7d49eSTony Lindgren return 0; 121125c7d49eSTony Lindgren } 121225c7d49eSTony Lindgren 121325c7d49eSTony Lindgren /** 121425c7d49eSTony Lindgren * omap_device_enable_clocks - enable all main and interface clocks 121525c7d49eSTony Lindgren * @od: struct omap_device *od 121625c7d49eSTony Lindgren * 121725c7d49eSTony Lindgren * Enable the main functional clock and interface clock for all of the 121825c7d49eSTony Lindgren * omap_hwmods associated with the omap_device. Returns 0. 121925c7d49eSTony Lindgren */ 122025c7d49eSTony Lindgren int omap_device_enable_clocks(struct omap_device *od) 122125c7d49eSTony Lindgren { 122225c7d49eSTony Lindgren int i; 122325c7d49eSTony Lindgren 122425c7d49eSTony Lindgren for (i = 0; i < od->hwmods_cnt; i++) 122525c7d49eSTony Lindgren omap_hwmod_enable_clocks(od->hwmods[i]); 122625c7d49eSTony Lindgren 122725c7d49eSTony Lindgren /* XXX pass along return value here? */ 122825c7d49eSTony Lindgren return 0; 122925c7d49eSTony Lindgren } 123025c7d49eSTony Lindgren 123125c7d49eSTony Lindgren static struct notifier_block platform_nb = { 123225c7d49eSTony Lindgren .notifier_call = _omap_device_notifier_call, 123325c7d49eSTony Lindgren }; 123425c7d49eSTony Lindgren 123525c7d49eSTony Lindgren static int __init omap_device_init(void) 123625c7d49eSTony Lindgren { 123725c7d49eSTony Lindgren bus_register_notifier(&platform_bus_type, &platform_nb); 123825c7d49eSTony Lindgren return 0; 123925c7d49eSTony Lindgren } 124025c7d49eSTony Lindgren core_initcall(omap_device_init); 124125c7d49eSTony Lindgren 124225c7d49eSTony Lindgren /** 124325c7d49eSTony Lindgren * omap_device_late_idle - idle devices without drivers 124425c7d49eSTony Lindgren * @dev: struct device * associated with omap_device 124525c7d49eSTony Lindgren * @data: unused 124625c7d49eSTony Lindgren * 124725c7d49eSTony Lindgren * Check the driver bound status of this device, and idle it 124825c7d49eSTony Lindgren * if there is no driver attached. 124925c7d49eSTony Lindgren */ 125025c7d49eSTony Lindgren static int __init omap_device_late_idle(struct device *dev, void *data) 125125c7d49eSTony Lindgren { 125225c7d49eSTony Lindgren struct platform_device *pdev = to_platform_device(dev); 125325c7d49eSTony Lindgren struct omap_device *od = to_omap_device(pdev); 125425c7d49eSTony Lindgren 125525c7d49eSTony Lindgren if (!od) 125625c7d49eSTony Lindgren return 0; 125725c7d49eSTony Lindgren 125825c7d49eSTony Lindgren /* 125925c7d49eSTony Lindgren * If omap_device state is enabled, but has no driver bound, 126025c7d49eSTony Lindgren * idle it. 126125c7d49eSTony Lindgren */ 126225c7d49eSTony Lindgren if (od->_driver_status != BUS_NOTIFY_BOUND_DRIVER) { 126325c7d49eSTony Lindgren if (od->_state == OMAP_DEVICE_STATE_ENABLED) { 126425c7d49eSTony Lindgren dev_warn(dev, "%s: enabled but no driver. Idling\n", 126525c7d49eSTony Lindgren __func__); 126625c7d49eSTony Lindgren omap_device_idle(pdev); 126725c7d49eSTony Lindgren } 126825c7d49eSTony Lindgren } 126925c7d49eSTony Lindgren 127025c7d49eSTony Lindgren return 0; 127125c7d49eSTony Lindgren } 127225c7d49eSTony Lindgren 127325c7d49eSTony Lindgren static int __init omap_device_late_init(void) 127425c7d49eSTony Lindgren { 127525c7d49eSTony Lindgren bus_for_each_dev(&platform_bus_type, NULL, NULL, omap_device_late_idle); 127625c7d49eSTony Lindgren return 0; 127725c7d49eSTony Lindgren } 127825c7d49eSTony Lindgren late_initcall(omap_device_late_init); 1279