162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/atomic.h> 762306a36Sopenharmony_ci#include <linux/bug.h> 862306a36Sopenharmony_ci#include <linux/interrupt.h> 962306a36Sopenharmony_ci#include <linux/jiffies.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/list.h> 1262306a36Sopenharmony_ci#include <linux/lockdep.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/spinlock.h> 1862306a36Sopenharmony_ci#include <linux/types.h> 1962306a36Sopenharmony_ci#include <linux/wait.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <soc/qcom/rpmh.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "rpmh-internal.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define RPMH_TIMEOUT_MS msecs_to_jiffies(10000) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define DEFINE_RPMH_MSG_ONSTACK(device, s, q, name) \ 2862306a36Sopenharmony_ci struct rpmh_request name = { \ 2962306a36Sopenharmony_ci .msg = { \ 3062306a36Sopenharmony_ci .state = s, \ 3162306a36Sopenharmony_ci .cmds = name.cmd, \ 3262306a36Sopenharmony_ci .num_cmds = 0, \ 3362306a36Sopenharmony_ci .wait_for_compl = true, \ 3462306a36Sopenharmony_ci }, \ 3562306a36Sopenharmony_ci .cmd = { { 0 } }, \ 3662306a36Sopenharmony_ci .completion = q, \ 3762306a36Sopenharmony_ci .dev = device, \ 3862306a36Sopenharmony_ci .needs_free = false, \ 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define ctrlr_to_drv(ctrlr) container_of(ctrlr, struct rsc_drv, client) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/** 4462306a36Sopenharmony_ci * struct cache_req: the request object for caching 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * @addr: the address of the resource 4762306a36Sopenharmony_ci * @sleep_val: the sleep vote 4862306a36Sopenharmony_ci * @wake_val: the wake vote 4962306a36Sopenharmony_ci * @list: linked list obj 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_cistruct cache_req { 5262306a36Sopenharmony_ci u32 addr; 5362306a36Sopenharmony_ci u32 sleep_val; 5462306a36Sopenharmony_ci u32 wake_val; 5562306a36Sopenharmony_ci struct list_head list; 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/** 5962306a36Sopenharmony_ci * struct batch_cache_req - An entry in our batch catch 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * @list: linked list obj 6262306a36Sopenharmony_ci * @count: number of messages 6362306a36Sopenharmony_ci * @rpm_msgs: the messages 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistruct batch_cache_req { 6762306a36Sopenharmony_ci struct list_head list; 6862306a36Sopenharmony_ci int count; 6962306a36Sopenharmony_ci struct rpmh_request rpm_msgs[]; 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic struct rpmh_ctrlr *get_rpmh_ctrlr(const struct device *dev) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct rsc_drv *drv = dev_get_drvdata(dev->parent); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return &drv->client; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_civoid rpmh_tx_done(const struct tcs_request *msg) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct rpmh_request *rpm_msg = container_of(msg, struct rpmh_request, 8262306a36Sopenharmony_ci msg); 8362306a36Sopenharmony_ci struct completion *compl = rpm_msg->completion; 8462306a36Sopenharmony_ci bool free = rpm_msg->needs_free; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (!compl) 8762306a36Sopenharmony_ci goto exit; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* Signal the blocking thread we are done */ 9062306a36Sopenharmony_ci complete(compl); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ciexit: 9362306a36Sopenharmony_ci if (free) 9462306a36Sopenharmony_ci kfree(rpm_msg); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic struct cache_req *__find_req(struct rpmh_ctrlr *ctrlr, u32 addr) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct cache_req *p, *req = NULL; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci list_for_each_entry(p, &ctrlr->cache, list) { 10262306a36Sopenharmony_ci if (p->addr == addr) { 10362306a36Sopenharmony_ci req = p; 10462306a36Sopenharmony_ci break; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return req; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic struct cache_req *cache_rpm_request(struct rpmh_ctrlr *ctrlr, 11262306a36Sopenharmony_ci enum rpmh_state state, 11362306a36Sopenharmony_ci struct tcs_cmd *cmd) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct cache_req *req; 11662306a36Sopenharmony_ci unsigned long flags; 11762306a36Sopenharmony_ci u32 old_sleep_val, old_wake_val; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci spin_lock_irqsave(&ctrlr->cache_lock, flags); 12062306a36Sopenharmony_ci req = __find_req(ctrlr, cmd->addr); 12162306a36Sopenharmony_ci if (req) 12262306a36Sopenharmony_ci goto existing; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci req = kzalloc(sizeof(*req), GFP_ATOMIC); 12562306a36Sopenharmony_ci if (!req) { 12662306a36Sopenharmony_ci req = ERR_PTR(-ENOMEM); 12762306a36Sopenharmony_ci goto unlock; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci req->addr = cmd->addr; 13162306a36Sopenharmony_ci req->sleep_val = req->wake_val = UINT_MAX; 13262306a36Sopenharmony_ci list_add_tail(&req->list, &ctrlr->cache); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ciexisting: 13562306a36Sopenharmony_ci old_sleep_val = req->sleep_val; 13662306a36Sopenharmony_ci old_wake_val = req->wake_val; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci switch (state) { 13962306a36Sopenharmony_ci case RPMH_ACTIVE_ONLY_STATE: 14062306a36Sopenharmony_ci case RPMH_WAKE_ONLY_STATE: 14162306a36Sopenharmony_ci req->wake_val = cmd->data; 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci case RPMH_SLEEP_STATE: 14462306a36Sopenharmony_ci req->sleep_val = cmd->data; 14562306a36Sopenharmony_ci break; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci ctrlr->dirty |= (req->sleep_val != old_sleep_val || 14962306a36Sopenharmony_ci req->wake_val != old_wake_val) && 15062306a36Sopenharmony_ci req->sleep_val != UINT_MAX && 15162306a36Sopenharmony_ci req->wake_val != UINT_MAX; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ciunlock: 15462306a36Sopenharmony_ci spin_unlock_irqrestore(&ctrlr->cache_lock, flags); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return req; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/** 16062306a36Sopenharmony_ci * __rpmh_write: Cache and send the RPMH request 16162306a36Sopenharmony_ci * 16262306a36Sopenharmony_ci * @dev: The device making the request 16362306a36Sopenharmony_ci * @state: Active/Sleep request type 16462306a36Sopenharmony_ci * @rpm_msg: The data that needs to be sent (cmds). 16562306a36Sopenharmony_ci * 16662306a36Sopenharmony_ci * Cache the RPMH request and send if the state is ACTIVE_ONLY. 16762306a36Sopenharmony_ci * SLEEP/WAKE_ONLY requests are not sent to the controller at 16862306a36Sopenharmony_ci * this time. Use rpmh_flush() to send them to the controller. 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_cistatic int __rpmh_write(const struct device *dev, enum rpmh_state state, 17162306a36Sopenharmony_ci struct rpmh_request *rpm_msg) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); 17462306a36Sopenharmony_ci int ret = -EINVAL; 17562306a36Sopenharmony_ci struct cache_req *req; 17662306a36Sopenharmony_ci int i; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* Cache the request in our store and link the payload */ 17962306a36Sopenharmony_ci for (i = 0; i < rpm_msg->msg.num_cmds; i++) { 18062306a36Sopenharmony_ci req = cache_rpm_request(ctrlr, state, &rpm_msg->msg.cmds[i]); 18162306a36Sopenharmony_ci if (IS_ERR(req)) 18262306a36Sopenharmony_ci return PTR_ERR(req); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (state == RPMH_ACTIVE_ONLY_STATE) { 18662306a36Sopenharmony_ci WARN_ON(irqs_disabled()); 18762306a36Sopenharmony_ci ret = rpmh_rsc_send_data(ctrlr_to_drv(ctrlr), &rpm_msg->msg); 18862306a36Sopenharmony_ci } else { 18962306a36Sopenharmony_ci /* Clean up our call by spoofing tx_done */ 19062306a36Sopenharmony_ci ret = 0; 19162306a36Sopenharmony_ci rpmh_tx_done(&rpm_msg->msg); 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return ret; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic int __fill_rpmh_msg(struct rpmh_request *req, enum rpmh_state state, 19862306a36Sopenharmony_ci const struct tcs_cmd *cmd, u32 n) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci if (!cmd || !n || n > MAX_RPMH_PAYLOAD) 20162306a36Sopenharmony_ci return -EINVAL; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci memcpy(req->cmd, cmd, n * sizeof(*cmd)); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci req->msg.state = state; 20662306a36Sopenharmony_ci req->msg.cmds = req->cmd; 20762306a36Sopenharmony_ci req->msg.num_cmds = n; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return 0; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci/** 21362306a36Sopenharmony_ci * rpmh_write_async: Write a set of RPMH commands 21462306a36Sopenharmony_ci * 21562306a36Sopenharmony_ci * @dev: The device making the request 21662306a36Sopenharmony_ci * @state: Active/sleep set 21762306a36Sopenharmony_ci * @cmd: The payload data 21862306a36Sopenharmony_ci * @n: The number of elements in payload 21962306a36Sopenharmony_ci * 22062306a36Sopenharmony_ci * Write a set of RPMH commands, the order of commands is maintained 22162306a36Sopenharmony_ci * and will be sent as a single shot. 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_ciint rpmh_write_async(const struct device *dev, enum rpmh_state state, 22462306a36Sopenharmony_ci const struct tcs_cmd *cmd, u32 n) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct rpmh_request *rpm_msg; 22762306a36Sopenharmony_ci int ret; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci rpm_msg = kzalloc(sizeof(*rpm_msg), GFP_ATOMIC); 23062306a36Sopenharmony_ci if (!rpm_msg) 23162306a36Sopenharmony_ci return -ENOMEM; 23262306a36Sopenharmony_ci rpm_msg->needs_free = true; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci ret = __fill_rpmh_msg(rpm_msg, state, cmd, n); 23562306a36Sopenharmony_ci if (ret) { 23662306a36Sopenharmony_ci kfree(rpm_msg); 23762306a36Sopenharmony_ci return ret; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci return __rpmh_write(dev, state, rpm_msg); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ciEXPORT_SYMBOL(rpmh_write_async); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci/** 24562306a36Sopenharmony_ci * rpmh_write: Write a set of RPMH commands and block until response 24662306a36Sopenharmony_ci * 24762306a36Sopenharmony_ci * @dev: The device making the request 24862306a36Sopenharmony_ci * @state: Active/sleep set 24962306a36Sopenharmony_ci * @cmd: The payload data 25062306a36Sopenharmony_ci * @n: The number of elements in @cmd 25162306a36Sopenharmony_ci * 25262306a36Sopenharmony_ci * May sleep. Do not call from atomic contexts. 25362306a36Sopenharmony_ci */ 25462306a36Sopenharmony_ciint rpmh_write(const struct device *dev, enum rpmh_state state, 25562306a36Sopenharmony_ci const struct tcs_cmd *cmd, u32 n) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(compl); 25862306a36Sopenharmony_ci DEFINE_RPMH_MSG_ONSTACK(dev, state, &compl, rpm_msg); 25962306a36Sopenharmony_ci int ret; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci ret = __fill_rpmh_msg(&rpm_msg, state, cmd, n); 26262306a36Sopenharmony_ci if (ret) 26362306a36Sopenharmony_ci return ret; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci ret = __rpmh_write(dev, state, &rpm_msg); 26662306a36Sopenharmony_ci if (ret) 26762306a36Sopenharmony_ci return ret; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci ret = wait_for_completion_timeout(&compl, RPMH_TIMEOUT_MS); 27062306a36Sopenharmony_ci WARN_ON(!ret); 27162306a36Sopenharmony_ci return (ret > 0) ? 0 : -ETIMEDOUT; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ciEXPORT_SYMBOL(rpmh_write); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic void cache_batch(struct rpmh_ctrlr *ctrlr, struct batch_cache_req *req) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci unsigned long flags; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci spin_lock_irqsave(&ctrlr->cache_lock, flags); 28062306a36Sopenharmony_ci list_add_tail(&req->list, &ctrlr->batch_cache); 28162306a36Sopenharmony_ci ctrlr->dirty = true; 28262306a36Sopenharmony_ci spin_unlock_irqrestore(&ctrlr->cache_lock, flags); 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic int flush_batch(struct rpmh_ctrlr *ctrlr) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct batch_cache_req *req; 28862306a36Sopenharmony_ci const struct rpmh_request *rpm_msg; 28962306a36Sopenharmony_ci int ret = 0; 29062306a36Sopenharmony_ci int i; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* Send Sleep/Wake requests to the controller, expect no response */ 29362306a36Sopenharmony_ci list_for_each_entry(req, &ctrlr->batch_cache, list) { 29462306a36Sopenharmony_ci for (i = 0; i < req->count; i++) { 29562306a36Sopenharmony_ci rpm_msg = req->rpm_msgs + i; 29662306a36Sopenharmony_ci ret = rpmh_rsc_write_ctrl_data(ctrlr_to_drv(ctrlr), 29762306a36Sopenharmony_ci &rpm_msg->msg); 29862306a36Sopenharmony_ci if (ret) 29962306a36Sopenharmony_ci break; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return ret; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci/** 30762306a36Sopenharmony_ci * rpmh_write_batch: Write multiple sets of RPMH commands and wait for the 30862306a36Sopenharmony_ci * batch to finish. 30962306a36Sopenharmony_ci * 31062306a36Sopenharmony_ci * @dev: the device making the request 31162306a36Sopenharmony_ci * @state: Active/sleep set 31262306a36Sopenharmony_ci * @cmd: The payload data 31362306a36Sopenharmony_ci * @n: The array of count of elements in each batch, 0 terminated. 31462306a36Sopenharmony_ci * 31562306a36Sopenharmony_ci * Write a request to the RSC controller without caching. If the request 31662306a36Sopenharmony_ci * state is ACTIVE, then the requests are treated as completion request 31762306a36Sopenharmony_ci * and sent to the controller immediately. The function waits until all the 31862306a36Sopenharmony_ci * commands are complete. If the request was to SLEEP or WAKE_ONLY, then the 31962306a36Sopenharmony_ci * request is sent as fire-n-forget and no ack is expected. 32062306a36Sopenharmony_ci * 32162306a36Sopenharmony_ci * May sleep. Do not call from atomic contexts for ACTIVE_ONLY requests. 32262306a36Sopenharmony_ci */ 32362306a36Sopenharmony_ciint rpmh_write_batch(const struct device *dev, enum rpmh_state state, 32462306a36Sopenharmony_ci const struct tcs_cmd *cmd, u32 *n) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct batch_cache_req *req; 32762306a36Sopenharmony_ci struct rpmh_request *rpm_msgs; 32862306a36Sopenharmony_ci struct completion *compls; 32962306a36Sopenharmony_ci struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); 33062306a36Sopenharmony_ci unsigned long time_left; 33162306a36Sopenharmony_ci int count = 0; 33262306a36Sopenharmony_ci int ret, i; 33362306a36Sopenharmony_ci void *ptr; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (!cmd || !n) 33662306a36Sopenharmony_ci return -EINVAL; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci while (n[count] > 0) 33962306a36Sopenharmony_ci count++; 34062306a36Sopenharmony_ci if (!count) 34162306a36Sopenharmony_ci return -EINVAL; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci ptr = kzalloc(sizeof(*req) + 34462306a36Sopenharmony_ci count * (sizeof(req->rpm_msgs[0]) + sizeof(*compls)), 34562306a36Sopenharmony_ci GFP_ATOMIC); 34662306a36Sopenharmony_ci if (!ptr) 34762306a36Sopenharmony_ci return -ENOMEM; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci req = ptr; 35062306a36Sopenharmony_ci compls = ptr + sizeof(*req) + count * sizeof(*rpm_msgs); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci req->count = count; 35362306a36Sopenharmony_ci rpm_msgs = req->rpm_msgs; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci for (i = 0; i < count; i++) { 35662306a36Sopenharmony_ci __fill_rpmh_msg(rpm_msgs + i, state, cmd, n[i]); 35762306a36Sopenharmony_ci cmd += n[i]; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (state != RPMH_ACTIVE_ONLY_STATE) { 36162306a36Sopenharmony_ci cache_batch(ctrlr, req); 36262306a36Sopenharmony_ci return 0; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci for (i = 0; i < count; i++) { 36662306a36Sopenharmony_ci struct completion *compl = &compls[i]; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci init_completion(compl); 36962306a36Sopenharmony_ci rpm_msgs[i].completion = compl; 37062306a36Sopenharmony_ci ret = rpmh_rsc_send_data(ctrlr_to_drv(ctrlr), &rpm_msgs[i].msg); 37162306a36Sopenharmony_ci if (ret) { 37262306a36Sopenharmony_ci pr_err("Error(%d) sending RPMH message addr=%#x\n", 37362306a36Sopenharmony_ci ret, rpm_msgs[i].msg.cmds[0].addr); 37462306a36Sopenharmony_ci break; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci time_left = RPMH_TIMEOUT_MS; 37962306a36Sopenharmony_ci while (i--) { 38062306a36Sopenharmony_ci time_left = wait_for_completion_timeout(&compls[i], time_left); 38162306a36Sopenharmony_ci if (!time_left) { 38262306a36Sopenharmony_ci /* 38362306a36Sopenharmony_ci * Better hope they never finish because they'll signal 38462306a36Sopenharmony_ci * the completion that we're going to free once 38562306a36Sopenharmony_ci * we've returned from this function. 38662306a36Sopenharmony_ci */ 38762306a36Sopenharmony_ci WARN_ON(1); 38862306a36Sopenharmony_ci ret = -ETIMEDOUT; 38962306a36Sopenharmony_ci goto exit; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ciexit: 39462306a36Sopenharmony_ci kfree(ptr); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci return ret; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ciEXPORT_SYMBOL(rpmh_write_batch); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic int is_req_valid(struct cache_req *req) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci return (req->sleep_val != UINT_MAX && 40362306a36Sopenharmony_ci req->wake_val != UINT_MAX && 40462306a36Sopenharmony_ci req->sleep_val != req->wake_val); 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic int send_single(struct rpmh_ctrlr *ctrlr, enum rpmh_state state, 40862306a36Sopenharmony_ci u32 addr, u32 data) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci DEFINE_RPMH_MSG_ONSTACK(NULL, state, NULL, rpm_msg); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* Wake sets are always complete and sleep sets are not */ 41362306a36Sopenharmony_ci rpm_msg.msg.wait_for_compl = (state == RPMH_WAKE_ONLY_STATE); 41462306a36Sopenharmony_ci rpm_msg.cmd[0].addr = addr; 41562306a36Sopenharmony_ci rpm_msg.cmd[0].data = data; 41662306a36Sopenharmony_ci rpm_msg.msg.num_cmds = 1; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci return rpmh_rsc_write_ctrl_data(ctrlr_to_drv(ctrlr), &rpm_msg.msg); 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci/** 42262306a36Sopenharmony_ci * rpmh_flush() - Flushes the buffered sleep and wake sets to TCSes 42362306a36Sopenharmony_ci * 42462306a36Sopenharmony_ci * @ctrlr: Controller making request to flush cached data 42562306a36Sopenharmony_ci * 42662306a36Sopenharmony_ci * Return: 42762306a36Sopenharmony_ci * * 0 - Success 42862306a36Sopenharmony_ci * * Error code - Otherwise 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_ciint rpmh_flush(struct rpmh_ctrlr *ctrlr) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci struct cache_req *p; 43362306a36Sopenharmony_ci int ret = 0; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci lockdep_assert_irqs_disabled(); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* 43862306a36Sopenharmony_ci * Currently rpmh_flush() is only called when we think we're running 43962306a36Sopenharmony_ci * on the last processor. If the lock is busy it means another 44062306a36Sopenharmony_ci * processor is up and it's better to abort than spin. 44162306a36Sopenharmony_ci */ 44262306a36Sopenharmony_ci if (!spin_trylock(&ctrlr->cache_lock)) 44362306a36Sopenharmony_ci return -EBUSY; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (!ctrlr->dirty) { 44662306a36Sopenharmony_ci pr_debug("Skipping flush, TCS has latest data.\n"); 44762306a36Sopenharmony_ci goto write_next_wakeup; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* Invalidate the TCSes first to avoid stale data */ 45162306a36Sopenharmony_ci rpmh_rsc_invalidate(ctrlr_to_drv(ctrlr)); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* First flush the cached batch requests */ 45462306a36Sopenharmony_ci ret = flush_batch(ctrlr); 45562306a36Sopenharmony_ci if (ret) 45662306a36Sopenharmony_ci goto exit; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci list_for_each_entry(p, &ctrlr->cache, list) { 45962306a36Sopenharmony_ci if (!is_req_valid(p)) { 46062306a36Sopenharmony_ci pr_debug("%s: skipping RPMH req: a:%#x s:%#x w:%#x", 46162306a36Sopenharmony_ci __func__, p->addr, p->sleep_val, p->wake_val); 46262306a36Sopenharmony_ci continue; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci ret = send_single(ctrlr, RPMH_SLEEP_STATE, p->addr, 46562306a36Sopenharmony_ci p->sleep_val); 46662306a36Sopenharmony_ci if (ret) 46762306a36Sopenharmony_ci goto exit; 46862306a36Sopenharmony_ci ret = send_single(ctrlr, RPMH_WAKE_ONLY_STATE, p->addr, 46962306a36Sopenharmony_ci p->wake_val); 47062306a36Sopenharmony_ci if (ret) 47162306a36Sopenharmony_ci goto exit; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci ctrlr->dirty = false; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ciwrite_next_wakeup: 47762306a36Sopenharmony_ci rpmh_rsc_write_next_wakeup(ctrlr_to_drv(ctrlr)); 47862306a36Sopenharmony_ciexit: 47962306a36Sopenharmony_ci spin_unlock(&ctrlr->cache_lock); 48062306a36Sopenharmony_ci return ret; 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci/** 48462306a36Sopenharmony_ci * rpmh_invalidate: Invalidate sleep and wake sets in batch_cache 48562306a36Sopenharmony_ci * 48662306a36Sopenharmony_ci * @dev: The device making the request 48762306a36Sopenharmony_ci * 48862306a36Sopenharmony_ci * Invalidate the sleep and wake values in batch_cache. 48962306a36Sopenharmony_ci */ 49062306a36Sopenharmony_civoid rpmh_invalidate(const struct device *dev) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); 49362306a36Sopenharmony_ci struct batch_cache_req *req, *tmp; 49462306a36Sopenharmony_ci unsigned long flags; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci spin_lock_irqsave(&ctrlr->cache_lock, flags); 49762306a36Sopenharmony_ci list_for_each_entry_safe(req, tmp, &ctrlr->batch_cache, list) 49862306a36Sopenharmony_ci kfree(req); 49962306a36Sopenharmony_ci INIT_LIST_HEAD(&ctrlr->batch_cache); 50062306a36Sopenharmony_ci ctrlr->dirty = true; 50162306a36Sopenharmony_ci spin_unlock_irqrestore(&ctrlr->cache_lock, flags); 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ciEXPORT_SYMBOL(rpmh_invalidate); 504