예제 #1
0
/**
 * omap_device_enable - fully activate an omap_device
 * @od: struct omap_device * to activate
 *
 * Do whatever is necessary for the hwmods underlying omap_device @od
 * to be accessible and ready to operate.  This generally involves
 * enabling clocks, setting SYSCONFIG registers; and in the future may
 * involve remuxing pins.  Device drivers should call this function
 * (through platform_data function pointers) where they would normally
 * enable clocks, etc.  Returns -EINVAL if called when the omap_device
 * is already enabled, or passes along the return value of
 * _omap_device_activate().
 */
int omap_device_enable(struct platform_device *pdev)
{
	int ret;
	struct omap_device *od;

	od = _find_by_pdev(pdev);

	if (od->_state == OMAP_DEVICE_STATE_ENABLED) {
		WARN(1, "omap_device: %s.%d: %s() called from invalid state %d\n",
		     od->pdev.name, od->pdev.id, __func__, od->_state);
		return -EINVAL;
	}

	/* Enable everything if we're enabling this device from scratch */
	if (od->_state == OMAP_DEVICE_STATE_UNKNOWN)
		od->pm_lat_level = od->pm_lats_cnt;

	ret = _omap_device_activate(od, IGNORE_WAKEUP_LAT);

	od->dev_wakeup_lat = 0;
	od->_dev_wakeup_lat_limit = UINT_MAX;
	od->_state = OMAP_DEVICE_STATE_ENABLED;

	return ret;
}
예제 #2
0
/**
 * omap_device_get_context_loss_count - get lost context count
 * @od: struct omap_device *
 *
 * Using the primary hwmod, query the context loss count for this
 * device.
 *
 * Callers should consider context for this device lost any time this
 * function returns a value different than the value the caller got
 * the last time it called this function.
 *
 * If any hwmods exist for the omap_device assoiated with @pdev,
 * return the context loss counter for that hwmod, otherwise return
 * zero.
 */
u32 omap_device_get_context_loss_count(struct platform_device *pdev)
{
	struct omap_device *od;
	u32 ret = 0;

	od = _find_by_pdev(pdev);

	if (od->hwmods_cnt)
		ret = omap_hwmod_get_context_loss_count(od->hwmods[0]);

	return ret;
}
예제 #3
0
void omap_device_populate_rate_fns(struct device *dev,
		int (*set_rate)(struct device *dev, unsigned long rate),
		unsigned long (*get_rate) (struct device *dev))
{
	struct platform_device *pdev;
	struct omap_device *od;

	pdev = container_of(dev, struct platform_device, dev);
	od = _find_by_pdev(pdev);

	od->set_rate = set_rate;
	od->get_rate = get_rate;
}
예제 #4
0
int omap_device_set_rate(struct device *dev, unsigned long freq)
{
	struct platform_device *pdev;
	struct omap_device *od;

	pdev = container_of(dev, struct platform_device, dev);
	od = _find_by_pdev(pdev);

	if (!od->set_rate) {
		dev_err(dev, "%s: No set_rate API for scaling device\n",
			__func__);
		return -ENODATA;
	}

	return od->set_rate(dev, freq);
}
예제 #5
0
unsigned long omap_device_get_rate(struct device *dev)
{
	struct platform_device *pdev;
	struct omap_device *od;

	pdev = container_of(dev, struct platform_device, dev);
	od = _find_by_pdev(pdev);


	if (!od->get_rate) {
		dev_err(dev, "%s: No get rate API for the device\n",
			__func__);
		return 0;
	}

	return od->get_rate(dev);
}
예제 #6
0
/**
 * omap_device_idle - idle an omap_device
 * @od: struct omap_device * to idle
 *
 * Idle omap_device @od by calling as many .deactivate_func() entries
 * in the omap_device's pm_lats table as is possible without exceeding
 * the device's maximum wakeup latency limit, pm_lat_limit.  Device
 * drivers should call this function (through platform_data function
 * pointers) where they would normally disable clocks after operations
 * complete, etc..  Returns -EINVAL if the omap_device is not
 * currently enabled, or passes along the return value of
 * _omap_device_deactivate().
 */
int omap_device_idle(struct platform_device *pdev)
{
	int ret;
	struct omap_device *od;

	od = _find_by_pdev(pdev);

	if (od->_state != OMAP_DEVICE_STATE_ENABLED) {
		WARN(1, "omap_device: %s.%d: %s() called from invalid state %d\n",
		     od->pdev.name, od->pdev.id, __func__, od->_state);
		return -EINVAL;
	}

	ret = _omap_device_deactivate(od, USE_WAKEUP_LAT);

	od->_state = OMAP_DEVICE_STATE_IDLE;

	return ret;
}
예제 #7
0
/**
 * omap_device_reset - reset an omap_device
 * @od: struct omap_device * to reset
 *
 * Reset omap_device @od by reseting all of the underlying omap_hwmods.
 * Used when a driver need to put the HW in a known state after a error
 * Returns -EINVAL if the omap_device is not currently enabled, or passes
 * along the return value of omap_hwmod_reset().
 */
int omap_device_reset(struct platform_device *pdev)
{
	int ret = 0;
	int i;
	struct omap_device *od;
	struct omap_hwmod *oh;

	od = _find_by_pdev(pdev);

	if (od->_state != OMAP_DEVICE_STATE_ENABLED) {
		WARN(1, "omap_device: %s.%d: %s() called from invalid state %d\n",
		     od->pdev.name, od->pdev.id, __func__, od->_state);
		return -EINVAL;
	}

	for (i = 0, oh = *od->hwmods; i < od->hwmods_cnt; i++, oh++)
		ret |= omap_hwmod_reset(oh);

	return ret;
}
예제 #8
0
/**
 * omap_device_align_pm_lat - activate/deactivate device to match wakeup lat lim
 * @od: struct omap_device *
 *
 * When a device's maximum wakeup latency limit changes, call some of
 * the .activate_func or .deactivate_func function pointers in the
 * omap_device's pm_lats array to ensure that the device's maximum
 * wakeup latency is less than or equal to the new latency limit.
 * Intended to be called by OMAP PM code whenever a device's maximum
 * wakeup latency limit changes (e.g., via
 * omap_pm_set_dev_wakeup_lat()).  Returns 0 if nothing needs to be
 * done (e.g., if the omap_device is not currently idle, or if the
 * wakeup latency is already current with the new limit) or passes
 * along the return value of _omap_device_deactivate() or
 * _omap_device_activate().
 */
int omap_device_align_pm_lat(struct platform_device *pdev,
			     u32 new_wakeup_lat_limit)
{
	int ret = -EINVAL;
	struct omap_device *od;

	od = _find_by_pdev(pdev);

	if (new_wakeup_lat_limit == od->dev_wakeup_lat)
		return 0;

	od->_dev_wakeup_lat_limit = new_wakeup_lat_limit;

	if (od->_state != OMAP_DEVICE_STATE_IDLE)
		return 0;
	else if (new_wakeup_lat_limit > od->dev_wakeup_lat)
		ret = _omap_device_deactivate(od, USE_WAKEUP_LAT);
	else if (new_wakeup_lat_limit < od->dev_wakeup_lat)
		ret = _omap_device_activate(od, USE_WAKEUP_LAT);

	return ret;
}
예제 #9
0
/**
 * omap_device_shutdown - shut down an omap_device
 * @od: struct omap_device * to shut down
 *
 * Shut down omap_device @od by calling all .deactivate_func() entries
 * in the omap_device's pm_lats table and then shutting down all of
 * the underlying omap_hwmods.  Used when a device is being "removed"
 * or a device driver is being unloaded.  Returns -EINVAL if the
 * omap_device is not currently enabled or idle, or passes along the
 * return value of _omap_device_deactivate().
 */
int omap_device_shutdown(struct platform_device *pdev)
{
	int ret, i;
	struct omap_device *od;

	od = _find_by_pdev(pdev);

	if (od->_state != OMAP_DEVICE_STATE_ENABLED &&
	    od->_state != OMAP_DEVICE_STATE_IDLE) {
		WARN(1, "omap_device: %s.%d: %s() called from invalid state %d\n",
		     od->pdev.name, od->pdev.id, __func__, od->_state);
		return -EINVAL;
	}

	ret = _omap_device_deactivate(od, IGNORE_WAKEUP_LAT);

	for (i = 0; i < od->hwmods_cnt; i++)
		omap_hwmod_shutdown(od->hwmods[i]);

	od->_state = OMAP_DEVICE_STATE_SHUTDOWN;

	return ret;
}
예제 #10
0
/**
 * omap_device_set_rate - Set a new rate at which the device is to operate
 * @req_dev : pointer to the device requesting the scaling.
 * @dev : pointer to the device that is to be scaled
 * @rate : the rnew rate for the device.
 *
 * This API gets the device opp table associated with this device and
 * tries putting the device to the requested rate and the voltage domain
 * associated with the device to the voltage corresponding to the
 * requested rate. Since multiple devices can be assocciated with a
 * voltage domain this API finds out the possible voltage the
 * voltage domain can enter and then decides on the final device
 * rate. Return 0 on success else the error value
 */
int omap_device_set_rate(struct device *req_dev, struct device *dev,
			unsigned long rate)
{
	struct omap_opp *opp;
	unsigned long volt, freq, min_freq, max_freq, flags;
	struct voltagedomain *voltdm;
	struct platform_device *pdev;
	struct omap_device *od;
	int ret;

	pdev = container_of(dev, struct platform_device, dev);
	od = _find_by_pdev(pdev);

	/* if in low power DPLL cascading mode, bail out early */
	if (cpu_is_omap44xx()) {
		read_lock_irqsave(&dpll_cascading_lock, flags);

		if (in_dpll_cascading) {
			ret = -EINVAL;
			goto out;
		}
	}

	/*
	 * Figure out if the desired frquency lies between the
	 * maximum and minimum possible for the particular device
	 */
	min_freq = 0;
	if (IS_ERR(opp_find_freq_ceil(dev, &min_freq))) {
		dev_err(dev, "%s: Unable to find lowest opp\n", __func__);
		ret = -ENODEV;
		goto out;
	}

	max_freq = ULONG_MAX;
	if (IS_ERR(opp_find_freq_floor(dev, &max_freq))) {
		dev_err(dev, "%s: Unable to find highest opp\n", __func__);
		ret = -ENODEV;
		goto out;
	}

	if (rate < min_freq)
		freq = min_freq;
	else if (rate > max_freq)
		freq = max_freq;
	else
		freq = rate;

	/* Get the possible rate from the opp layer */
	opp = opp_find_freq_ceil(dev, &freq);
	if (IS_ERR(opp)) {
		dev_dbg(dev, "%s: Unable to find OPP for freq%ld\n",
			__func__, rate);
		ret = -ENODEV;
		goto out;
	}
	if (unlikely(freq != rate))
		dev_dbg(dev, "%s: Available freq %ld != dpll freq %ld.\n",
			__func__, freq, rate);

	/* Get the voltage corresponding to the requested frequency */
	volt = opp_get_voltage(opp);

	/*
	 * Call into the voltage layer to get the final voltage possible
	 * for the voltage domain associated with the device.
	 */
	voltdm = od->hwmods[0]->voltdm;
	ret = omap_voltage_add_userreq(voltdm, req_dev, &volt);
	if (ret) {
		dev_err(dev, "%s: Unable to get the final volt for scaling\n",
			__func__);
		goto out;
	}

	/* Do the actual scaling */
	ret =  omap_voltage_scale(voltdm);
out:
	if (cpu_is_omap44xx())
		read_unlock_irqrestore(&dpll_cascading_lock, flags);

	return ret;
}
예제 #11
0
/**
 * omap_device_scale() - Set a new rate at which the device is to operate
 * @req_dev:	pointer to the device requesting the scaling.
 * @dev:	pointer to the device that is to be scaled
 * @rate:	the rnew rate for the device.
 *
 * This API gets the device opp table associated with this device and
 * tries putting the device to the requested rate and the voltage domain
 * associated with the device to the voltage corresponding to the
 * requested rate. Since multiple devices can be assocciated with a
 * voltage domain this API finds out the possible voltage the
 * voltage domain can enter and then decides on the final device
 * rate. Return 0 on success else the error value
 */
int omap_device_scale(struct device *req_dev, struct device *dev,
			unsigned long rate)
{
	struct opp *opp;
	unsigned long volt, freq, min_freq, max_freq;
	struct voltagedomain *voltdm;
	struct platform_device *pdev;
	struct omap_device *od;
	int ret;

	pdev = container_of(dev, struct platform_device, dev);
	od = _find_by_pdev(pdev);

	/*
	 * Figure out if the desired frquency lies between the
	 * maximum and minimum possible for the particular device
	 */
	min_freq = 0;
	if (IS_ERR(opp_find_freq_ceil(dev, &min_freq))) {
		dev_err(dev, "%s: Unable to find lowest opp\n", __func__);
		return -ENODEV;
	}

	max_freq = ULONG_MAX;
	if (IS_ERR(opp_find_freq_floor(dev, &max_freq))) {
		dev_err(dev, "%s: Unable to find highest opp\n", __func__);
		return -ENODEV;
	}

	if (rate < min_freq)
		freq = min_freq;
	else if (rate > max_freq)
		freq = max_freq;
	else
		freq = rate;

	opp = opp_find_freq_ceil(dev, &freq);
	if (IS_ERR(opp)) {
		dev_err(dev, "%s: Unable to find OPP for freq%ld\n",
			__func__, rate);
		return -ENODEV;
	}

	/* Get the voltage corresponding to the requested frequency */
	volt = opp_get_voltage(opp);

	/*
	 * Call into the voltage layer to get the final voltage possible
	 * for the voltage domain associated with the device.
	 */
	voltdm = od->hwmods[0]->voltdm;
	ret = omap_voltage_add_request(voltdm, req_dev, &volt);
	if (ret) {
		dev_err(dev, "%s: Unable to get the final volt for scaling\n",
			__func__);
		return ret;
	}

	/* Do the actual scaling */
	return omap_voltage_scale(voltdm, volt);
}