summaryrefslogtreecommitdiff
path: root/include/task.h
blob: f9f50afbd906a60cff7063b87f8e4076788fee5c (plain)
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
/* Copyright 2012 The ChromiumOS Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/* Task scheduling / events module for Chrome EC operating system */

#ifndef __CROS_EC_TASK_H
#define __CROS_EC_TASK_H

#include "common.h"
#include "compile_time_macros.h"
#include "task_id.h"

/* Task event bitmasks */
/* Tasks may use the bits in TASK_EVENT_CUSTOM_BIT for their own events */
#define TASK_EVENT_CUSTOM_BIT(x) BUILD_CHECK_INLINE(BIT(x), BIT(x) & 0x0ffff)

/* Used to signal that sysjump preparation has completed */
#define TASK_EVENT_SYSJUMP_READY BIT(16)

/* Used to signal that IPC layer is available for sending new data */
#define TASK_EVENT_IPC_READY	BIT(17)

#define TASK_EVENT_PD_AWAKE	BIT(18)

/* npcx peci event */
#define TASK_EVENT_PECI_DONE	BIT(19)

/* I2C tx/rx interrupt handler completion event. */
#ifdef CHIP_STM32
#define TASK_EVENT_I2C_COMPLETION(port) \
				(1 << ((port) + 20))
#define TASK_EVENT_I2C_IDLE	(TASK_EVENT_I2C_COMPLETION(0))
#define TASK_EVENT_MAX_I2C	6
#ifdef I2C_PORT_COUNT
#if (I2C_PORT_COUNT > TASK_EVENT_MAX_I2C)
#error "Too many i2c ports for i2c events"
#endif
#endif
#else
#define TASK_EVENT_I2C_IDLE	BIT(20)
#define TASK_EVENT_PS2_DONE	BIT(21)
#endif

/* DMA transmit complete event */
#define TASK_EVENT_DMA_TC       BIT(26)
/* ADC interrupt handler event */
#define TASK_EVENT_ADC_DONE	BIT(27)
/*
 * task_reset() that was requested has been completed
 *
 * For test-only builds, may be used by some tasks to restart themselves.
 */
#define TASK_EVENT_RESET_DONE   BIT(28)
/* task_wake() called on task */
#define TASK_EVENT_WAKE		BIT(29)
/* Mutex unlocking */
#define TASK_EVENT_MUTEX	BIT(30)
/*
 * Timer expired.  For example, task_wait_event() timed out before receiving
 * another event.
 */
#define TASK_EVENT_TIMER	(1U << 31)

/* Maximum time for task_wait_event() */
#define TASK_MAX_WAIT_US 0x7fffffff

/**
 * Disable CPU interrupt bit.
 *
 * This might break the system so think really hard before using these. There
 * are usually better ways of accomplishing this.
 */
void interrupt_disable(void);

/**
 * Enable CPU interrupt bit.
 */
void interrupt_enable(void);

/**
 * Check if interrupts are enabled
 */
int is_interrupt_enabled(void);

/*
 * Define irq_lock and irq_unlock that match the function signatures to Zephyr's
 * functions. In reality, these simply call the current implementation of
 * interrupt_disable() and interrupt_enable().
 */
#ifndef CONFIG_ZEPHYR
/**
 * Perform the same operation as interrupt_disable but allow nesting. The
 * return value from this function should be used as the argument to
 * irq_unlock. Do not attempt to parse the value, it is a representation
 * of the state and not an indication of any form of count.
 *
 * For more information see:
 * https://docs.zephyrproject.org/latest/reference/kernel/other/interrupts.html#c.irq_lock
 *
 * @return Lock key to use for restoring the state via irq_unlock.
 */
uint32_t irq_lock(void);

/**
 * Perform the same operation as interrupt_enable but allow nesting. The key
 * should be the unchanged value returned by irq_lock.
 *
 * For more information see:
 * https://docs.zephyrproject.org/latest/reference/kernel/other/interrupts.html#c.irq_unlock
 *
 * @param key The lock-out key used to restore the interrupt state.
 */
void irq_unlock(uint32_t key);
#endif /* CONFIG_ZEPHYR */

/**
 * Return true if we are in interrupt context.
 */
int in_interrupt_context(void);

/**
 * Return true if we are in software interrupt context.
 */
int in_soft_interrupt_context(void);

/**
 * Return current interrupt mask with disabling interrupt. Meaning is
 * chip-specific and should not be examined; just pass it to set_int_mask() to
 * restore a previous interrupt state after interrupt disable.
 */
uint32_t read_clear_int_mask(void);

/**
 * Set interrupt mask. As with interrupt_disable(), use with care.
 */
void set_int_mask(uint32_t val);

/**
 * Set a task event.
 *
 * If the task is higher priority than the current task, this will cause an
 * immediate context switch to the new task.
 *
 * Can be called both in interrupt context and task context.
 *
 * @param tskid		Task to set event for
 * @param event		Event bitmap to set (TASK_EVENT_*)
 * @return		The bitmap of events which occurred if wait!=0, else 0.
 */
uint32_t task_set_event(task_id_t tskid, uint32_t event);

/**
 * Wake a task.  This sends it the TASK_EVENT_WAKE event.
 *
 * @param tskid		Task to wake
 */
static inline void task_wake(task_id_t tskid)
{
	task_set_event(tskid, TASK_EVENT_WAKE);
}

/**
 * Return the identifier of the task currently running.
 */
task_id_t task_get_current(void);

/**
 * Return a pointer to the bitmap of events of the task.
 */
uint32_t *task_get_event_bitmap(task_id_t tskid);

/**
 * Wait for the next event.
 *
 * If one or more events are already pending, returns immediately.  Otherwise,
 * it de-schedules the calling task and wakes up the next one in the priority
 * order.  Automatically clears the bitmap of received events before returning
 * the events which are set.
 *
 * @param timeout_us	If > 0, sets a timer to produce the TASK_EVENT_TIMER
 *			event after the specified micro-second duration.
 *
 * @return The bitmap of received events.
 */
uint32_t task_wait_event(int timeout_us);

/**
 * Wait for any event included in an event mask.
 *
 * If one or more events are already pending, returns immediately.  Otherwise,
 * it de-schedules the calling task and wakes up the next one in the priority
 * order.  Automatically clears the bitmap of received events before returning
 * the events which are set.
 *
 * @param event_mask	Bitmap of task events to wait for.
 *
 * @param timeout_us	If > 0, sets a timer to produce the TASK_EVENT_TIMER
 *			event after the specified micro-second duration.
 *
 * @return		The bitmap of received events. Includes
 *			TASK_EVENT_TIMER if the timeout is reached.
 */
uint32_t task_wait_event_mask(uint32_t event_mask, int timeout_us);

/**
 * Prints the list of tasks.
 *
 * Uses the command output channel.  May be called from interrupt level.
 */
void task_print_list(void);

/**
 * Returns the name of the task.
 */
const char *task_get_name(task_id_t tskid);

#ifdef CONFIG_TASK_PROFILING
/**
 * Start tracking an interrupt.
 *
 * This must be called from interrupt context (!) before the interrupt routine
 * is called.
 */
void task_start_irq_handler(void *excep_return);
void task_end_irq_handler(void *excep_return);
#else
#define task_start_irq_handler(excep_return)
#endif

/**
 * Change the task scheduled to run after returning from the exception.
 *
 * If task_send_event() has been called and has set need_resched flag,
 * re-computes which task is running and eventually swaps the context
 * saved on the process stack to restore the new one at exception exit.
 *
 * This must be called from interrupt context (!) and is designed to be the
 * last call of the interrupt handler.
 */
void task_resched_if_needed(void *excep_return);

/**
 * Initialize tasks and interrupt controller.
 */
void task_pre_init(void);

/**
 * Start task scheduling.  Does not normally return.
 */
int task_start(void);

/**
 * Return non-zero if task_start() has been called and task scheduling has
 * started.
 */
int task_start_called(void);

#ifdef CONFIG_FPU
/**
 * Clear floating-point used flag for currently executing task. This means the
 * FPU regs will not be stored on context switches until the next time floating
 * point is used for currently executing task.
 */
void task_clear_fp_used(void);
#endif

/**
 * Mark all tasks as ready to run and reschedule the highest priority task.
 */
void task_enable_all_tasks(void);

/**
 * Enable a task.
 */
void task_enable_task(task_id_t tskid);

/**
 * Disable a task.
 *
 * If the task disable itself, this will cause an immediate reschedule.
 */
void task_disable_task(task_id_t tskid);

/**
 * Enable an interrupt.
 */
void task_enable_irq(int irq);

/**
 * Disable an interrupt.
 */
void task_disable_irq(int irq);

/**
 * Software-trigger an interrupt.
 */
void task_trigger_irq(int irq);

/*
 * A task that supports resets may call this to indicate that it may be reset
 * at any point between this call and the next call to task_disable_resets().
 *
 * Calling this function will trigger any resets that were requested while
 * resets were disabled.
 *
 * It is not expected for this to be called if resets are already enabled.
 */
void task_enable_resets(void);

/*
 * A task that supports resets may call this to indicate that it may not be
 * reset until the next call to task_enable_resets(). Any calls to task_reset()
 * during this time will cause a reset request to be queued, and executed
 * the next time task_enable_resets() is called.
 *
 * Must not be called if resets are already disabled.
 */
void task_disable_resets(void);

/*
 * If the current task was reset, completes the reset operation.
 *
 * Returns a non-zero value if the task was reset; tasks with state outside
 * of the stack should perform any necessary cleanup immediately after calling
 * this function.
 *
 * Tasks that support reset must call this function once at startup before
 * doing anything else.
 *
 * Must only be called once at task startup.
 */
int task_reset_cleanup(void);

/*
 * Resets the specified task, which must not be the current task,
 * to initial state.
 *
 * Returns EC_SUCCESS, or EC_ERROR_INVAL if the specified task does
 * not support resets.
 *
 * If wait is true, blocks until the task has been reset. Otherwise,
 * returns immediately - in this case the task reset may be delayed until
 * that task can be safely reset. The duration of this delay depends on the
 * task implementation.
 */
int task_reset(task_id_t id, int wait);

/**
 * Clear a pending interrupt.
 *
 * Note that most interrupts can be removed from the pending state simply by
 * handling whatever caused the interrupt in the first place.  This only needs
 * to be called if an interrupt handler disables itself without clearing the
 * reason for the interrupt, and then the interrupt is re-enabled from a
 * different context.
 */
void task_clear_pending_irq(int irq);

#ifdef CONFIG_ZEPHYR
typedef struct k_mutex mutex_t;

#define mutex_lock(mtx) (k_mutex_lock(mtx, K_FOREVER))
#define mutex_unlock(mtx) (k_mutex_unlock(mtx))
#else
struct mutex {
	uint32_t lock;
	uint32_t waiters;
};

typedef struct mutex mutex_t;

/**
 * K_MUTEX_DEFINE is a macro normally provided by the Zephyr kernel,
 * and allows creation of a static mutex without the need to
 * initialize it.  We provide the same macro for CrOS EC OS so that we
 * can use it in shared code.
 */
#define K_MUTEX_DEFINE(name) static mutex_t name = { }

/**
 * Lock a mutex.
 *
 * This tries to lock the mutex mtx.  If the mutex is already locked by another
 * task, de-schedules the current task until the mutex is again unlocked.
 *
 * Must not be used in interrupt context!
 */
void mutex_lock(mutex_t *mtx);

/**
 * Release a mutex previously locked by the same task.
 */
void mutex_unlock(mutex_t *mtx);

/** Zephyr will try to init the mutex using `k_mutex_init()`. */
#define k_mutex_init(mutex) 0
#endif /* CONFIG_ZEPHYR */

struct irq_priority {
	uint8_t irq;
	uint8_t priority;
};

/*
 * Some cores may make use of this struct for mapping irqs to handlers
 * for DECLARE_IRQ in a linker-script defined section.
 */
struct irq_def {
	int irq;

	/* The routine which was declared as an IRQ */
	void (*routine)(void);

	/*
	 * The routine usually needs wrapped so the core can handle it
	 * as an IRQ.
	 */
	void (*handler)(void);
};

/*
 * Implement the DECLARE_IRQ(irq, routine, priority) macro which is
 * a core specific helper macro to declare an interrupt handler "routine".
 */
#ifndef CONFIG_ZEPHYR
#ifdef CONFIG_COMMON_RUNTIME
#include "irq_handler.h"
#else
#define IRQ_HANDLER(irqname) CONCAT3(irq_, irqname, _handler)
#define IRQ_HANDLER_OPT(irqname) CONCAT3(irq_, irqname, _handler_optional)
#define DECLARE_IRQ(irq, routine, priority) DECLARE_IRQ_(irq, routine, priority)
#define DECLARE_IRQ_(irq, routine, priority) \
	void IRQ_HANDLER_OPT(irq)(void) __attribute__((alias(#routine)));

/* Include ec.irqlist here for compilation dependency */
#define ENABLE_IRQ(x)
#include "ec.irqlist"
#endif /* CONFIG_COMMON_RUNTIME */
#endif /* !CONFIG_ZEPHYR */

#endif  /* __CROS_EC_TASK_H */