1 // SPDX-License-Identifier: GPL-2.0-only
3 * Exynos generic interconnect provider driver
5 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
7 * Authors: Artur Świgoń <a.swigon@samsung.com>
8 * Sylwester Nawrocki <s.nawrocki@samsung.com>
10 #include <linux/device.h>
11 #include <linux/interconnect-provider.h>
12 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/pm_qos.h>
16 #include <linux/slab.h>
18 #define EXYNOS_ICC_DEFAULT_BUS_CLK_RATIO 8
20 struct exynos_icc_priv
{
23 /* One interconnect node per provider */
24 struct icc_provider provider
;
25 struct icc_node
*node
;
27 struct dev_pm_qos_request qos_req
;
31 static struct icc_node
*exynos_icc_get_parent(struct device_node
*np
)
33 struct of_phandle_args args
;
34 struct icc_node_data
*icc_node_data
;
35 struct icc_node
*icc_node
;
38 num
= of_count_phandle_with_args(np
, "interconnects",
39 "#interconnect-cells");
41 return NULL
; /* parent nodes are optional */
43 /* Get the interconnect target node */
44 ret
= of_parse_phandle_with_args(np
, "interconnects",
45 "#interconnect-cells", 0, &args
);
49 icc_node_data
= of_icc_get_from_provider(&args
);
52 if (IS_ERR(icc_node_data
))
53 return ERR_CAST(icc_node_data
);
55 icc_node
= icc_node_data
->node
;
61 static int exynos_generic_icc_set(struct icc_node
*src
, struct icc_node
*dst
)
63 struct exynos_icc_priv
*src_priv
= src
->data
, *dst_priv
= dst
->data
;
64 s32 src_freq
= max(src
->avg_bw
, src
->peak_bw
) / src_priv
->bus_clk_ratio
;
65 s32 dst_freq
= max(dst
->avg_bw
, dst
->peak_bw
) / dst_priv
->bus_clk_ratio
;
68 ret
= dev_pm_qos_update_request(&src_priv
->qos_req
, src_freq
);
70 dev_err(src_priv
->dev
, "failed to update PM QoS of %s (src)\n",
75 ret
= dev_pm_qos_update_request(&dst_priv
->qos_req
, dst_freq
);
77 dev_err(dst_priv
->dev
, "failed to update PM QoS of %s (dst)\n",
85 static struct icc_node
*exynos_generic_icc_xlate(struct of_phandle_args
*spec
,
88 struct exynos_icc_priv
*priv
= data
;
90 if (spec
->np
!= priv
->dev
->parent
->of_node
)
91 return ERR_PTR(-EINVAL
);
96 static int exynos_generic_icc_remove(struct platform_device
*pdev
)
98 struct exynos_icc_priv
*priv
= platform_get_drvdata(pdev
);
99 struct icc_node
*parent_node
, *node
= priv
->node
;
101 parent_node
= exynos_icc_get_parent(priv
->dev
->parent
->of_node
);
102 if (parent_node
&& !IS_ERR(parent_node
))
103 icc_link_destroy(node
, parent_node
);
105 icc_nodes_remove(&priv
->provider
);
106 icc_provider_del(&priv
->provider
);
111 static int exynos_generic_icc_probe(struct platform_device
*pdev
)
113 struct device
*bus_dev
= pdev
->dev
.parent
;
114 struct exynos_icc_priv
*priv
;
115 struct icc_provider
*provider
;
116 struct icc_node
*icc_node
, *icc_parent_node
;
119 priv
= devm_kzalloc(&pdev
->dev
, sizeof(*priv
), GFP_KERNEL
);
123 priv
->dev
= &pdev
->dev
;
124 platform_set_drvdata(pdev
, priv
);
126 provider
= &priv
->provider
;
128 provider
->set
= exynos_generic_icc_set
;
129 provider
->aggregate
= icc_std_aggregate
;
130 provider
->xlate
= exynos_generic_icc_xlate
;
131 provider
->dev
= bus_dev
;
132 provider
->inter_set
= true;
133 provider
->data
= priv
;
135 ret
= icc_provider_add(provider
);
139 icc_node
= icc_node_create(pdev
->id
);
140 if (IS_ERR(icc_node
)) {
141 ret
= PTR_ERR(icc_node
);
145 priv
->node
= icc_node
;
146 icc_node
->name
= devm_kasprintf(&pdev
->dev
, GFP_KERNEL
, "%pOFn",
148 if (of_property_read_u32(bus_dev
->of_node
, "samsung,data-clock-ratio",
149 &priv
->bus_clk_ratio
))
150 priv
->bus_clk_ratio
= EXYNOS_ICC_DEFAULT_BUS_CLK_RATIO
;
153 * Register a PM QoS request for the parent (devfreq) device.
155 ret
= dev_pm_qos_add_request(bus_dev
, &priv
->qos_req
,
156 DEV_PM_QOS_MIN_FREQUENCY
, 0);
160 icc_node
->data
= priv
;
161 icc_node_add(icc_node
, provider
);
163 icc_parent_node
= exynos_icc_get_parent(bus_dev
->of_node
);
164 if (IS_ERR(icc_parent_node
)) {
165 ret
= PTR_ERR(icc_parent_node
);
168 if (icc_parent_node
) {
169 ret
= icc_link_create(icc_node
, icc_parent_node
->id
);
177 dev_pm_qos_remove_request(&priv
->qos_req
);
179 icc_nodes_remove(provider
);
181 icc_provider_del(provider
);
185 static struct platform_driver exynos_generic_icc_driver
= {
187 .name
= "exynos-generic-icc",
188 .sync_state
= icc_sync_state
,
190 .probe
= exynos_generic_icc_probe
,
191 .remove
= exynos_generic_icc_remove
,
193 module_platform_driver(exynos_generic_icc_driver
);
195 MODULE_DESCRIPTION("Exynos generic interconnect driver");
196 MODULE_AUTHOR("Artur Świgoń <a.swigon@samsung.com>");
197 MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
198 MODULE_LICENSE("GPL v2");
199 MODULE_ALIAS("platform:exynos-generic-icc");