// SPDX-License-Identifier: GPL-2.0 /* * avm-prom config mrpc client * * Copyright 2017, 2019 AVM GmbH */ #define pr_fmt(fmt) "avmcfg_client: " fmt #include #include #define AVMCFG_TIMEOUT 60000 /* * Implement the avm-prom config ops by mapping them to mrpc client calls. */ struct avm_prom_mrpc_device { struct avm_prom_config_device prom_dev; struct mrpc_client *client; }; #define to_client(prom_dev) (((struct avm_prom_mrpc_device *)(prom_dev))->client) static int mrpc_avmcfg_client_getloc(struct mrpc_client *client, struct avm_prom_config_loc *loc) { struct avmcfg_probe_reply reply; int ret, errcode; ret = mrpc_call(client, MRPC_AVMCFG_PROC_GETLOC, NULL, 0, &reply, sizeof(reply), AVMCFG_TIMEOUT, &errcode); /* Normalize positive-mrpc internal error codes */ if (ret > 0) return -EIO; if (ret) return ret; ret = be32_to_cpu(reply.result); if (ret) return ret; avm_prom_config_rloc2loc(&reply.rloc, loc); return 0; } static ssize_t mrpc_avmcfg_client_raw_read(struct avm_prom_config_device *prom_dev, loff_t offset, void *buff, size_t len) { struct mrpc_client *client = to_client(prom_dev); struct avmcfg_read_request req; struct avmcfg_read_reply reply; int ret, errcode; u32 len_rcvd; if (offset > U32_MAX || len > U32_MAX) return -EOVERFLOW; len = min(len, sizeof(reply.data)); req.offset = cpu_to_be32(offset); req.len = cpu_to_be32(len); ret = mrpc_call(client, MRPC_AVMCFG_PROC_RAW_READ, &req, sizeof(req), &reply, sizeof(reply), AVMCFG_TIMEOUT, &errcode); /* Normalize positive mrpc-internal error codes */ if (ret > 0) return -EIO; if (ret) return ret; len_rcvd = be32_to_cpu(reply.result); if (len_rcvd > len) { pr_err("%s: Long read: %u > %zu\n", __func__, (unsigned int)len_rcvd, len); return -EIO; } memcpy(buff, reply.data, len_rcvd); return len_rcvd; } static const struct avm_prom_config_device_ops mrpc_avmcfg_client_ops = { .read = mrpc_avmcfg_client_raw_read }; static struct avm_prom_mrpc_device avm_prom_mrpc_device = { .prom_dev.ops = &mrpc_avmcfg_client_ops, }; static int __init avmcfg_client_init(void) { struct avm_prom_mrpc_device *prom_mrpc = &avm_prom_mrpc_device; struct mrpc_client *client; int ret; client = mrpc_client_register(MRPC_RESERVED_ID_AVMCFG, "AVM configuration client"); if (!client) return -ENODEV; prom_mrpc->client = client; ret = mrpc_avmcfg_client_getloc(client, &prom_mrpc->prom_dev.loc); if (ret) { pr_err("Error retrieving avm prom-config location data: %d\n", ret); goto err_unregister_client; } ret = avm_prom_config_add_device(&prom_mrpc->prom_dev); if (ret) { pr_err("Error registering avm-prom device: %d\n", ret); goto err_unregister_client; } ret = avm_prom_load_config_entries(); if (ret) pr_err("Error loading config entries: %d\n", ret); err_unregister_client: if (ret) mrpc_client_unregister(client); return ret; } device_initcall(avmcfg_client_init);