|  | @@ -0,0 +1,147 @@
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | + * omap_device implementation
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Copyright (C) 2009-2010 Nokia Corporation
 | 
	
		
			
				|  |  | + * Paul Walmsley, Kevin Hilman
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Developed in collaboration with (alphabetical order): Benoit
 | 
	
		
			
				|  |  | + * Cousson, Thara Gopinath, Tony Lindgren, Rajendra Nayak, Vikram
 | 
	
		
			
				|  |  | + * Pandita, Sakari Poussa, Anand Sawant, Santosh Shilimkar, Richard
 | 
	
		
			
				|  |  | + * Woodruff
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * This program is free software; you can redistribute it and/or modify
 | 
	
		
			
				|  |  | + * it under the terms of the GNU General Public License version 2 as
 | 
	
		
			
				|  |  | + * published by the Free Software Foundation.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * This code provides a consistent interface for OMAP device drivers
 | 
	
		
			
				|  |  | + * to control power management and interconnect properties of their
 | 
	
		
			
				|  |  | + * devices.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * In the medium- to long-term, this code should either be
 | 
	
		
			
				|  |  | + * a) implemented via arch-specific pointers in platform_data
 | 
	
		
			
				|  |  | + * or
 | 
	
		
			
				|  |  | + * b) implemented as a proper omap_bus/omap_device in Linux, no more
 | 
	
		
			
				|  |  | + *    platform_data func pointers
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Guidelines for usage by driver authors:
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * 1. These functions are intended to be used by device drivers via
 | 
	
		
			
				|  |  | + * function pointers in struct platform_data.  As an example,
 | 
	
		
			
				|  |  | + * omap_device_enable() should be passed to the driver as
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * struct foo_driver_platform_data {
 | 
	
		
			
				|  |  | + * ...
 | 
	
		
			
				|  |  | + *      int (*device_enable)(struct platform_device *pdev);
 | 
	
		
			
				|  |  | + * ...
 | 
	
		
			
				|  |  | + * }
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Note that the generic "device_enable" name is used, rather than
 | 
	
		
			
				|  |  | + * "omap_device_enable".  This is so other architectures can pass in their
 | 
	
		
			
				|  |  | + * own enable/disable functions here.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * This should be populated during device setup:
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * ...
 | 
	
		
			
				|  |  | + * pdata->device_enable = omap_device_enable;
 | 
	
		
			
				|  |  | + * ...
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * 2. Drivers should first check to ensure the function pointer is not null
 | 
	
		
			
				|  |  | + * before calling it, as in:
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * if (pdata->device_enable)
 | 
	
		
			
				|  |  | + *     pdata->device_enable(pdev);
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * This allows other architectures that don't use similar device_enable()/
 | 
	
		
			
				|  |  | + * device_shutdown() functions to execute normally.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * ...
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Suggested usage by device drivers:
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * During device initialization:
 | 
	
		
			
				|  |  | + * device_enable()
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * During device idle:
 | 
	
		
			
				|  |  | + * (save remaining device context if necessary)
 | 
	
		
			
				|  |  | + * device_idle();
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * During device resume:
 | 
	
		
			
				|  |  | + * device_enable();
 | 
	
		
			
				|  |  | + * (restore context if necessary)
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * During device shutdown:
 | 
	
		
			
				|  |  | + * device_shutdown()
 | 
	
		
			
				|  |  | + * (device must be reinitialized at this point to use it again)
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +#undef DEBUG
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include <linux/kernel.h>
 | 
	
		
			
				|  |  | +#include <linux/export.h>
 | 
	
		
			
				|  |  | +#include <linux/platform_device.h>
 | 
	
		
			
				|  |  | +#include <linux/slab.h>
 | 
	
		
			
				|  |  | +#include <linux/err.h>
 | 
	
		
			
				|  |  | +#include <linux/io.h>
 | 
	
		
			
				|  |  | +#include <linux/clk.h>
 | 
	
		
			
				|  |  | +#include <linux/clkdev.h>
 | 
	
		
			
				|  |  | +#include <linux/pm_runtime.h>
 | 
	
		
			
				|  |  | +#include <linux/of.h>
 | 
	
		
			
				|  |  | +#include <linux/notifier.h>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include "omap_device.h"
 | 
	
		
			
				|  |  | +#include "omap_hwmod.h"
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* These parameters are passed to _omap_device_{de,}activate() */
 | 
	
		
			
				|  |  | +#define USE_WAKEUP_LAT			0
 | 
	
		
			
				|  |  | +#define IGNORE_WAKEUP_LAT		1
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static int omap_early_device_register(struct platform_device *pdev);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static struct omap_device_pm_latency omap_default_latency[] = {
 | 
	
		
			
				|  |  | +	{
 | 
	
		
			
				|  |  | +		.deactivate_func = omap_device_idle_hwmods,
 | 
	
		
			
				|  |  | +		.activate_func   = omap_device_enable_hwmods,
 | 
	
		
			
				|  |  | +		.flags = OMAP_DEVICE_LATENCY_AUTO_ADJUST,
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* Private functions */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * _omap_device_activate - increase device readiness
 | 
	
		
			
				|  |  | + * @od: struct omap_device *
 | 
	
		
			
				|  |  | + * @ignore_lat: increase to latency target (0) or full readiness (1)?
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Increase readiness of omap_device @od (thus decreasing device
 | 
	
		
			
				|  |  | + * wakeup latency, but consuming more power).  If @ignore_lat is
 | 
	
		
			
				|  |  | + * IGNORE_WAKEUP_LAT, make the omap_device fully active.  Otherwise,
 | 
	
		
			
				|  |  | + * if @ignore_lat is USE_WAKEUP_LAT, and the device's maximum wakeup
 | 
	
		
			
				|  |  | + * latency is greater than the requested maximum wakeup latency, step
 | 
	
		
			
				|  |  | + * backwards in the omap_device_pm_latency table to ensure the
 | 
	
		
			
				|  |  | + * device's maximum wakeup latency is less than or equal to the
 | 
	
		
			
				|  |  | + * requested maximum wakeup latency.  Returns 0.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +static int _omap_device_activate(struct omap_device *od, u8 ignore_lat)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	struct timespec a, b, c;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	dev_dbg(&od->pdev->dev, "omap_device: activating\n");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	while (od->pm_lat_level > 0) {
 | 
	
		
			
				|  |  | +		struct omap_device_pm_latency *odpl;
 | 
	
		
			
				|  |  | +		unsigned long long act_lat = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		od->pm_lat_level--;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		odpl = od->pm_lats + od->pm_lat_level;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		if (!ignore_lat &&
 | 
	
		
			
				|  |  | +		    (od->dev_wakeup_lat <= od->_dev_wakeup_lat_limit))
 | 
	
		
			
				|  |  | +			break;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		read_persistent_clock(&a);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		/* XXX check return code */
 | 
	
		
			
				|  |  | +		odpl->activate_func(od);
 | 
	
		
			
				|  |  | +
 |