162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
262306a36Sopenharmony_ci#ifndef _ASM_POWERPC_RTAS_WORK_AREA_H
362306a36Sopenharmony_ci#define _ASM_POWERPC_RTAS_WORK_AREA_H
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/build_bug.h>
662306a36Sopenharmony_ci#include <linux/sizes.h>
762306a36Sopenharmony_ci#include <linux/types.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <asm/page.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/**
1262306a36Sopenharmony_ci * struct rtas_work_area - RTAS work area descriptor.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * Descriptor for a "work area" in PAPR terminology that satisfies
1562306a36Sopenharmony_ci * RTAS addressing requirements.
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_cistruct rtas_work_area {
1862306a36Sopenharmony_ci	/* private: Use the APIs provided below. */
1962306a36Sopenharmony_ci	char *buf;
2062306a36Sopenharmony_ci	size_t size;
2162306a36Sopenharmony_ci};
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cienum {
2462306a36Sopenharmony_ci	/* Maximum allocation size, enforced at build time. */
2562306a36Sopenharmony_ci	RTAS_WORK_AREA_MAX_ALLOC_SZ = SZ_128K,
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/**
2962306a36Sopenharmony_ci * rtas_work_area_alloc() - Acquire a work area of the requested size.
3062306a36Sopenharmony_ci * @size_: Allocation size. Must be compile-time constant and not more
3162306a36Sopenharmony_ci *         than %RTAS_WORK_AREA_MAX_ALLOC_SZ.
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * Allocate a buffer suitable for passing to RTAS functions that have
3462306a36Sopenharmony_ci * a memory address parameter, often (but not always) referred to as a
3562306a36Sopenharmony_ci * "work area" in PAPR. Although callers are allowed to block while
3662306a36Sopenharmony_ci * holding a work area, the amount of memory reserved for this purpose
3762306a36Sopenharmony_ci * is limited, and allocations should be short-lived. A good guideline
3862306a36Sopenharmony_ci * is to release any allocated work area before returning from a
3962306a36Sopenharmony_ci * system call.
4062306a36Sopenharmony_ci *
4162306a36Sopenharmony_ci * This function does not fail. It blocks until the allocation
4262306a36Sopenharmony_ci * succeeds. To prevent deadlocks, callers are discouraged from
4362306a36Sopenharmony_ci * allocating more than one work area simultaneously in a single task
4462306a36Sopenharmony_ci * context.
4562306a36Sopenharmony_ci *
4662306a36Sopenharmony_ci * Context: This function may sleep.
4762306a36Sopenharmony_ci * Return: A &struct rtas_work_area descriptor for the allocated work area.
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_ci#define rtas_work_area_alloc(size_) ({				\
5062306a36Sopenharmony_ci	static_assert(__builtin_constant_p(size_));		\
5162306a36Sopenharmony_ci	static_assert((size_) > 0);				\
5262306a36Sopenharmony_ci	static_assert((size_) <= RTAS_WORK_AREA_MAX_ALLOC_SZ);	\
5362306a36Sopenharmony_ci	__rtas_work_area_alloc(size_);				\
5462306a36Sopenharmony_ci})
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/*
5762306a36Sopenharmony_ci * Do not call __rtas_work_area_alloc() directly. Use
5862306a36Sopenharmony_ci * rtas_work_area_alloc().
5962306a36Sopenharmony_ci */
6062306a36Sopenharmony_cistruct rtas_work_area *__rtas_work_area_alloc(size_t size);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/**
6362306a36Sopenharmony_ci * rtas_work_area_free() - Release a work area.
6462306a36Sopenharmony_ci * @area: Work area descriptor as returned from rtas_work_area_alloc().
6562306a36Sopenharmony_ci *
6662306a36Sopenharmony_ci * Return a work area buffer to the pool.
6762306a36Sopenharmony_ci */
6862306a36Sopenharmony_civoid rtas_work_area_free(struct rtas_work_area *area);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic inline char *rtas_work_area_raw_buf(const struct rtas_work_area *area)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	return area->buf;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic inline size_t rtas_work_area_size(const struct rtas_work_area *area)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	return area->size;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic inline phys_addr_t rtas_work_area_phys(const struct rtas_work_area *area)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	return __pa(area->buf);
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/*
8662306a36Sopenharmony_ci * Early setup for the work area allocator. Call from
8762306a36Sopenharmony_ci * rtas_initialize() only.
8862306a36Sopenharmony_ci */
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci#ifdef CONFIG_PPC_PSERIES
9162306a36Sopenharmony_civoid rtas_work_area_reserve_arena(phys_addr_t limit);
9262306a36Sopenharmony_ci#else /* CONFIG_PPC_PSERIES */
9362306a36Sopenharmony_cistatic inline void rtas_work_area_reserve_arena(phys_addr_t limit) {}
9462306a36Sopenharmony_ci#endif /* CONFIG_PPC_PSERIES */
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci#endif /* _ASM_POWERPC_RTAS_WORK_AREA_H */
97