/**************************************************************************** * drivers/clk/clk_rpmsg.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define CLK_RPMSG_EPT_NAME "rpmsg-clk" #define CLK_RPMSG_ENABLE 0 #define CLK_RPMSG_DISABLE 1 #define CLK_RPMSG_SETRATE 2 #define CLK_RPMSG_SETPHASE 3 #define CLK_RPMSG_GETPHASE 4 #define CLK_RPMSG_GETRATE 5 #define CLK_RPMSG_ROUNDRATE 6 #define CLK_RPMSG_ISENABLED 7 /**************************************************************************** * Private Types ****************************************************************************/ struct clk_rpmsg_client_s { struct rpmsg_endpoint ept; struct list_node node; FAR const char *cpuname; sem_t sem; }; struct clk_rpmsg_server_s { struct rpmsg_endpoint ept; struct list_node clk_list; }; struct clk_rpmsg_s { FAR struct clk_s *clk; bool enable; struct list_node node; }; struct clk_rpmsg_cookie_s { sem_t sem; int64_t result; }; begin_packed_struct struct clk_rpmsg_header_s { uint32_t command; uint32_t response; int64_t result; uint64_t cookie; } end_packed_struct; begin_packed_struct struct clk_rpmsg_enable_s { struct clk_rpmsg_header_s header; char name[0]; } end_packed_struct; #define clk_rpmsg_disable_s clk_rpmsg_enable_s #define clk_rpmsg_isenabled_s clk_rpmsg_enable_s begin_packed_struct struct clk_rpmsg_setrate_s { struct clk_rpmsg_header_s header; uint64_t rate; char name[0]; } end_packed_struct; #define clk_rpmsg_getrate_s clk_rpmsg_enable_s #define clk_rpmsg_roundrate_s clk_rpmsg_setrate_s begin_packed_struct struct clk_rpmsg_setphase_s { struct clk_rpmsg_header_s header; int32_t degrees; char name[0]; } end_packed_struct; #define clk_rpmsg_getphase_s clk_rpmsg_enable_s /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static FAR struct clk_rpmsg_client_s * clk_rpmsg_get_priv(FAR const char *name); static FAR struct rpmsg_endpoint *clk_rpmsg_get_ept(FAR const char **name); static FAR struct clk_rpmsg_s * clk_rpmsg_get_clk(FAR struct rpmsg_endpoint *ept, FAR const char *name); static int clk_rpmsg_enable_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int clk_rpmsg_disable_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int clk_rpmsg_getrate_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int clk_rpmsg_roundrate_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int clk_rpmsg_setrate_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int clk_rpmsg_setphase_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int clk_rpmsg_getphase_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int clk_rpmsg_isenabled_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static void clk_rpmsg_client_created(FAR struct rpmsg_device *rdev, FAR void *priv_); static void clk_rpmsg_client_destroy(FAR struct rpmsg_device *rdev, FAR void *priv_); static bool clk_rpmsg_server_match(FAR struct rpmsg_device *rdev, FAR void *priv_, FAR const char *name, uint32_t dest); static void clk_rpmsg_server_bind(FAR struct rpmsg_device *rdev, FAR void *priv_, FAR const char *name, uint32_t dest); static int clk_rpmsg_ept_cb(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int64_t clk_rpmsg_sendrecv(FAR struct rpmsg_endpoint *ept, uint32_t command, FAR struct clk_rpmsg_header_s *msg, int32_t len); static int clk_rpmsg_enable(FAR struct clk_s *clk); static void clk_rpmsg_disable(FAR struct clk_s *clk); static int clk_rpmsg_is_enabled(FAR struct clk_s *clk); static uint32_t clk_rpmsg_round_rate(FAR struct clk_s *clk, uint32_t rate, FAR uint32_t *parent_rate); static int clk_rpmsg_set_rate(FAR struct clk_s *clk, uint32_t rate, uint32_t parent_rate); static uint32_t clk_rpmsg_recalc_rate(FAR struct clk_s *clk, uint32_t parent_rate); static int clk_rpmsg_get_phase(FAR struct clk_s *clk); static int clk_rpmsg_set_phase(FAR struct clk_s *clk, int degrees); /**************************************************************************** * Private Datas ****************************************************************************/ static mutex_t g_clk_rpmsg_lock = NXMUTEX_INITIALIZER; static struct list_node g_clk_rpmsg_priv = LIST_INITIAL_VALUE(g_clk_rpmsg_priv); static const rpmsg_ept_cb g_clk_rpmsg_handler[] = { [CLK_RPMSG_ENABLE] = clk_rpmsg_enable_handler, [CLK_RPMSG_DISABLE] = clk_rpmsg_disable_handler, [CLK_RPMSG_SETRATE] = clk_rpmsg_setrate_handler, [CLK_RPMSG_SETPHASE] = clk_rpmsg_setphase_handler, [CLK_RPMSG_GETPHASE] = clk_rpmsg_getphase_handler, [CLK_RPMSG_GETRATE] = clk_rpmsg_getrate_handler, [CLK_RPMSG_ROUNDRATE] = clk_rpmsg_roundrate_handler, [CLK_RPMSG_ISENABLED] = clk_rpmsg_isenabled_handler, }; /**************************************************************************** * Private Functions ****************************************************************************/ static FAR struct clk_rpmsg_client_s * clk_rpmsg_get_priv(FAR const char *name) { FAR struct clk_rpmsg_client_s *priv; FAR const char *slash = strchr(name, '/'); if (!slash) { return NULL; } nxmutex_lock(&g_clk_rpmsg_lock); list_for_every_entry(&g_clk_rpmsg_priv, priv, struct clk_rpmsg_client_s, node) { if (!strncmp(priv->cpuname, name, slash - name)) { goto out; } } priv = kmm_zalloc(sizeof(struct clk_rpmsg_client_s)); if (!priv) { goto out; } priv->cpuname = strndup(name, slash - name); list_add_head(&g_clk_rpmsg_priv, &priv->node); nxmutex_unlock(&g_clk_rpmsg_lock); rpmsg_register_callback(priv, clk_rpmsg_client_created, clk_rpmsg_client_destroy, NULL, NULL); return priv; out: nxmutex_unlock(&g_clk_rpmsg_lock); return priv; } static FAR struct rpmsg_endpoint *clk_rpmsg_get_ept(FAR const char **name) { FAR struct clk_rpmsg_client_s *priv; priv = clk_rpmsg_get_priv(*name); if (priv == NULL) { return NULL; } *name += strlen(priv->cpuname) + 1; return &priv->ept; } static FAR struct clk_rpmsg_s * clk_rpmsg_get_clk(FAR struct rpmsg_endpoint *ept, FAR const char *name) { FAR struct clk_rpmsg_server_s *priv = ept->priv; FAR struct list_node *clk_list = &priv->clk_list; FAR struct clk_rpmsg_s *clkrp; list_for_every_entry(clk_list, clkrp, struct clk_rpmsg_s, node) { if (!strcmp(clk_get_name(clkrp->clk), name)) { return clkrp; } } clkrp = kmm_zalloc(sizeof(*clkrp)); if (!clkrp) { return NULL; } clkrp->clk = clk_get(name); if (!clkrp->clk) { kmm_free(clkrp); return NULL; } list_add_head(clk_list, &clkrp->node); return clkrp; } static int clk_rpmsg_enable_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct clk_rpmsg_enable_s *msg = data; FAR struct clk_rpmsg_s *clkrp = clk_rpmsg_get_clk(ept, msg->name); int ret = -ENOENT; if (clkrp && !clkrp->enable) { ret = clk_enable(clkrp->clk); if (ret >= 0) { clkrp->enable = true; } } else if (clkrp && clkrp->enable) { ret = 0; } msg->header.result = ret; return rpmsg_send(ept, msg, sizeof(*msg)); } static int clk_rpmsg_disable_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct clk_rpmsg_disable_s *msg = data; FAR struct clk_rpmsg_s *clkrp = clk_rpmsg_get_clk(ept, msg->name); int ret = -ENOENT; if (clkrp && clkrp->enable) { ret = clk_disable(clkrp->clk); if (ret >= 0) { clkrp->enable = false; } } else if (clkrp && !clkrp->enable) { ret = 0; } msg->header.result = ret; return rpmsg_send(ept, msg, sizeof(*msg)); } static int clk_rpmsg_getrate_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct clk_rpmsg_getrate_s *msg = data; FAR struct clk_rpmsg_s *clkrp = clk_rpmsg_get_clk(ept, msg->name); if (clkrp) { msg->header.result = clk_get_rate(clkrp->clk); } else { msg->header.result = -ENOENT; } return rpmsg_send(ept, msg, sizeof(*msg)); } static int clk_rpmsg_roundrate_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct clk_rpmsg_roundrate_s *msg = data; FAR struct clk_rpmsg_s *clkrp = clk_rpmsg_get_clk(ept, msg->name); if (clkrp) { msg->header.result = clk_round_rate(clkrp->clk, msg->rate); } else { msg->header.result = -ENOENT; } return rpmsg_send(ept, msg, sizeof(*msg)); } static int clk_rpmsg_setrate_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct clk_rpmsg_setrate_s *msg = data; FAR struct clk_rpmsg_s *clkrp = clk_rpmsg_get_clk(ept, msg->name); if (clkrp) { msg->header.result = clk_set_rate(clkrp->clk, msg->rate); } else { msg->header.result = -ENOENT; } return rpmsg_send(ept, msg, sizeof(*msg)); } static int clk_rpmsg_setphase_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct clk_rpmsg_setphase_s *msg = data; FAR struct clk_rpmsg_s *clkrp = clk_rpmsg_get_clk(ept, msg->name); if (clkrp) { msg->header.result = clk_set_phase(clkrp->clk, msg->degrees); } else { msg->header.result = -ENOENT; } return rpmsg_send(ept, msg, sizeof(*msg)); } static int clk_rpmsg_getphase_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct clk_rpmsg_getphase_s *msg = data; FAR struct clk_rpmsg_s *clkrp = clk_rpmsg_get_clk(ept, msg->name); if (clkrp) { msg->header.result = clk_get_phase(clkrp->clk); } else { msg->header.result = -ENOENT; } return rpmsg_send(ept, msg, sizeof(*msg)); } static int clk_rpmsg_isenabled_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct clk_rpmsg_isenabled_s *msg = data; FAR struct clk_rpmsg_s *clkrp = clk_rpmsg_get_clk(ept, msg->name); if (clkrp) { msg->header.result = clkrp->enable; } else { msg->header.result = -ENOENT; } return rpmsg_send(ept, msg, sizeof(*msg)); } static int64_t clk_rpmsg_sendrecv(FAR struct rpmsg_endpoint *ept, uint32_t command, FAR struct clk_rpmsg_header_s *msg, int32_t len) { struct clk_rpmsg_cookie_s cookie; int ret; msg->command = command; msg->response = 0; msg->cookie = (uintptr_t)&cookie; nxsem_init(&cookie.sem, 0, 0); cookie.result = -EIO; ret = rpmsg_send_nocopy(ept, msg, len); if (ret >= 0) { ret = nxsem_wait_uninterruptible(&cookie.sem); if (ret >= 0) { ret = cookie.result; } } else { rpmsg_release_tx_buffer(ept, msg); } nxsem_destroy(&cookie.sem); return ret; } static bool clk_rpmsg_server_match(FAR struct rpmsg_device *rdev, FAR void *priv_, FAR const char *name, uint32_t dest) { return !strcmp(name, CLK_RPMSG_EPT_NAME); } static void clk_rpmsg_server_ept_release(FAR struct rpmsg_endpoint *ept) { FAR struct clk_rpmsg_server_s *priv = ept->priv; FAR struct clk_rpmsg_s *clkrp_tmp; FAR struct clk_rpmsg_s *clkrp; list_for_every_entry_safe(&priv->clk_list, clkrp, clkrp_tmp, struct clk_rpmsg_s, node) { if (clkrp->enable) { clk_disable(clkrp->clk); } list_delete(&clkrp->node); kmm_free(clkrp); } kmm_free(priv); } static void clk_rpmsg_server_bind(FAR struct rpmsg_device *rdev, FAR void *priv_, FAR const char *name, uint32_t dest) { FAR struct clk_rpmsg_server_s *priv; priv = kmm_zalloc(sizeof(struct clk_rpmsg_server_s)); if (!priv) { return; } priv->ept.priv = priv; priv->ept.release_cb = clk_rpmsg_server_ept_release; list_initialize(&priv->clk_list); rpmsg_create_ept(&priv->ept, rdev, name, RPMSG_ADDR_ANY, RPMSG_ADDR_ANY, clk_rpmsg_ept_cb, rpmsg_destroy_ept); } static void clk_rpmsg_client_created(FAR struct rpmsg_device *rdev, FAR void *priv_) { struct clk_rpmsg_client_s *priv = priv_; if (!priv) { return; } if (!strcmp(priv->cpuname, rpmsg_get_cpuname(rdev))) { priv->ept.priv = priv; rpmsg_create_ept(&priv->ept, rdev, CLK_RPMSG_EPT_NAME, RPMSG_ADDR_ANY, RPMSG_ADDR_ANY, clk_rpmsg_ept_cb, NULL); nxsem_post(&priv->sem); } } static void clk_rpmsg_client_destroy(FAR struct rpmsg_device *rdev, FAR void *priv_) { struct clk_rpmsg_client_s *priv = priv_; if (!priv) { return; } nxsem_wait(&priv->sem); rpmsg_destroy_ept(&priv->ept); } static int clk_rpmsg_ept_cb(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct clk_rpmsg_header_s *hdr = data; uint32_t cmd = hdr->command; int ret = -EINVAL; if (hdr->response) { FAR struct clk_rpmsg_cookie_s *cookie = (struct clk_rpmsg_cookie_s *)(uintptr_t)hdr->cookie; if (cookie) { cookie->result = hdr->result; nxsem_post(&cookie->sem); ret = 0; } } else if (cmd < nitems(g_clk_rpmsg_handler) && g_clk_rpmsg_handler[cmd]) { hdr->response = 1; ret = g_clk_rpmsg_handler[cmd](ept, data, len, src, priv); } return ret; } static int clk_rpmsg_enable(FAR struct clk_s *clk) { FAR struct rpmsg_endpoint *ept; FAR struct clk_rpmsg_enable_s *msg; FAR const char *name = clk->name; uint32_t size; uint32_t len; if (up_interrupt_context() || sched_idletask()) { return -EPERM; } ept = clk_rpmsg_get_ept(&name); if (!ept) { return -ENODEV; } len = sizeof(*msg) + strlen(name) + 1; msg = rpmsg_get_tx_payload_buffer(ept, &size, true); if (!msg) { return -ENOMEM; } DEBUGASSERT(len <= size); strlcpy(msg->name, name, size - sizeof(*msg)); return clk_rpmsg_sendrecv(ept, CLK_RPMSG_ENABLE, (struct clk_rpmsg_header_s *)msg, len); } static void clk_rpmsg_disable(FAR struct clk_s *clk) { FAR struct rpmsg_endpoint *ept; FAR struct clk_rpmsg_disable_s *msg; FAR const char *name = clk->name; uint32_t size; uint32_t len; if (up_interrupt_context() || sched_idletask()) { return; } ept = clk_rpmsg_get_ept(&name); if (!ept) { return; } len = sizeof(*msg) + strlen(name) + 1; msg = rpmsg_get_tx_payload_buffer(ept, &size, true); if (!msg) { return; } DEBUGASSERT(len <= size); strlcpy(msg->name, name, size - sizeof(*msg)); clk_rpmsg_sendrecv(ept, CLK_RPMSG_DISABLE, (struct clk_rpmsg_header_s *)msg, len); } static int clk_rpmsg_is_enabled(FAR struct clk_s *clk) { FAR struct rpmsg_endpoint *ept; FAR struct clk_rpmsg_enable_s *msg; FAR const char *name = clk->name; uint32_t size; uint32_t len; if (up_interrupt_context() || sched_idletask()) { return -EPERM; } ept = clk_rpmsg_get_ept(&name); if (!ept) { return -ENODEV; } len = sizeof(*msg) + strlen(name) + 1; msg = rpmsg_get_tx_payload_buffer(ept, &size, true); if (!msg) { return -ENOMEM; } DEBUGASSERT(len <= size); strlcpy(msg->name, name, size - sizeof(*msg)); return clk_rpmsg_sendrecv(ept, CLK_RPMSG_ISENABLED, (struct clk_rpmsg_header_s *)msg, len); } static uint32_t clk_rpmsg_round_rate(FAR struct clk_s *clk, uint32_t rate, FAR uint32_t *parent_rate) { FAR struct rpmsg_endpoint *ept; FAR struct clk_rpmsg_roundrate_s *msg; FAR const char *name = clk->name; uint32_t size; uint32_t len; int64_t ret; ept = clk_rpmsg_get_ept(&name); if (!ept) { return 0; } len = sizeof(*msg) + strlen(name) + 1; msg = rpmsg_get_tx_payload_buffer(ept, &size, true); if (!msg) { return 0; } DEBUGASSERT(len <= size); msg->rate = rate; strlcpy(msg->name, name, size - sizeof(*msg)); ret = clk_rpmsg_sendrecv(ept, CLK_RPMSG_ROUNDRATE, (struct clk_rpmsg_header_s *)msg, len); if (ret < 0) { return 0; } return ret; } static int clk_rpmsg_set_rate(FAR struct clk_s *clk, uint32_t rate, uint32_t parent_rate) { FAR struct rpmsg_endpoint *ept; FAR struct clk_rpmsg_setrate_s *msg; FAR const char *name = clk->name; uint32_t size; uint32_t len; ept = clk_rpmsg_get_ept(&name); if (!ept) { return -ENODEV; } len = sizeof(*msg) + strlen(name) + 1; msg = rpmsg_get_tx_payload_buffer(ept, &size, true); if (!msg) { return -ENOMEM; } DEBUGASSERT(len <= size); msg->rate = rate; strlcpy(msg->name, name, size - sizeof(*msg)); return clk_rpmsg_sendrecv(ept, CLK_RPMSG_SETRATE, (struct clk_rpmsg_header_s *)msg, len); } static uint32_t clk_rpmsg_recalc_rate(FAR struct clk_s *clk, uint32_t parent_rate) { FAR struct rpmsg_endpoint *ept; FAR struct clk_rpmsg_getrate_s *msg; FAR const char *name = clk->name; uint32_t size; uint32_t len; int64_t ret; ept = clk_rpmsg_get_ept(&name); if (!ept) { return 0; } len = sizeof(*msg) + strlen(name) + 1; msg = rpmsg_get_tx_payload_buffer(ept, &size, true); if (!msg) { return 0; } DEBUGASSERT(len <= size); strlcpy(msg->name, name, size - sizeof(*msg)); ret = clk_rpmsg_sendrecv(ept, CLK_RPMSG_GETRATE, (struct clk_rpmsg_header_s *)msg, len); if (ret < 0) { return 0; } return ret; } static int clk_rpmsg_get_phase(FAR struct clk_s *clk) { FAR struct rpmsg_endpoint *ept; FAR struct clk_rpmsg_getphase_s *msg; FAR const char *name = clk->name; uint32_t size; uint32_t len; ept = clk_rpmsg_get_ept(&name); if (!ept) { return -ENODEV; } len = sizeof(*msg) + strlen(name) + 1; msg = rpmsg_get_tx_payload_buffer(ept, &size, true); if (!msg) { return -ENOMEM; } DEBUGASSERT(len <= size); strlcpy(msg->name, name, size - sizeof(*msg)); return clk_rpmsg_sendrecv(ept, CLK_RPMSG_GETPHASE, (struct clk_rpmsg_header_s *)msg, len); } static int clk_rpmsg_set_phase(FAR struct clk_s *clk, int degrees) { FAR struct rpmsg_endpoint *ept; FAR struct clk_rpmsg_setphase_s *msg; FAR const char *name = clk->name; uint32_t size; uint32_t len; ept = clk_rpmsg_get_ept(&name); if (!ept) { return -ENODEV; } len = sizeof(*msg) + strlen(name) + 1; msg = rpmsg_get_tx_payload_buffer(ept, &size, true); if (!msg) { return -ENOMEM; } DEBUGASSERT(len <= size); msg->degrees = degrees; strlcpy(msg->name, name, size - sizeof(*msg)); return clk_rpmsg_sendrecv(ept, CLK_RPMSG_SETPHASE, (struct clk_rpmsg_header_s *)msg, len); } /**************************************************************************** * Public Data ****************************************************************************/ const struct clk_ops_s g_clk_rpmsg_ops = { .enable = clk_rpmsg_enable, .disable = clk_rpmsg_disable, .is_enabled = clk_rpmsg_is_enabled, .recalc_rate = clk_rpmsg_recalc_rate, .round_rate = clk_rpmsg_round_rate, .set_rate = clk_rpmsg_set_rate, .set_phase = clk_rpmsg_set_phase, .get_phase = clk_rpmsg_get_phase, }; /**************************************************************************** * Public Functions ****************************************************************************/ FAR struct clk_s *clk_register_rpmsg(FAR const char *name, uint8_t flags) { if (strchr(name, '/') == NULL) { return NULL; } return clk_register(name, NULL, 0, flags, &g_clk_rpmsg_ops, NULL, 0); } int clk_rpmsg_server_initialize(void) { return rpmsg_register_callback(NULL, NULL, NULL, clk_rpmsg_server_match, clk_rpmsg_server_bind); }