brintos

brintos / linux-shallow public Read only

0
0
Text · 9.8 KiB · ba7816f Raw
434 lines · c
1// SPDX-License-Identifier: GPL-2.0-only OR MIT2/* Copyright (c) 2023 Imagination Technologies Ltd. */3 4#include "pvr_device.h"5#include "pvr_fw.h"6#include "pvr_fw_startstop.h"7#include "pvr_power.h"8#include "pvr_queue.h"9#include "pvr_rogue_fwif.h"10 11#include <drm/drm_drv.h>12#include <drm/drm_managed.h>13#include <linux/clk.h>14#include <linux/interrupt.h>15#include <linux/mutex.h>16#include <linux/platform_device.h>17#include <linux/pm_runtime.h>18#include <linux/timer.h>19#include <linux/types.h>20#include <linux/workqueue.h>21 22#define POWER_SYNC_TIMEOUT_US (1000000) /* 1s */23 24#define WATCHDOG_TIME_MS (500)25 26/**27 * pvr_device_lost() - Mark GPU device as lost28 * @pvr_dev: Target PowerVR device.29 *30 * This will cause the DRM device to be unplugged.31 */32void33pvr_device_lost(struct pvr_device *pvr_dev)34{35	if (!pvr_dev->lost) {36		pvr_dev->lost = true;37		drm_dev_unplug(from_pvr_device(pvr_dev));38	}39}40 41static int42pvr_power_send_command(struct pvr_device *pvr_dev, struct rogue_fwif_kccb_cmd *pow_cmd)43{44	struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev;45	u32 slot_nr;46	u32 value;47	int err;48 49	WRITE_ONCE(*fw_dev->power_sync, 0);50 51	err = pvr_kccb_send_cmd_powered(pvr_dev, pow_cmd, &slot_nr);52	if (err)53		return err;54 55	/* Wait for FW to acknowledge. */56	return readl_poll_timeout(pvr_dev->fw_dev.power_sync, value, value != 0, 100,57				  POWER_SYNC_TIMEOUT_US);58}59 60static int61pvr_power_request_idle(struct pvr_device *pvr_dev)62{63	struct rogue_fwif_kccb_cmd pow_cmd;64 65	/* Send FORCED_IDLE request to FW. */66	pow_cmd.cmd_type = ROGUE_FWIF_KCCB_CMD_POW;67	pow_cmd.cmd_data.pow_data.pow_type = ROGUE_FWIF_POW_FORCED_IDLE_REQ;68	pow_cmd.cmd_data.pow_data.power_req_data.pow_request_type = ROGUE_FWIF_POWER_FORCE_IDLE;69 70	return pvr_power_send_command(pvr_dev, &pow_cmd);71}72 73static int74pvr_power_request_pwr_off(struct pvr_device *pvr_dev)75{76	struct rogue_fwif_kccb_cmd pow_cmd;77 78	/* Send POW_OFF request to firmware. */79	pow_cmd.cmd_type = ROGUE_FWIF_KCCB_CMD_POW;80	pow_cmd.cmd_data.pow_data.pow_type = ROGUE_FWIF_POW_OFF_REQ;81	pow_cmd.cmd_data.pow_data.power_req_data.forced = true;82 83	return pvr_power_send_command(pvr_dev, &pow_cmd);84}85 86static int87pvr_power_fw_disable(struct pvr_device *pvr_dev, bool hard_reset)88{89	if (!hard_reset) {90		int err;91 92		cancel_delayed_work_sync(&pvr_dev->watchdog.work);93 94		err = pvr_power_request_idle(pvr_dev);95		if (err)96			return err;97 98		err = pvr_power_request_pwr_off(pvr_dev);99		if (err)100			return err;101	}102 103	return pvr_fw_stop(pvr_dev);104}105 106static int107pvr_power_fw_enable(struct pvr_device *pvr_dev)108{109	int err;110 111	err = pvr_fw_start(pvr_dev);112	if (err)113		return err;114 115	err = pvr_wait_for_fw_boot(pvr_dev);116	if (err) {117		drm_err(from_pvr_device(pvr_dev), "Firmware failed to boot\n");118		pvr_fw_stop(pvr_dev);119		return err;120	}121 122	queue_delayed_work(pvr_dev->sched_wq, &pvr_dev->watchdog.work,123			   msecs_to_jiffies(WATCHDOG_TIME_MS));124 125	return 0;126}127 128bool129pvr_power_is_idle(struct pvr_device *pvr_dev)130{131	/*132	 * FW power state can be out of date if a KCCB command has been submitted but the FW hasn't133	 * started processing it yet. So also check the KCCB status.134	 */135	enum rogue_fwif_pow_state pow_state = READ_ONCE(pvr_dev->fw_dev.fwif_sysdata->pow_state);136	bool kccb_idle = pvr_kccb_is_idle(pvr_dev);137 138	return (pow_state == ROGUE_FWIF_POW_IDLE) && kccb_idle;139}140 141static bool142pvr_watchdog_kccb_stalled(struct pvr_device *pvr_dev)143{144	/* Check KCCB commands are progressing. */145	u32 kccb_cmds_executed = pvr_dev->fw_dev.fwif_osdata->kccb_cmds_executed;146	bool kccb_is_idle = pvr_kccb_is_idle(pvr_dev);147 148	if (pvr_dev->watchdog.old_kccb_cmds_executed == kccb_cmds_executed && !kccb_is_idle) {149		pvr_dev->watchdog.kccb_stall_count++;150 151		/*152		 * If we have commands pending with no progress for 2 consecutive polls then153		 * consider KCCB command processing stalled.154		 */155		if (pvr_dev->watchdog.kccb_stall_count == 2) {156			pvr_dev->watchdog.kccb_stall_count = 0;157			return true;158		}159	} else if (pvr_dev->watchdog.old_kccb_cmds_executed == kccb_cmds_executed) {160		bool has_active_contexts;161 162		mutex_lock(&pvr_dev->queues.lock);163		has_active_contexts = list_empty(&pvr_dev->queues.active);164		mutex_unlock(&pvr_dev->queues.lock);165 166		if (has_active_contexts) {167			/* Send a HEALTH_CHECK command so we can verify FW is still alive. */168			struct rogue_fwif_kccb_cmd health_check_cmd;169 170			health_check_cmd.cmd_type = ROGUE_FWIF_KCCB_CMD_HEALTH_CHECK;171 172			pvr_kccb_send_cmd_powered(pvr_dev, &health_check_cmd, NULL);173		}174	} else {175		pvr_dev->watchdog.old_kccb_cmds_executed = kccb_cmds_executed;176		pvr_dev->watchdog.kccb_stall_count = 0;177	}178 179	return false;180}181 182static void183pvr_watchdog_worker(struct work_struct *work)184{185	struct pvr_device *pvr_dev = container_of(work, struct pvr_device,186						  watchdog.work.work);187	bool stalled;188 189	if (pvr_dev->lost)190		return;191 192	if (pm_runtime_get_if_in_use(from_pvr_device(pvr_dev)->dev) <= 0)193		goto out_requeue;194 195	if (!pvr_dev->fw_dev.booted)196		goto out_pm_runtime_put;197 198	stalled = pvr_watchdog_kccb_stalled(pvr_dev);199 200	if (stalled) {201		drm_err(from_pvr_device(pvr_dev), "FW stalled, trying hard reset");202 203		pvr_power_reset(pvr_dev, true);204		/* Device may be lost at this point. */205	}206 207out_pm_runtime_put:208	pm_runtime_put(from_pvr_device(pvr_dev)->dev);209 210out_requeue:211	if (!pvr_dev->lost) {212		queue_delayed_work(pvr_dev->sched_wq, &pvr_dev->watchdog.work,213				   msecs_to_jiffies(WATCHDOG_TIME_MS));214	}215}216 217/**218 * pvr_watchdog_init() - Initialise watchdog for device219 * @pvr_dev: Target PowerVR device.220 *221 * Returns:222 *  * 0 on success, or223 *  * -%ENOMEM on out of memory.224 */225int226pvr_watchdog_init(struct pvr_device *pvr_dev)227{228	INIT_DELAYED_WORK(&pvr_dev->watchdog.work, pvr_watchdog_worker);229 230	return 0;231}232 233int234pvr_power_device_suspend(struct device *dev)235{236	struct platform_device *plat_dev = to_platform_device(dev);237	struct drm_device *drm_dev = platform_get_drvdata(plat_dev);238	struct pvr_device *pvr_dev = to_pvr_device(drm_dev);239	int err = 0;240	int idx;241 242	if (!drm_dev_enter(drm_dev, &idx))243		return -EIO;244 245	if (pvr_dev->fw_dev.booted) {246		err = pvr_power_fw_disable(pvr_dev, false);247		if (err)248			goto err_drm_dev_exit;249	}250 251	clk_disable_unprepare(pvr_dev->mem_clk);252	clk_disable_unprepare(pvr_dev->sys_clk);253	clk_disable_unprepare(pvr_dev->core_clk);254 255err_drm_dev_exit:256	drm_dev_exit(idx);257 258	return err;259}260 261int262pvr_power_device_resume(struct device *dev)263{264	struct platform_device *plat_dev = to_platform_device(dev);265	struct drm_device *drm_dev = platform_get_drvdata(plat_dev);266	struct pvr_device *pvr_dev = to_pvr_device(drm_dev);267	int idx;268	int err;269 270	if (!drm_dev_enter(drm_dev, &idx))271		return -EIO;272 273	err = clk_prepare_enable(pvr_dev->core_clk);274	if (err)275		goto err_drm_dev_exit;276 277	err = clk_prepare_enable(pvr_dev->sys_clk);278	if (err)279		goto err_core_clk_disable;280 281	err = clk_prepare_enable(pvr_dev->mem_clk);282	if (err)283		goto err_sys_clk_disable;284 285	if (pvr_dev->fw_dev.booted) {286		err = pvr_power_fw_enable(pvr_dev);287		if (err)288			goto err_mem_clk_disable;289	}290 291	drm_dev_exit(idx);292 293	return 0;294 295err_mem_clk_disable:296	clk_disable_unprepare(pvr_dev->mem_clk);297 298err_sys_clk_disable:299	clk_disable_unprepare(pvr_dev->sys_clk);300 301err_core_clk_disable:302	clk_disable_unprepare(pvr_dev->core_clk);303 304err_drm_dev_exit:305	drm_dev_exit(idx);306 307	return err;308}309 310int311pvr_power_device_idle(struct device *dev)312{313	struct platform_device *plat_dev = to_platform_device(dev);314	struct drm_device *drm_dev = platform_get_drvdata(plat_dev);315	struct pvr_device *pvr_dev = to_pvr_device(drm_dev);316 317	return pvr_power_is_idle(pvr_dev) ? 0 : -EBUSY;318}319 320/**321 * pvr_power_reset() - Reset the GPU322 * @pvr_dev: Device pointer323 * @hard_reset: %true for hard reset, %false for soft reset324 *325 * If @hard_reset is %false and the FW processor fails to respond during the reset process, this326 * function will attempt a hard reset.327 *328 * If a hard reset fails then the GPU device is reported as lost.329 *330 * Returns:331 *  * 0 on success, or332 *  * Any error code returned by pvr_power_get, pvr_power_fw_disable or pvr_power_fw_enable().333 */334int335pvr_power_reset(struct pvr_device *pvr_dev, bool hard_reset)336{337	bool queues_disabled = false;338	int err;339 340	/*341	 * Take a power reference during the reset. This should prevent any interference with the342	 * power state during reset.343	 */344	WARN_ON(pvr_power_get(pvr_dev));345 346	down_write(&pvr_dev->reset_sem);347 348	if (pvr_dev->lost) {349		err = -EIO;350		goto err_up_write;351	}352 353	/* Disable IRQs for the duration of the reset. */354	disable_irq(pvr_dev->irq);355 356	do {357		if (hard_reset) {358			pvr_queue_device_pre_reset(pvr_dev);359			queues_disabled = true;360		}361 362		err = pvr_power_fw_disable(pvr_dev, hard_reset);363		if (!err) {364			if (hard_reset) {365				pvr_dev->fw_dev.booted = false;366				WARN_ON(pm_runtime_force_suspend(from_pvr_device(pvr_dev)->dev));367 368				err = pvr_fw_hard_reset(pvr_dev);369				if (err)370					goto err_device_lost;371 372				err = pm_runtime_force_resume(from_pvr_device(pvr_dev)->dev);373				pvr_dev->fw_dev.booted = true;374				if (err)375					goto err_device_lost;376			} else {377				/* Clear the FW faulted flags. */378				pvr_dev->fw_dev.fwif_sysdata->hwr_state_flags &=379					~(ROGUE_FWIF_HWR_FW_FAULT |380					  ROGUE_FWIF_HWR_RESTART_REQUESTED);381			}382 383			pvr_fw_irq_clear(pvr_dev);384 385			err = pvr_power_fw_enable(pvr_dev);386		}387 388		if (err && hard_reset)389			goto err_device_lost;390 391		if (err && !hard_reset) {392			drm_err(from_pvr_device(pvr_dev), "FW stalled, trying hard reset");393			hard_reset = true;394		}395	} while (err);396 397	if (queues_disabled)398		pvr_queue_device_post_reset(pvr_dev);399 400	enable_irq(pvr_dev->irq);401 402	up_write(&pvr_dev->reset_sem);403 404	pvr_power_put(pvr_dev);405 406	return 0;407 408err_device_lost:409	drm_err(from_pvr_device(pvr_dev), "GPU device lost");410	pvr_device_lost(pvr_dev);411 412	/* Leave IRQs disabled if the device is lost. */413 414	if (queues_disabled)415		pvr_queue_device_post_reset(pvr_dev);416 417err_up_write:418	up_write(&pvr_dev->reset_sem);419 420	pvr_power_put(pvr_dev);421 422	return err;423}424 425/**426 * pvr_watchdog_fini() - Shutdown watchdog for device427 * @pvr_dev: Target PowerVR device.428 */429void430pvr_watchdog_fini(struct pvr_device *pvr_dev)431{432	cancel_delayed_work_sync(&pvr_dev->watchdog.work);433}434