16e9aec54SHaren Myneni // SPDX-License-Identifier: GPL-2.0-only
26e9aec54SHaren Myneni
36e9aec54SHaren Myneni #define pr_fmt(fmt) "papr-indices: " fmt
46e9aec54SHaren Myneni
56e9aec54SHaren Myneni #include <linux/build_bug.h>
66e9aec54SHaren Myneni #include <linux/file.h>
76e9aec54SHaren Myneni #include <linux/fs.h>
86e9aec54SHaren Myneni #include <linux/init.h>
96e9aec54SHaren Myneni #include <linux/lockdep.h>
106e9aec54SHaren Myneni #include <linux/kernel.h>
116e9aec54SHaren Myneni #include <linux/miscdevice.h>
126e9aec54SHaren Myneni #include <linux/signal.h>
136e9aec54SHaren Myneni #include <linux/slab.h>
146e9aec54SHaren Myneni #include <linux/string.h>
156e9aec54SHaren Myneni #include <linux/string_helpers.h>
166e9aec54SHaren Myneni #include <linux/uaccess.h>
176e9aec54SHaren Myneni #include <asm/machdep.h>
186e9aec54SHaren Myneni #include <asm/rtas-work-area.h>
196e9aec54SHaren Myneni #include <asm/rtas.h>
206e9aec54SHaren Myneni #include <uapi/asm/papr-indices.h>
216e9aec54SHaren Myneni #include "papr-rtas-common.h"
226e9aec54SHaren Myneni
23f60a3796SHaren Myneni /*
24f60a3796SHaren Myneni * Function-specific return values for ibm,set-dynamic-indicator and
25f60a3796SHaren Myneni * ibm,get-dynamic-sensor-state RTAS calls.
26f60a3796SHaren Myneni * PAPR+ v2.13 7.3.18 and 7.3.19.
27f60a3796SHaren Myneni */
28f60a3796SHaren Myneni #define RTAS_IBM_DYNAMIC_INDICE_NO_INDICATOR -3
29f60a3796SHaren Myneni
306e9aec54SHaren Myneni /**
316e9aec54SHaren Myneni * struct rtas_get_indices_params - Parameters (in and out) for
326e9aec54SHaren Myneni * ibm,get-indices.
336e9aec54SHaren Myneni * @is_sensor: In: Caller-provided whether sensor or indicator.
346e9aec54SHaren Myneni * @indice_type:In: Caller-provided indice (sensor or indicator) token
356e9aec54SHaren Myneni * @work_area: In: Caller-provided work area buffer for results.
366e9aec54SHaren Myneni * @next: In: Sequence number. Out: Next sequence number.
376e9aec54SHaren Myneni * @status: Out: RTAS call status.
386e9aec54SHaren Myneni */
396e9aec54SHaren Myneni struct rtas_get_indices_params {
406e9aec54SHaren Myneni u8 is_sensor;
416e9aec54SHaren Myneni u32 indice_type;
426e9aec54SHaren Myneni struct rtas_work_area *work_area;
436e9aec54SHaren Myneni u32 next;
446e9aec54SHaren Myneni s32 status;
456e9aec54SHaren Myneni };
466e9aec54SHaren Myneni
476e9aec54SHaren Myneni /*
486e9aec54SHaren Myneni * rtas_ibm_get_indices() - Call ibm,get-indices to fill a work area buffer.
496e9aec54SHaren Myneni * @params: See &struct rtas_ibm_get_indices_params.
506e9aec54SHaren Myneni *
516e9aec54SHaren Myneni * Calls ibm,get-indices until it errors or successfully deposits data
526e9aec54SHaren Myneni * into the supplied work area. Handles RTAS retry statuses. Maps RTAS
536e9aec54SHaren Myneni * error statuses to reasonable errno values.
546e9aec54SHaren Myneni *
556e9aec54SHaren Myneni * The caller is expected to invoke rtas_ibm_get_indices() multiple times
566e9aec54SHaren Myneni * to retrieve all indices data for the provided indice type. Only one
576e9aec54SHaren Myneni * sequence should be in progress at any time; starting a new sequence
586e9aec54SHaren Myneni * will disrupt any sequence already in progress. Serialization of
596e9aec54SHaren Myneni * indices retrieval sequences is the responsibility of the caller.
606e9aec54SHaren Myneni *
616e9aec54SHaren Myneni * The caller should inspect @params.status to determine whether more
626e9aec54SHaren Myneni * calls are needed to complete the sequence.
636e9aec54SHaren Myneni *
646e9aec54SHaren Myneni * Context: May sleep.
656e9aec54SHaren Myneni * Return: -ve on error, 0 otherwise.
666e9aec54SHaren Myneni */
rtas_ibm_get_indices(struct rtas_get_indices_params * params)676e9aec54SHaren Myneni static int rtas_ibm_get_indices(struct rtas_get_indices_params *params)
686e9aec54SHaren Myneni {
696e9aec54SHaren Myneni struct rtas_work_area *work_area = params->work_area;
706e9aec54SHaren Myneni const s32 token = rtas_function_token(RTAS_FN_IBM_GET_INDICES);
716e9aec54SHaren Myneni u32 rets;
726e9aec54SHaren Myneni s32 fwrc;
736e9aec54SHaren Myneni int ret;
746e9aec54SHaren Myneni
756e9aec54SHaren Myneni if (token == RTAS_UNKNOWN_SERVICE)
766e9aec54SHaren Myneni return -ENOENT;
776e9aec54SHaren Myneni
786e9aec54SHaren Myneni lockdep_assert_held(&rtas_ibm_get_indices_lock);
796e9aec54SHaren Myneni
806e9aec54SHaren Myneni do {
816e9aec54SHaren Myneni fwrc = rtas_call(token, 5, 2, &rets, params->is_sensor,
826e9aec54SHaren Myneni params->indice_type,
836e9aec54SHaren Myneni rtas_work_area_phys(work_area),
846e9aec54SHaren Myneni rtas_work_area_size(work_area),
856e9aec54SHaren Myneni params->next);
866e9aec54SHaren Myneni } while (rtas_busy_delay(fwrc));
876e9aec54SHaren Myneni
886e9aec54SHaren Myneni switch (fwrc) {
896e9aec54SHaren Myneni case RTAS_HARDWARE_ERROR:
906e9aec54SHaren Myneni ret = -EIO;
916e9aec54SHaren Myneni break;
926e9aec54SHaren Myneni case RTAS_INVALID_PARAMETER: /* Indicator type is not supported */
936e9aec54SHaren Myneni ret = -EINVAL;
946e9aec54SHaren Myneni break;
956e9aec54SHaren Myneni case RTAS_SEQ_START_OVER:
966e9aec54SHaren Myneni ret = -EAGAIN;
976e9aec54SHaren Myneni pr_info_ratelimited("Indices changed during retrieval, retrying\n");
986e9aec54SHaren Myneni params->next = 1;
996e9aec54SHaren Myneni break;
1006e9aec54SHaren Myneni case RTAS_SEQ_MORE_DATA:
1016e9aec54SHaren Myneni params->next = rets;
1026e9aec54SHaren Myneni ret = 0;
1036e9aec54SHaren Myneni break;
1046e9aec54SHaren Myneni case RTAS_SEQ_COMPLETE:
1056e9aec54SHaren Myneni params->next = 0;
1066e9aec54SHaren Myneni ret = 0;
1076e9aec54SHaren Myneni break;
1086e9aec54SHaren Myneni default:
1096e9aec54SHaren Myneni ret = -EIO;
1106e9aec54SHaren Myneni pr_err_ratelimited("unexpected ibm,get-indices status %d\n", fwrc);
1116e9aec54SHaren Myneni break;
1126e9aec54SHaren Myneni }
1136e9aec54SHaren Myneni
1146e9aec54SHaren Myneni params->status = fwrc;
1156e9aec54SHaren Myneni return ret;
1166e9aec54SHaren Myneni }
1176e9aec54SHaren Myneni
1186e9aec54SHaren Myneni /*
1196e9aec54SHaren Myneni * Internal indices sequence APIs. A sequence is a series of calls to
1206e9aec54SHaren Myneni * ibm,get-indices for a given location code. The sequence ends when
1216e9aec54SHaren Myneni * an error is encountered or all indices for the input has been
1226e9aec54SHaren Myneni * returned.
1236e9aec54SHaren Myneni */
1246e9aec54SHaren Myneni
1256e9aec54SHaren Myneni /*
1266e9aec54SHaren Myneni * indices_sequence_begin() - Begin a indices retrieval sequence.
1276e9aec54SHaren Myneni *
1286e9aec54SHaren Myneni * Context: May sleep.
1296e9aec54SHaren Myneni */
indices_sequence_begin(struct papr_rtas_sequence * seq)1306e9aec54SHaren Myneni static void indices_sequence_begin(struct papr_rtas_sequence *seq)
1316e9aec54SHaren Myneni {
1326e9aec54SHaren Myneni struct rtas_get_indices_params *param;
1336e9aec54SHaren Myneni
1346e9aec54SHaren Myneni param = (struct rtas_get_indices_params *)seq->params;
1356e9aec54SHaren Myneni /*
1366e9aec54SHaren Myneni * We could allocate the work area before acquiring the
1376e9aec54SHaren Myneni * function lock, but that would allow concurrent requests to
1386e9aec54SHaren Myneni * exhaust the limited work area pool for no benefit. So
1396e9aec54SHaren Myneni * allocate the work area under the lock.
1406e9aec54SHaren Myneni */
1416e9aec54SHaren Myneni mutex_lock(&rtas_ibm_get_indices_lock);
1426e9aec54SHaren Myneni param->work_area = rtas_work_area_alloc(RTAS_GET_INDICES_BUF_SIZE);
1436e9aec54SHaren Myneni param->next = 1;
1446e9aec54SHaren Myneni param->status = 0;
1456e9aec54SHaren Myneni }
1466e9aec54SHaren Myneni
1476e9aec54SHaren Myneni /*
1486e9aec54SHaren Myneni * indices_sequence_end() - Finalize a indices retrieval sequence.
1496e9aec54SHaren Myneni *
1506e9aec54SHaren Myneni * Releases resources obtained by indices_sequence_begin().
1516e9aec54SHaren Myneni */
indices_sequence_end(struct papr_rtas_sequence * seq)1526e9aec54SHaren Myneni static void indices_sequence_end(struct papr_rtas_sequence *seq)
1536e9aec54SHaren Myneni {
1546e9aec54SHaren Myneni struct rtas_get_indices_params *param;
1556e9aec54SHaren Myneni
1566e9aec54SHaren Myneni param = (struct rtas_get_indices_params *)seq->params;
1576e9aec54SHaren Myneni rtas_work_area_free(param->work_area);
1586e9aec54SHaren Myneni mutex_unlock(&rtas_ibm_get_indices_lock);
1596e9aec54SHaren Myneni }
1606e9aec54SHaren Myneni
1616e9aec54SHaren Myneni /*
1626e9aec54SHaren Myneni * Work function to be passed to papr_rtas_blob_generate().
1636e9aec54SHaren Myneni *
1646e9aec54SHaren Myneni * ibm,get-indices RTAS call fills the work area with the certain
1656e9aec54SHaren Myneni * format but does not return the bytes written in the buffer. So
1666e9aec54SHaren Myneni * instead of kernel parsing this work area to determine the buffer
1676e9aec54SHaren Myneni * length, copy the complete work area (RTAS_GET_INDICES_BUF_SIZE)
1686e9aec54SHaren Myneni * to the blob and let the user space to obtain the data.
1696e9aec54SHaren Myneni * Means RTAS_GET_INDICES_BUF_SIZE data will be returned for each
1706e9aec54SHaren Myneni * read().
1716e9aec54SHaren Myneni */
1726e9aec54SHaren Myneni
indices_sequence_fill_work_area(struct papr_rtas_sequence * seq,size_t * len)1736e9aec54SHaren Myneni static const char *indices_sequence_fill_work_area(struct papr_rtas_sequence *seq,
1746e9aec54SHaren Myneni size_t *len)
1756e9aec54SHaren Myneni {
1766e9aec54SHaren Myneni struct rtas_get_indices_params *p;
1776e9aec54SHaren Myneni bool init_state;
1786e9aec54SHaren Myneni
1796e9aec54SHaren Myneni p = (struct rtas_get_indices_params *)seq->params;
1806e9aec54SHaren Myneni init_state = (p->next == 1) ? true : false;
1816e9aec54SHaren Myneni
1826e9aec54SHaren Myneni if (papr_rtas_sequence_should_stop(seq, p->status, init_state))
1836e9aec54SHaren Myneni return NULL;
1846e9aec54SHaren Myneni if (papr_rtas_sequence_set_err(seq, rtas_ibm_get_indices(p)))
1856e9aec54SHaren Myneni return NULL;
1866e9aec54SHaren Myneni
1876e9aec54SHaren Myneni *len = RTAS_GET_INDICES_BUF_SIZE;
1886e9aec54SHaren Myneni return rtas_work_area_raw_buf(p->work_area);
1896e9aec54SHaren Myneni }
1906e9aec54SHaren Myneni
1916e9aec54SHaren Myneni /*
1926e9aec54SHaren Myneni * papr_indices_handle_read - returns indices blob data to the user space
1936e9aec54SHaren Myneni *
1946e9aec54SHaren Myneni * ibm,get-indices RTAS call fills the work area with the certian
1956e9aec54SHaren Myneni * format but does not return the bytes written in the buffer and
1966e9aec54SHaren Myneni * copied RTAS_GET_INDICES_BUF_SIZE data to the blob for each RTAS
1976e9aec54SHaren Myneni * call. So send RTAS_GET_INDICES_BUF_SIZE buffer to the user space
1986e9aec54SHaren Myneni * for each read().
1996e9aec54SHaren Myneni */
papr_indices_handle_read(struct file * file,char __user * buf,size_t size,loff_t * off)2006e9aec54SHaren Myneni static ssize_t papr_indices_handle_read(struct file *file,
2016e9aec54SHaren Myneni char __user *buf, size_t size, loff_t *off)
2026e9aec54SHaren Myneni {
2036e9aec54SHaren Myneni const struct papr_rtas_blob *blob = file->private_data;
2046e9aec54SHaren Myneni
2056e9aec54SHaren Myneni /* we should not instantiate a handle without any data attached. */
2066e9aec54SHaren Myneni if (!papr_rtas_blob_has_data(blob)) {
2076e9aec54SHaren Myneni pr_err_once("handle without data\n");
2086e9aec54SHaren Myneni return -EIO;
2096e9aec54SHaren Myneni }
2106e9aec54SHaren Myneni
2116e9aec54SHaren Myneni if (size < RTAS_GET_INDICES_BUF_SIZE) {
2126e9aec54SHaren Myneni pr_err_once("Invalid buffer length %ld, expect %d\n",
2136e9aec54SHaren Myneni size, RTAS_GET_INDICES_BUF_SIZE);
2146e9aec54SHaren Myneni return -EINVAL;
2156e9aec54SHaren Myneni } else if (size > RTAS_GET_INDICES_BUF_SIZE)
2166e9aec54SHaren Myneni size = RTAS_GET_INDICES_BUF_SIZE;
2176e9aec54SHaren Myneni
2186e9aec54SHaren Myneni return simple_read_from_buffer(buf, size, off, blob->data, blob->len);
2196e9aec54SHaren Myneni }
2206e9aec54SHaren Myneni
2216e9aec54SHaren Myneni static const struct file_operations papr_indices_handle_ops = {
2226e9aec54SHaren Myneni .read = papr_indices_handle_read,
2236e9aec54SHaren Myneni .llseek = papr_rtas_common_handle_seek,
2246e9aec54SHaren Myneni .release = papr_rtas_common_handle_release,
2256e9aec54SHaren Myneni };
2266e9aec54SHaren Myneni
2276e9aec54SHaren Myneni /*
2286e9aec54SHaren Myneni * papr_indices_create_handle() - Create a fd-based handle for reading
2296e9aec54SHaren Myneni * indices data
2306e9aec54SHaren Myneni * @ubuf: Input parameters to RTAS call such as whether sensor or indicator
2316e9aec54SHaren Myneni * and indice type in user memory
2326e9aec54SHaren Myneni *
2336e9aec54SHaren Myneni * Handler for PAPR_INDICES_IOC_GET ioctl command. Validates @ubuf
2346e9aec54SHaren Myneni * and instantiates an immutable indices "blob" for it. The blob is
2356e9aec54SHaren Myneni * attached to a file descriptor for reading by user space. The memory
2366e9aec54SHaren Myneni * backing the blob is freed when the file is released.
2376e9aec54SHaren Myneni *
2386e9aec54SHaren Myneni * The entire requested indices is retrieved by this call and all
2396e9aec54SHaren Myneni * necessary RTAS interactions are performed before returning the fd
2406e9aec54SHaren Myneni * to user space. This keeps the read handler simple and ensures that
2416e9aec54SHaren Myneni * the kernel can prevent interleaving of ibm,get-indices call sequences.
2426e9aec54SHaren Myneni *
2436e9aec54SHaren Myneni * Return: The installed fd number if successful, -ve errno otherwise.
2446e9aec54SHaren Myneni */
papr_indices_create_handle(struct papr_indices_io_block __user * ubuf)2456e9aec54SHaren Myneni static long papr_indices_create_handle(struct papr_indices_io_block __user *ubuf)
2466e9aec54SHaren Myneni {
2476e9aec54SHaren Myneni struct papr_rtas_sequence seq = {};
2486e9aec54SHaren Myneni struct rtas_get_indices_params params = {};
2496e9aec54SHaren Myneni int fd;
2506e9aec54SHaren Myneni
2516e9aec54SHaren Myneni if (get_user(params.is_sensor, &ubuf->indices.is_sensor))
2526e9aec54SHaren Myneni return -EFAULT;
2536e9aec54SHaren Myneni
2546e9aec54SHaren Myneni if (get_user(params.indice_type, &ubuf->indices.indice_type))
2556e9aec54SHaren Myneni return -EFAULT;
2566e9aec54SHaren Myneni
2576e9aec54SHaren Myneni seq = (struct papr_rtas_sequence) {
2586e9aec54SHaren Myneni .begin = indices_sequence_begin,
2596e9aec54SHaren Myneni .end = indices_sequence_end,
2606e9aec54SHaren Myneni .work = indices_sequence_fill_work_area,
2616e9aec54SHaren Myneni };
2626e9aec54SHaren Myneni
2636e9aec54SHaren Myneni seq.params = ¶ms;
2646e9aec54SHaren Myneni fd = papr_rtas_setup_file_interface(&seq,
2656e9aec54SHaren Myneni &papr_indices_handle_ops, "[papr-indices]");
2666e9aec54SHaren Myneni
2676e9aec54SHaren Myneni return fd;
2686e9aec54SHaren Myneni }
2696e9aec54SHaren Myneni
2706e9aec54SHaren Myneni /*
271f60a3796SHaren Myneni * Create work area with the input parameters. This function is used
272f60a3796SHaren Myneni * for both ibm,set-dynamic-indicator and ibm,get-dynamic-sensor-state
273f60a3796SHaren Myneni * RTAS Calls.
274f60a3796SHaren Myneni */
275f60a3796SHaren Myneni static struct rtas_work_area *
papr_dynamic_indice_buf_from_user(struct papr_indices_io_block __user * ubuf,struct papr_indices_io_block * kbuf)276f60a3796SHaren Myneni papr_dynamic_indice_buf_from_user(struct papr_indices_io_block __user *ubuf,
277f60a3796SHaren Myneni struct papr_indices_io_block *kbuf)
278f60a3796SHaren Myneni {
279f60a3796SHaren Myneni struct rtas_work_area *work_area;
280f60a3796SHaren Myneni u32 length;
281f60a3796SHaren Myneni __be32 len_be;
282f60a3796SHaren Myneni
283f60a3796SHaren Myneni if (copy_from_user(kbuf, ubuf, sizeof(*kbuf)))
284f60a3796SHaren Myneni return ERR_PTR(-EFAULT);
285f60a3796SHaren Myneni
286f60a3796SHaren Myneni
287f60a3796SHaren Myneni if (!string_is_terminated(kbuf->dynamic_param.location_code_str,
288f60a3796SHaren Myneni ARRAY_SIZE(kbuf->dynamic_param.location_code_str)))
289f60a3796SHaren Myneni return ERR_PTR(-EINVAL);
290f60a3796SHaren Myneni
291f60a3796SHaren Myneni /*
292f60a3796SHaren Myneni * The input data in the work area should be as follows:
293f60a3796SHaren Myneni * - 32-bit integer length of the location code string,
294f60a3796SHaren Myneni * including NULL.
295f60a3796SHaren Myneni * - Location code string, NULL terminated, identifying the
296f60a3796SHaren Myneni * token (sensor or indicator).
297f60a3796SHaren Myneni * PAPR 2.13 - R1–7.3.18–5 ibm,set-dynamic-indicator
298f60a3796SHaren Myneni * - R1–7.3.19–5 ibm,get-dynamic-sensor-state
299f60a3796SHaren Myneni */
300f60a3796SHaren Myneni /*
301f60a3796SHaren Myneni * Length that user space passed should also include NULL
302f60a3796SHaren Myneni * terminator.
303f60a3796SHaren Myneni */
304f60a3796SHaren Myneni length = strlen(kbuf->dynamic_param.location_code_str) + 1;
305f60a3796SHaren Myneni if (length > LOC_CODE_SIZE)
306f60a3796SHaren Myneni return ERR_PTR(-EINVAL);
307f60a3796SHaren Myneni
308f60a3796SHaren Myneni len_be = cpu_to_be32(length);
309f60a3796SHaren Myneni
310f60a3796SHaren Myneni work_area = rtas_work_area_alloc(LOC_CODE_SIZE + sizeof(u32));
311f60a3796SHaren Myneni memcpy(rtas_work_area_raw_buf(work_area), &len_be, sizeof(u32));
312f60a3796SHaren Myneni memcpy((rtas_work_area_raw_buf(work_area) + sizeof(u32)),
313f60a3796SHaren Myneni &kbuf->dynamic_param.location_code_str, length);
314f60a3796SHaren Myneni
315f60a3796SHaren Myneni return work_area;
316f60a3796SHaren Myneni }
317f60a3796SHaren Myneni
318f60a3796SHaren Myneni /**
319f60a3796SHaren Myneni * papr_dynamic_indicator_ioc_set - ibm,set-dynamic-indicator RTAS Call
320f60a3796SHaren Myneni * PAPR 2.13 7.3.18
321f60a3796SHaren Myneni *
322f60a3796SHaren Myneni * @ubuf: Input parameters to RTAS call such as indicator token and
323f60a3796SHaren Myneni * new state.
324f60a3796SHaren Myneni *
325f60a3796SHaren Myneni * Returns success or -errno.
326f60a3796SHaren Myneni */
papr_dynamic_indicator_ioc_set(struct papr_indices_io_block __user * ubuf)327f60a3796SHaren Myneni static long papr_dynamic_indicator_ioc_set(struct papr_indices_io_block __user *ubuf)
328f60a3796SHaren Myneni {
329f60a3796SHaren Myneni struct papr_indices_io_block kbuf;
330f60a3796SHaren Myneni struct rtas_work_area *work_area;
331f60a3796SHaren Myneni s32 fwrc, token, ret;
332f60a3796SHaren Myneni
333f60a3796SHaren Myneni token = rtas_function_token(RTAS_FN_IBM_SET_DYNAMIC_INDICATOR);
334f60a3796SHaren Myneni if (token == RTAS_UNKNOWN_SERVICE)
335f60a3796SHaren Myneni return -ENOENT;
336f60a3796SHaren Myneni
337f60a3796SHaren Myneni mutex_lock(&rtas_ibm_set_dynamic_indicator_lock);
338f60a3796SHaren Myneni work_area = papr_dynamic_indice_buf_from_user(ubuf, &kbuf);
339f60a3796SHaren Myneni if (IS_ERR(work_area)) {
340f60a3796SHaren Myneni ret = PTR_ERR(work_area);
341f60a3796SHaren Myneni goto out;
342f60a3796SHaren Myneni }
343f60a3796SHaren Myneni
344f60a3796SHaren Myneni do {
345f60a3796SHaren Myneni fwrc = rtas_call(token, 3, 1, NULL,
346f60a3796SHaren Myneni kbuf.dynamic_param.token,
347f60a3796SHaren Myneni kbuf.dynamic_param.state,
348f60a3796SHaren Myneni rtas_work_area_phys(work_area));
349f60a3796SHaren Myneni } while (rtas_busy_delay(fwrc));
350f60a3796SHaren Myneni
351f60a3796SHaren Myneni rtas_work_area_free(work_area);
352f60a3796SHaren Myneni
353f60a3796SHaren Myneni switch (fwrc) {
354f60a3796SHaren Myneni case RTAS_SUCCESS:
355f60a3796SHaren Myneni ret = 0;
356f60a3796SHaren Myneni break;
357f60a3796SHaren Myneni case RTAS_IBM_DYNAMIC_INDICE_NO_INDICATOR: /* No such indicator */
358f60a3796SHaren Myneni ret = -EOPNOTSUPP;
359f60a3796SHaren Myneni break;
360f60a3796SHaren Myneni default:
361f60a3796SHaren Myneni pr_err("unexpected ibm,set-dynamic-indicator result %d\n",
362f60a3796SHaren Myneni fwrc);
363f60a3796SHaren Myneni fallthrough;
364f60a3796SHaren Myneni case RTAS_HARDWARE_ERROR: /* Hardware/platform error */
365f60a3796SHaren Myneni ret = -EIO;
366f60a3796SHaren Myneni break;
367f60a3796SHaren Myneni }
368f60a3796SHaren Myneni
369f60a3796SHaren Myneni out:
370f60a3796SHaren Myneni mutex_unlock(&rtas_ibm_set_dynamic_indicator_lock);
371f60a3796SHaren Myneni return ret;
372f60a3796SHaren Myneni }
373f60a3796SHaren Myneni
374*496c7524SHaren Myneni /**
375*496c7524SHaren Myneni * papr_dynamic_sensor_ioc_get - ibm,get-dynamic-sensor-state RTAS Call
376*496c7524SHaren Myneni * PAPR 2.13 7.3.19
377*496c7524SHaren Myneni *
378*496c7524SHaren Myneni * @ubuf: Input parameters to RTAS call such as sensor token
379*496c7524SHaren Myneni * Copies the state in user space buffer.
380*496c7524SHaren Myneni *
381*496c7524SHaren Myneni *
382*496c7524SHaren Myneni * Returns success or -errno.
383*496c7524SHaren Myneni */
384*496c7524SHaren Myneni
papr_dynamic_sensor_ioc_get(struct papr_indices_io_block __user * ubuf)385*496c7524SHaren Myneni static long papr_dynamic_sensor_ioc_get(struct papr_indices_io_block __user *ubuf)
386*496c7524SHaren Myneni {
387*496c7524SHaren Myneni struct papr_indices_io_block kbuf;
388*496c7524SHaren Myneni struct rtas_work_area *work_area;
389*496c7524SHaren Myneni s32 fwrc, token, ret;
390*496c7524SHaren Myneni u32 rets;
391*496c7524SHaren Myneni
392*496c7524SHaren Myneni token = rtas_function_token(RTAS_FN_IBM_GET_DYNAMIC_SENSOR_STATE);
393*496c7524SHaren Myneni if (token == RTAS_UNKNOWN_SERVICE)
394*496c7524SHaren Myneni return -ENOENT;
395*496c7524SHaren Myneni
396*496c7524SHaren Myneni mutex_lock(&rtas_ibm_get_dynamic_sensor_state_lock);
397*496c7524SHaren Myneni work_area = papr_dynamic_indice_buf_from_user(ubuf, &kbuf);
398*496c7524SHaren Myneni if (IS_ERR(work_area)) {
399*496c7524SHaren Myneni ret = PTR_ERR(work_area);
400*496c7524SHaren Myneni goto out;
401*496c7524SHaren Myneni }
402*496c7524SHaren Myneni
403*496c7524SHaren Myneni do {
404*496c7524SHaren Myneni fwrc = rtas_call(token, 2, 2, &rets,
405*496c7524SHaren Myneni kbuf.dynamic_param.token,
406*496c7524SHaren Myneni rtas_work_area_phys(work_area));
407*496c7524SHaren Myneni } while (rtas_busy_delay(fwrc));
408*496c7524SHaren Myneni
409*496c7524SHaren Myneni rtas_work_area_free(work_area);
410*496c7524SHaren Myneni
411*496c7524SHaren Myneni switch (fwrc) {
412*496c7524SHaren Myneni case RTAS_SUCCESS:
413*496c7524SHaren Myneni if (put_user(rets, &ubuf->dynamic_param.state))
414*496c7524SHaren Myneni ret = -EFAULT;
415*496c7524SHaren Myneni else
416*496c7524SHaren Myneni ret = 0;
417*496c7524SHaren Myneni break;
418*496c7524SHaren Myneni case RTAS_IBM_DYNAMIC_INDICE_NO_INDICATOR: /* No such indicator */
419*496c7524SHaren Myneni ret = -EOPNOTSUPP;
420*496c7524SHaren Myneni break;
421*496c7524SHaren Myneni default:
422*496c7524SHaren Myneni pr_err("unexpected ibm,get-dynamic-sensor result %d\n",
423*496c7524SHaren Myneni fwrc);
424*496c7524SHaren Myneni fallthrough;
425*496c7524SHaren Myneni case RTAS_HARDWARE_ERROR: /* Hardware/platform error */
426*496c7524SHaren Myneni ret = -EIO;
427*496c7524SHaren Myneni break;
428*496c7524SHaren Myneni }
429*496c7524SHaren Myneni
430*496c7524SHaren Myneni out:
431*496c7524SHaren Myneni mutex_unlock(&rtas_ibm_get_dynamic_sensor_state_lock);
432*496c7524SHaren Myneni return ret;
433*496c7524SHaren Myneni }
434*496c7524SHaren Myneni
435f60a3796SHaren Myneni /*
4366e9aec54SHaren Myneni * Top-level ioctl handler for /dev/papr-indices.
4376e9aec54SHaren Myneni */
papr_indices_dev_ioctl(struct file * filp,unsigned int ioctl,unsigned long arg)4386e9aec54SHaren Myneni static long papr_indices_dev_ioctl(struct file *filp, unsigned int ioctl,
4396e9aec54SHaren Myneni unsigned long arg)
4406e9aec54SHaren Myneni {
4416e9aec54SHaren Myneni void __user *argp = (__force void __user *)arg;
4426e9aec54SHaren Myneni long ret;
4436e9aec54SHaren Myneni
4446e9aec54SHaren Myneni switch (ioctl) {
4456e9aec54SHaren Myneni case PAPR_INDICES_IOC_GET:
4466e9aec54SHaren Myneni ret = papr_indices_create_handle(argp);
4476e9aec54SHaren Myneni break;
448*496c7524SHaren Myneni case PAPR_DYNAMIC_SENSOR_IOC_GET:
449*496c7524SHaren Myneni ret = papr_dynamic_sensor_ioc_get(argp);
450*496c7524SHaren Myneni break;
451f60a3796SHaren Myneni case PAPR_DYNAMIC_INDICATOR_IOC_SET:
452f60a3796SHaren Myneni if (filp->f_mode & FMODE_WRITE)
453f60a3796SHaren Myneni ret = papr_dynamic_indicator_ioc_set(argp);
454f60a3796SHaren Myneni else
455f60a3796SHaren Myneni ret = -EBADF;
456f60a3796SHaren Myneni break;
4576e9aec54SHaren Myneni default:
4586e9aec54SHaren Myneni ret = -ENOIOCTLCMD;
4596e9aec54SHaren Myneni break;
4606e9aec54SHaren Myneni }
4616e9aec54SHaren Myneni
4626e9aec54SHaren Myneni return ret;
4636e9aec54SHaren Myneni }
4646e9aec54SHaren Myneni
4656e9aec54SHaren Myneni static const struct file_operations papr_indices_ops = {
4666e9aec54SHaren Myneni .unlocked_ioctl = papr_indices_dev_ioctl,
4676e9aec54SHaren Myneni };
4686e9aec54SHaren Myneni
4696e9aec54SHaren Myneni static struct miscdevice papr_indices_dev = {
4706e9aec54SHaren Myneni .minor = MISC_DYNAMIC_MINOR,
4716e9aec54SHaren Myneni .name = "papr-indices",
4726e9aec54SHaren Myneni .fops = &papr_indices_ops,
4736e9aec54SHaren Myneni };
4746e9aec54SHaren Myneni
papr_indices_init(void)4756e9aec54SHaren Myneni static __init int papr_indices_init(void)
4766e9aec54SHaren Myneni {
4776e9aec54SHaren Myneni if (!rtas_function_implemented(RTAS_FN_IBM_GET_INDICES))
4786e9aec54SHaren Myneni return -ENODEV;
4796e9aec54SHaren Myneni
480f60a3796SHaren Myneni if (!rtas_function_implemented(RTAS_FN_IBM_SET_DYNAMIC_INDICATOR))
481f60a3796SHaren Myneni return -ENODEV;
482f60a3796SHaren Myneni
483*496c7524SHaren Myneni if (!rtas_function_implemented(RTAS_FN_IBM_GET_DYNAMIC_SENSOR_STATE))
484*496c7524SHaren Myneni return -ENODEV;
485*496c7524SHaren Myneni
4866e9aec54SHaren Myneni return misc_register(&papr_indices_dev);
4876e9aec54SHaren Myneni }
4886e9aec54SHaren Myneni machine_device_initcall(pseries, papr_indices_init);
489