Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | // SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. */ #include <dm/device.h> #include <dm/device_compat.h> #include <linux/bug.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/types.h> #include <soc/qcom/rpmh.h> #include "rpmh-internal.h" #define RPMH_TIMEOUT_MS msecs_to_jiffies(10000) #define DEFINE_RPMH_MSG_ONSTACK(device, s, name) \ struct rpmh_request name = { \ .msg = { \ .state = s, \ .cmds = name.cmd, \ .num_cmds = 0, \ .wait_for_compl = true \ }, \ .cmd = { { 0 } }, \ .dev = device, \ .needs_free = false, \ } #define ctrlr_to_drv(ctrlr) container_of(ctrlr, struct rsc_drv, client) static struct rpmh_ctrlr *get_rpmh_ctrlr(const struct udevice *dev) { struct rsc_drv *drv = (struct rsc_drv *)dev_get_priv(dev->parent); if (!drv) { log_err("BUG: no RPMh driver for %s (parent %s)\n", dev->name, dev->parent->name); BUG(); } return &drv->client; } /** * __rpmh_write: Cache and send the RPMH request * * @dev: The device making the request * @state: Active/Sleep request type * @rpm_msg: The data that needs to be sent (cmds). * * Cache the RPMH request and send if the state is ACTIVE_ONLY. * SLEEP/WAKE_ONLY requests are not sent to the controller at * this time. Use rpmh_flush() to send them to the controller. */ static int __rpmh_write(const struct udevice *dev, enum rpmh_state state, struct rpmh_request *rpm_msg) { struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); if (state != RPMH_ACTIVE_ONLY_STATE) { log_debug("WARN: Only ACTIVE_ONLY state supported\n"); return 0; } return rpmh_rsc_send_data(ctrlr_to_drv(ctrlr), &rpm_msg->msg); } static int __fill_rpmh_msg(struct rpmh_request *req, enum rpmh_state state, const struct tcs_cmd *cmd, u32 n, bool is_read) { if (!cmd || !n || n > MAX_RPMH_PAYLOAD) return -EINVAL; memcpy(req->cmd, cmd, n * sizeof(*cmd)); req->msg.state = state; req->msg.cmds = req->cmd; req->msg.num_cmds = n; req->msg.is_read = is_read; debug("rpmh_msg: %d, %d cmds [first %#x/%#x]\n", state, n, cmd->addr, cmd->data); return 0; } /** * rpmh_read: Read a resource value * * @dev: The device making the request * @cmd: The payload having address of resource to read * * Reads the value for the resource address given in tcs_cmd->addr * and returns the tcs_cmd->data filled with same. * * May sleep. Do not call from atomic contexts. * * Return: 0 on success, negative errno on failure */ int rpmh_read(const struct udevice *dev, enum rpmh_state state, struct tcs_cmd *cmd) { DEFINE_RPMH_MSG_ONSTACK(dev, state, rpm_msg); int ret; ret = __fill_rpmh_msg(&rpm_msg, state, cmd, 1, true); if (ret) return ret; ret = __rpmh_write(dev, state, &rpm_msg); if (ret) return ret; /* Read back the response into the cmd data structure */ cmd->data = rpm_msg.cmd[0].data; return (ret > 0) ? 0 : ret; } /** * rpmh_write: Write a set of RPMH commands and block until response * * @dev: The device making the request * @state: Active/sleep set * @cmd: The payload data * @n: The number of elements in @cmd * * May sleep. Do not call from atomic contexts. */ int rpmh_write(const struct udevice *dev, enum rpmh_state state, const struct tcs_cmd *cmd, u32 n) { DEFINE_RPMH_MSG_ONSTACK(dev, state, rpm_msg); int ret; ret = __fill_rpmh_msg(&rpm_msg, state, cmd, n, false); if (ret) return ret; ret = __rpmh_write(dev, state, &rpm_msg); return ret; } EXPORT_SYMBOL_GPL(rpmh_write); |