imx.c (6961B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Interconnect framework driver for i.MX SoC 4 * 5 * Copyright (c) 2019, BayLibre 6 * Copyright (c) 2019-2020, NXP 7 * Author: Alexandre Bailon <abailon@baylibre.com> 8 * Author: Leonard Crestez <leonard.crestez@nxp.com> 9 */ 10 11#include <linux/device.h> 12#include <linux/interconnect-provider.h> 13#include <linux/module.h> 14#include <linux/of.h> 15#include <linux/of_platform.h> 16#include <linux/platform_device.h> 17#include <linux/pm_qos.h> 18 19#include "imx.h" 20 21/* private icc_node data */ 22struct imx_icc_node { 23 const struct imx_icc_node_desc *desc; 24 struct device *qos_dev; 25 struct dev_pm_qos_request qos_req; 26}; 27 28static int imx_icc_get_bw(struct icc_node *node, u32 *avg, u32 *peak) 29{ 30 *avg = 0; 31 *peak = 0; 32 33 return 0; 34} 35 36static int imx_icc_node_set(struct icc_node *node) 37{ 38 struct device *dev = node->provider->dev; 39 struct imx_icc_node *node_data = node->data; 40 u64 freq; 41 42 if (!node_data->qos_dev) 43 return 0; 44 45 freq = (node->avg_bw + node->peak_bw) * node_data->desc->adj->bw_mul; 46 do_div(freq, node_data->desc->adj->bw_div); 47 dev_dbg(dev, "node %s device %s avg_bw %ukBps peak_bw %ukBps min_freq %llukHz\n", 48 node->name, dev_name(node_data->qos_dev), 49 node->avg_bw, node->peak_bw, freq); 50 51 if (freq > S32_MAX) { 52 dev_err(dev, "%s can't request more than S32_MAX freq\n", 53 node->name); 54 return -ERANGE; 55 } 56 57 dev_pm_qos_update_request(&node_data->qos_req, freq); 58 59 return 0; 60} 61 62static int imx_icc_set(struct icc_node *src, struct icc_node *dst) 63{ 64 return imx_icc_node_set(dst); 65} 66 67/* imx_icc_node_destroy() - Destroy an imx icc_node, including private data */ 68static void imx_icc_node_destroy(struct icc_node *node) 69{ 70 struct imx_icc_node *node_data = node->data; 71 int ret; 72 73 if (dev_pm_qos_request_active(&node_data->qos_req)) { 74 ret = dev_pm_qos_remove_request(&node_data->qos_req); 75 if (ret) 76 dev_warn(node->provider->dev, 77 "failed to remove qos request for %s\n", 78 dev_name(node_data->qos_dev)); 79 } 80 81 put_device(node_data->qos_dev); 82 icc_node_del(node); 83 icc_node_destroy(node->id); 84} 85 86static int imx_icc_node_init_qos(struct icc_provider *provider, 87 struct icc_node *node) 88{ 89 struct imx_icc_node *node_data = node->data; 90 const struct imx_icc_node_adj_desc *adj = node_data->desc->adj; 91 struct device *dev = provider->dev; 92 struct device_node *dn = NULL; 93 struct platform_device *pdev; 94 95 if (adj->main_noc) { 96 node_data->qos_dev = dev; 97 dev_dbg(dev, "icc node %s[%d] is main noc itself\n", 98 node->name, node->id); 99 } else { 100 dn = of_parse_phandle(dev->of_node, adj->phandle_name, 0); 101 if (!dn) { 102 dev_warn(dev, "Failed to parse %s\n", 103 adj->phandle_name); 104 return -ENODEV; 105 } 106 /* Allow scaling to be disabled on a per-node basis */ 107 if (!of_device_is_available(dn)) { 108 dev_warn(dev, "Missing property %s, skip scaling %s\n", 109 adj->phandle_name, node->name); 110 of_node_put(dn); 111 return 0; 112 } 113 114 pdev = of_find_device_by_node(dn); 115 of_node_put(dn); 116 if (!pdev) { 117 dev_warn(dev, "node %s[%d] missing device for %pOF\n", 118 node->name, node->id, dn); 119 return -EPROBE_DEFER; 120 } 121 node_data->qos_dev = &pdev->dev; 122 dev_dbg(dev, "node %s[%d] has device node %pOF\n", 123 node->name, node->id, dn); 124 } 125 126 return dev_pm_qos_add_request(node_data->qos_dev, 127 &node_data->qos_req, 128 DEV_PM_QOS_MIN_FREQUENCY, 0); 129} 130 131static struct icc_node *imx_icc_node_add(struct icc_provider *provider, 132 const struct imx_icc_node_desc *node_desc) 133{ 134 struct device *dev = provider->dev; 135 struct imx_icc_node *node_data; 136 struct icc_node *node; 137 int ret; 138 139 node = icc_node_create(node_desc->id); 140 if (IS_ERR(node)) { 141 dev_err(dev, "failed to create node %d\n", node_desc->id); 142 return node; 143 } 144 145 if (node->data) { 146 dev_err(dev, "already created node %s id=%d\n", 147 node_desc->name, node_desc->id); 148 return ERR_PTR(-EEXIST); 149 } 150 151 node_data = devm_kzalloc(dev, sizeof(*node_data), GFP_KERNEL); 152 if (!node_data) { 153 icc_node_destroy(node->id); 154 return ERR_PTR(-ENOMEM); 155 } 156 157 node->name = node_desc->name; 158 node->data = node_data; 159 node_data->desc = node_desc; 160 icc_node_add(node, provider); 161 162 if (node_desc->adj) { 163 ret = imx_icc_node_init_qos(provider, node); 164 if (ret < 0) { 165 imx_icc_node_destroy(node); 166 return ERR_PTR(ret); 167 } 168 } 169 170 return node; 171} 172 173static void imx_icc_unregister_nodes(struct icc_provider *provider) 174{ 175 struct icc_node *node, *tmp; 176 177 list_for_each_entry_safe(node, tmp, &provider->nodes, node_list) 178 imx_icc_node_destroy(node); 179} 180 181static int imx_icc_register_nodes(struct icc_provider *provider, 182 const struct imx_icc_node_desc *descs, 183 int count) 184{ 185 struct icc_onecell_data *provider_data = provider->data; 186 int ret; 187 int i; 188 189 for (i = 0; i < count; i++) { 190 struct icc_node *node; 191 const struct imx_icc_node_desc *node_desc = &descs[i]; 192 size_t j; 193 194 node = imx_icc_node_add(provider, node_desc); 195 if (IS_ERR(node)) { 196 ret = dev_err_probe(provider->dev, PTR_ERR(node), 197 "failed to add %s\n", node_desc->name); 198 goto err; 199 } 200 provider_data->nodes[node->id] = node; 201 202 for (j = 0; j < node_desc->num_links; j++) { 203 ret = icc_link_create(node, node_desc->links[j]); 204 if (ret) { 205 dev_err(provider->dev, "failed to link node %d to %d: %d\n", 206 node->id, node_desc->links[j], ret); 207 goto err; 208 } 209 } 210 } 211 212 return 0; 213 214err: 215 imx_icc_unregister_nodes(provider); 216 217 return ret; 218} 219 220static int get_max_node_id(struct imx_icc_node_desc *nodes, int nodes_count) 221{ 222 int i, ret = 0; 223 224 for (i = 0; i < nodes_count; ++i) 225 if (nodes[i].id > ret) 226 ret = nodes[i].id; 227 228 return ret; 229} 230 231int imx_icc_register(struct platform_device *pdev, 232 struct imx_icc_node_desc *nodes, int nodes_count) 233{ 234 struct device *dev = &pdev->dev; 235 struct icc_onecell_data *data; 236 struct icc_provider *provider; 237 int max_node_id; 238 int ret; 239 240 /* icc_onecell_data is indexed by node_id, unlike nodes param */ 241 max_node_id = get_max_node_id(nodes, nodes_count); 242 data = devm_kzalloc(dev, struct_size(data, nodes, max_node_id), 243 GFP_KERNEL); 244 if (!data) 245 return -ENOMEM; 246 data->num_nodes = max_node_id; 247 248 provider = devm_kzalloc(dev, sizeof(*provider), GFP_KERNEL); 249 if (!provider) 250 return -ENOMEM; 251 provider->set = imx_icc_set; 252 provider->get_bw = imx_icc_get_bw; 253 provider->aggregate = icc_std_aggregate; 254 provider->xlate = of_icc_xlate_onecell; 255 provider->data = data; 256 provider->dev = dev->parent; 257 platform_set_drvdata(pdev, provider); 258 259 ret = icc_provider_add(provider); 260 if (ret) { 261 dev_err(dev, "error adding interconnect provider: %d\n", ret); 262 return ret; 263 } 264 265 ret = imx_icc_register_nodes(provider, nodes, nodes_count); 266 if (ret) 267 goto provider_del; 268 269 return 0; 270 271provider_del: 272 icc_provider_del(provider); 273 return ret; 274} 275EXPORT_SYMBOL_GPL(imx_icc_register); 276 277int imx_icc_unregister(struct platform_device *pdev) 278{ 279 struct icc_provider *provider = platform_get_drvdata(pdev); 280 281 imx_icc_unregister_nodes(provider); 282 283 return icc_provider_del(provider); 284} 285EXPORT_SYMBOL_GPL(imx_icc_unregister); 286 287MODULE_LICENSE("GPL v2");