/** * 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; }
/** * 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; }
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; }
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); }
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); }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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); }