drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c

Source file repositories/reference/linux-study-clean/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c

File Facts

System
Linux kernel
Corpus path
drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c
Extension
.c
Size
5267 bytes
Lines
199
Domain
Driver Families
Bucket
drivers/gpu
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

if (wait->reads++ == 16) {
			nvkm_fatal(subdev, "stalled at %016llx\n", time);
			return -ETIMEDOUT;
		}
	} else {
		wait->time1 = time;
		wait->reads = 1;
	}

	if (wait->time1 - wait->time0 > wait->limit)
		return -ETIMEDOUT;

	return wait->time1 - wait->time0;
}

void
nvkm_timer_wait_init(struct nvkm_device *device, u64 nsec,
		     struct nvkm_timer_wait *wait)
{
	wait->tmr = device->timer;
	wait->limit = nsec;
	wait->reads = 0;
}

u64
nvkm_timer_read(struct nvkm_timer *tmr)
{
	return tmr->func->read(tmr);
}

void
nvkm_timer_alarm_trigger(struct nvkm_timer *tmr)
{
	struct nvkm_alarm *alarm, *atemp;
	unsigned long flags;
	LIST_HEAD(exec);

	/* Process pending alarms. */
	spin_lock_irqsave(&tmr->lock, flags);
	list_for_each_entry_safe(alarm, atemp, &tmr->alarms, head) {
		/* Have we hit the earliest alarm that hasn't gone off? */
		if (alarm->timestamp > nvkm_timer_read(tmr)) {
			/* Schedule it.  If we didn't race, we're done. */
			tmr->func->alarm_init(tmr, alarm->timestamp);
			if (alarm->timestamp > nvkm_timer_read(tmr))
				break;
		}

		/* Move to completed list.  We'll drop the lock before
		 * executing the callback so it can reschedule itself.
		 */
		list_del_init(&alarm->head);
		list_add(&alarm->exec, &exec);
	}

	/* Shut down interrupt if no more pending alarms. */
	if (list_empty(&tmr->alarms))
		tmr->func->alarm_fini(tmr);
	spin_unlock_irqrestore(&tmr->lock, flags);

	/* Execute completed callbacks. */
	list_for_each_entry_safe(alarm, atemp, &exec, exec) {
		list_del(&alarm->exec);
		alarm->func(alarm);
	}
}

void
nvkm_timer_alarm(struct nvkm_timer *tmr, u32 nsec, struct nvkm_alarm *alarm)
{
	struct nvkm_alarm *list;
	unsigned long flags;

	/* Remove alarm from pending list.
	 *
	 * This both protects against the corruption of the list,
	 * and implements alarm rescheduling/cancellation.
	 */
	spin_lock_irqsave(&tmr->lock, flags);
	list_del_init(&alarm->head);

	if (nsec) {
		/* Insert into pending list, ordered earliest to latest. */
		alarm->timestamp = nvkm_timer_read(tmr) + nsec;
		list_for_each_entry(list, &tmr->alarms, head) {
			if (list->timestamp > alarm->timestamp)
				break;
		}

		list_add_tail(&alarm->head, &list->head);

Annotation

Implementation Notes