Linux Audio

Check our new training course

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