File: clock.c

package info (click to toggle)
crust-firmware 0.6-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,900 kB
  • sloc: ansic: 19,341; yacc: 596; lex: 479; makefile: 334; asm: 215; sh: 136; python: 42
file content (160 lines) | stat: -rw-r--r-- 4,114 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/*
 * Copyright © 2017-2022 The Crust Firmware Authors.
 * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only
 */

#include <clock.h>
#include <debug.h>
#include <device.h>
#include <error.h>
#include <intrusive.h>
#include <stdint.h>

#include "clock.h"

/**
 * Get the ops for the controller device providing this clock.
 */
static inline const struct clock_driver_ops *
clock_ops_for(const struct clock_handle *clock)
{
	const struct clock_driver *drv =
		container_of(clock->dev->drv, const struct clock_driver, drv);

	return &drv->ops;
}

/**
 * Get the mutable state for this clock.
 */
static inline struct clock_state *
clock_state_for(const struct clock_handle *clock)
{
	struct clock_device_state *state =
		container_of(clock->dev->state, struct clock_device_state, ds);

	return &state->cs[clock->id];
}

bool
clock_active(const struct clock_handle *clock)
{
	return clock_state_for(clock)->refcount;
}

void
clock_disable(const struct clock_handle *clock)
{
	/* Calling this function is only allowed after calling clock_get(). */
	assert(clock_active(clock));

	clock_ops_for(clock)->set_state(clock, CLOCK_STATE_GATED);
}

void
clock_enable(const struct clock_handle *clock)
{
	const struct clock_driver_ops *ops = clock_ops_for(clock);
	const struct clock_handle *parent;

	/* Calling this function is only allowed after calling clock_get(). */
	assert(clock_active(clock));

	/* If the clock has a parent, ensure the parent is enabled. */
	if ((parent = ops->get_parent(clock)))
		clock_enable(parent);

	ops->set_state(clock, CLOCK_STATE_ENABLED);
}

void
clock_get(const struct clock_handle *clock)
{
	const struct clock_driver_ops *ops = clock_ops_for(clock);
	struct clock_state *state = clock_state_for(clock);

	/* Perform additional setup if this is the first reference. */
	if (!state->refcount) {
		const struct clock_handle *parent;
		int err UNUSED;

		/* Ensure the controller's driver is loaded. */
		err = device_get(clock->dev);
		assert(err == SUCCESS);

		/* Ensure the clock's parent has an active reference. */
		if ((parent = ops->get_parent(clock)))
			clock_get(parent);

		debug("%s: Clock %u running at %u Hz", clock->dev->name,
		      clock->id, clock_get_rate(clock));
	}

	/* Bump the refcount only after successfully acquiring dependencies. */
	++state->refcount;

	/* Enable the clock. */
	clock_enable(clock);
}

uint32_t
clock_get_rate(const struct clock_handle *clock)
{
	const struct clock_driver_ops *ops = clock_ops_for(clock);
	const struct clock_handle *parent;
	uint32_t rate = 0;

	/* Initialize the rate with the parent's rate or a known safe value. */
	if ((parent = ops->get_parent(clock)))
		rate = clock_get_rate(parent);

	/* Call the driver function to calculate this clock's rate. */
	return ops->get_rate(clock, rate);
}

uint32_t
clock_get_state(const struct clock_handle *clock)
{
	const struct clock_driver_ops *ops = clock_ops_for(clock);
	const struct clock_handle *parent;
	uint32_t parent_state;

	/* If the clock has a parent, check the parent's state. */
	if ((parent = ops->get_parent(clock))) {
		parent_state = clock_get_state(parent);

		/* If the parent is not enabled, this clock has that state. */
		if (parent_state != CLOCK_STATE_ENABLED)
			return parent_state;
	}

	/* Call the driver function to check this clock's state. */
	return ops->get_state(clock);
}

void
clock_put(const struct clock_handle *clock)
{
	const struct clock_driver_ops *ops = clock_ops_for(clock);
	const struct clock_handle *parent;
	struct clock_state *state = clock_state_for(clock);

	/* Calling this function is only allowed after calling clock_get(). */
	assert(state->refcount);

	/* Do nothing if there are other consumers of this clock. */
	if (--state->refcount)
		return;

	debug("%s: Releasing clock %u", clock->dev->name, clock->id);

	/* Completely disable the clock once the last consumer is gone. */
	ops->set_state(clock, CLOCK_STATE_DISABLED);

	/* Drop the reference to the parent clock. */
	if ((parent = ops->get_parent(clock)))
		clock_put(parent);

	/* Drop the reference to the controller device. */
	device_put(clock->dev);
}