Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d636d02a authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: kgsl: Add kernel API support for limits mitigation"

parents 12c3dc01 066a2536
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -978,4 +978,17 @@ void kgsl_snapshot_add_section(struct kgsl_device *device, u16 id,
	size_t (*func)(struct kgsl_device *, u8 *, size_t, void *),
	void *priv);

/**
 * struct kgsl_pwr_limit - limit structure for each client
 * @node: Local list node for the limits list
 * @level: requested power level
 * @device: pointer to the device structure
 */
struct kgsl_pwr_limit {
	struct list_head node;
	unsigned int level;
	struct kgsl_device *device;
};


#endif  /* __KGSL_DEVICE_H */
+202 −13
Original line number Diff line number Diff line
@@ -223,6 +223,33 @@ void kgsl_pwrctrl_pwrlevel_change_settings(struct kgsl_device *device,
	}
}

/**
 * kgsl_pwrctrl_set_thermal_cycle() - set the thermal cycle if required
 * @pwr: Pointer to the kgsl_pwrctrl struct
 * @level: the level to transition to
 */
void kgsl_pwrctrl_set_thermal_cycle(struct kgsl_pwrctrl *pwr,
						unsigned int new_level)
{
	if (new_level != pwr->thermal_pwrlevel)
		return;
	if (pwr->thermal_pwrlevel == pwr->sysfs_pwr_limit->level) {
		/* Thermal cycle for sysfs pwr limit, start cycling*/
		if (pwr->thermal_cycle == CYCLE_ENABLE) {
			pwr->thermal_cycle = CYCLE_ACTIVE;
			mod_timer(&pwr->thermal_timer, jiffies +
					(TH_HZ - pwr->thermal_timeout));
			pwr->thermal_highlow = 1;
		}
	} else {
		/* Non sysfs pwr limit, stop thermal cycle if active*/
		if (pwr->thermal_cycle == CYCLE_ACTIVE) {
			pwr->thermal_cycle = CYCLE_ENABLE;
			del_timer_sync(&pwr->thermal_timer);
		}
	}
}

/**
 * kgsl_pwrctrl_pwrlevel_change() - Validate and change power levels
 * @device: Pointer to the kgsl_device struct
@@ -262,13 +289,7 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
	 * If thermal cycling is required and the new level hits the
	 * thermal limit, kick off the cycling.
	 */
	if ((pwr->thermal_cycle == CYCLE_ENABLE) &&
			(new_level == pwr->thermal_pwrlevel)) {
		pwr->thermal_cycle = CYCLE_ACTIVE;
		mod_timer(&pwr->thermal_timer, jiffies +
				(TH_HZ - pwr->thermal_timeout));
		pwr->thermal_highlow = 1;
	}
	kgsl_pwrctrl_set_thermal_cycle(pwr, new_level);

	if (new_level == old_level)
		return;
@@ -537,7 +558,8 @@ static ssize_t kgsl_pwrctrl_max_gpuclk_store(struct device *dev,
		unsigned int hfreq, diff, udiff, i;
		if ((val < pwr->pwrlevels[pwr->num_pwrlevels - 1].gpu_freq) ||
			(val > pwr->pwrlevels[0].gpu_freq))
			goto done;
			goto err;

		/* Find the neighboring frequencies */
		for (i = 0; i < pwr->num_pwrlevels - 1; i++) {
			if ((pwr->pwrlevels[i].gpu_freq > val) &&
@@ -546,6 +568,8 @@ static ssize_t kgsl_pwrctrl_max_gpuclk_store(struct device *dev,
				break;
			}
		}
		if (i == pwr->num_pwrlevels - 1)
			goto err;
		hfreq = pwr->pwrlevels[i].gpu_freq;
		diff =  hfreq - pwr->pwrlevels[i + 1].gpu_freq;
		udiff = hfreq - val;
@@ -555,13 +579,14 @@ static ssize_t kgsl_pwrctrl_max_gpuclk_store(struct device *dev,
		pwr->thermal_cycle = CYCLE_DISABLE;
		del_timer_sync(&pwr->thermal_timer);
	}
	mutex_unlock(&device->mutex);

	pwr->thermal_pwrlevel = (unsigned int) level;

	/* Update the current level using the new limit */
	kgsl_pwrctrl_pwrlevel_change(device, pwr->active_pwrlevel);
	if (pwr->sysfs_pwr_limit)
		kgsl_pwr_limits_set_freq(pwr->sysfs_pwr_limit,
					pwr->pwrlevels[level].gpu_freq);
	return count;

done:
err:
	mutex_unlock(&device->mutex);
	return count;
}
@@ -1447,6 +1472,10 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)
	setup_timer(&pwr->thermal_timer, kgsl_thermal_timer,
			(unsigned long) device);

	INIT_LIST_HEAD(&pwr->limits);
	spin_lock_init(&pwr->limits_lock);
	pwr->sysfs_pwr_limit = kgsl_pwr_limits_add(KGSL_DEVICE_3D0);

	devfreq_vbif_register_callback(kgsl_get_bw);

	return result;
@@ -1492,6 +1521,12 @@ void kgsl_pwrctrl_close(struct kgsl_device *device)

	pwr->grp_clks[0] = NULL;
	pwr->power_flags = 0;

	if (!IS_ERR_OR_NULL(pwr->sysfs_pwr_limit)) {
		list_del(&pwr->sysfs_pwr_limit->node);
		kfree(pwr->sysfs_pwr_limit);
		pwr->sysfs_pwr_limit = NULL;
	}
}

/**
@@ -2015,3 +2050,157 @@ int kgsl_active_count_wait(struct kgsl_device *device, int count)
	return result;
}
EXPORT_SYMBOL(kgsl_active_count_wait);

/**
 * _update_limits() - update the limits based on the current requests
 * @limit: Pointer to the limits structure
 * @reason: Reason for the update
 * @level: Level if any to be set
 *
 * Set the thermal pwrlevel based on the current limits
 */
static void _update_limits(struct kgsl_pwr_limit *limit, unsigned int reason,
							unsigned int level)
{
	struct kgsl_device *device = limit->device;
	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
	struct kgsl_pwr_limit *temp_limit;
	unsigned int max_level = 0;

	spin_lock(&pwr->limits_lock);
	switch (reason) {
	case KGSL_PWR_ADD_LIMIT:
			list_add(&limit->node, &pwr->limits);
			break;
	case KGSL_PWR_DEL_LIMIT:
			list_del(&limit->node);
			if (list_empty(&pwr->limits))
				goto done;
			break;
	case KGSL_PWR_SET_LIMIT:
			limit->level = level;
			break;
	}

	list_for_each_entry(temp_limit, &pwr->limits, node) {
		max_level = max_t(unsigned int, max_level, temp_limit->level);
	}

done:
	spin_unlock(&pwr->limits_lock);

	mutex_lock(&device->mutex);
	pwr->thermal_pwrlevel = max_level;
	kgsl_pwrctrl_pwrlevel_change(device, pwr->active_pwrlevel);
	mutex_unlock(&device->mutex);
}

/**
 * kgsl_pwr_limits_add() - Add a new pwr limit
 * @id: Device ID
 *
 * Allocate a pwr limit structure for the client, add it to the limits
 * list and return the pointer to the client
 */
void *kgsl_pwr_limits_add(enum kgsl_deviceid id)
{
	struct kgsl_device *device = kgsl_get_device(id);
	struct kgsl_pwr_limit *limit;

	if (IS_ERR_OR_NULL(device))
		return NULL;

	limit = kzalloc(sizeof(struct kgsl_pwr_limit),
						GFP_KERNEL);
	if (limit == NULL)
		return ERR_PTR(-ENOMEM);
	limit->device = device;

	_update_limits(limit, KGSL_PWR_ADD_LIMIT, 0);
	return limit;
}
EXPORT_SYMBOL(kgsl_pwr_limits_add);

/**
 * kgsl_pwr_limits_del() - Unregister the pwr limit client and
 * adjust the thermal limits
 * @limit_ptr: Client handle
 *
 * Delete the client handle from the thermal list and adjust the
 * active clocks if needed.
 */
void kgsl_pwr_limits_del(void *limit_ptr)
{
	struct kgsl_pwr_limit *limit = limit_ptr;
	if (IS_ERR(limit))
		return;

	_update_limits(limit, KGSL_PWR_DEL_LIMIT, 0);
	kfree(limit);
}
EXPORT_SYMBOL(kgsl_pwr_limits_del);

/**
 * kgsl_pwr_limits_set_freq() - Set the requested limit for the client
 * @limit_ptr: Client handle
 * @freq: Client requested frequency
 *
 * Set the new limit for the client and adjust the clocks
 */
int kgsl_pwr_limits_set_freq(void *limit_ptr, unsigned int freq)
{
	struct kgsl_pwrctrl *pwr;
	struct kgsl_pwr_limit *limit = limit_ptr;
	int level;

	if (IS_ERR(limit))
		return -EINVAL;

	pwr = &limit->device->pwrctrl;
	level = _get_nearest_pwrlevel(pwr, freq);
	if (level < 0)
		return -EINVAL;
	_update_limits(limit, KGSL_PWR_SET_LIMIT, level);
	return 0;
}
EXPORT_SYMBOL(kgsl_pwr_limits_set_freq);

/**
 * kgsl_pwr_limits_set_default() - Set the default thermal limit for the client
 * @limit_ptr: Client handle
 *
 * Set the default for the client and adjust the clocks
 */
void kgsl_pwr_limits_set_default(void *limit_ptr)
{
	struct kgsl_pwr_limit *limit = limit_ptr;

	if (IS_ERR(limit))
		return;

	_update_limits(limit, KGSL_PWR_SET_LIMIT, 0);
}
EXPORT_SYMBOL(kgsl_pwr_limits_set_default);

/**
 * kgsl_pwr_limits_get_freq() - Get the current limit
 * @id: Device ID
 *
 * Get the current limit set for the device
 */
unsigned int kgsl_pwr_limits_get_freq(enum kgsl_deviceid id)
{
	struct kgsl_device *device = kgsl_get_device(id);
	struct kgsl_pwrctrl *pwr;
	unsigned int freq;

	if (IS_ERR_OR_NULL(device))
		return 0;
	pwr = &device->pwrctrl;
	mutex_lock(&device->mutex);
	freq = pwr->pwrlevels[pwr->thermal_pwrlevel].gpu_freq;
	mutex_unlock(&device->mutex);

	return freq;
}
EXPORT_SYMBOL(kgsl_pwr_limits_get_freq);
+15 −0
Original line number Diff line number Diff line
@@ -41,6 +41,10 @@
	{ KGSL_CONSTRAINT_PWR_MIN, "Min" }, \
	{ KGSL_CONSTRAINT_PWR_MAX, "Max" }

#define KGSL_PWR_ADD_LIMIT 0
#define KGSL_PWR_DEL_LIMIT 1
#define KGSL_PWR_SET_LIMIT 2

/*
 * States for thermal cycling.  _DISABLE means that no cycling has been
 * requested.  _ENABLE means that cycling has been requested, but GPU
@@ -104,6 +108,14 @@ struct kgsl_pwr_constraint {
 * @constraint - currently active power constraint
 * @superfast - Boolean flag to indicate that the GPU start should be run in the
 * higher priority thread
 * @thermal_cycle_ws - Work struct for scheduling thermal cycling
 * @thermal_timer - Timer for thermal cycling
 * @thermal_timeout - Cycling timeout for switching between frequencies
 * @thermal_cycle - Is thermal cycling enabled
 * @thermal_highlow - flag for swithcing between high and low frequency
 * @limits - list head for limits
 * @limits_lock - spin lock to protect limits list
 * @sysfs_pwr_limit - pointer to the sysfs limits node
 */

struct kgsl_pwrctrl {
@@ -143,6 +155,9 @@ struct kgsl_pwrctrl {
	uint32_t thermal_timeout;
	uint32_t thermal_cycle;
	uint32_t thermal_highlow;
	struct list_head limits;
	spinlock_t limits_lock;
	struct kgsl_pwr_limit *sysfs_pwr_limit;
};

void kgsl_pwrctrl_irq(struct kgsl_device *device, int state);
+7 −0
Original line number Diff line number Diff line
@@ -74,6 +74,13 @@ struct kgsl_device_platform_data {
	unsigned int pm_qos_wakeup_latency;
};

/* Limits mitigations APIs */
void *kgsl_pwr_limits_add(enum kgsl_deviceid id);
void kgsl_pwr_limits_del(void *limit);
int kgsl_pwr_limits_set_freq(void *limit, unsigned int freq);
void kgsl_pwr_limits_set_default(void *limit);
unsigned int kgsl_pwr_limits_get_freq(enum kgsl_deviceid id);

#ifdef CONFIG_MSM_KGSL_DRM
int kgsl_gem_obj_addr(int drm_fd, int handle, unsigned long *start,
			unsigned long *len);