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(const 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 void exynos_generic_icc_remove(struct platform_device
*pdev
)
98 struct exynos_icc_priv
*priv
= platform_get_drvdata(pdev
);
100 icc_provider_deregister(&priv
->provider
);
101 icc_nodes_remove(&priv
->provider
);
104 static int exynos_generic_icc_probe(struct platform_device
*pdev
)
106 struct device
*bus_dev
= pdev
->dev
.parent
;
107 struct exynos_icc_priv
*priv
;
108 struct icc_provider
*provider
;
109 struct icc_node
*icc_node
, *icc_parent_node
;
112 priv
= devm_kzalloc(&pdev
->dev
, sizeof(*priv
), GFP_KERNEL
);
116 priv
->dev
= &pdev
->dev
;
117 platform_set_drvdata(pdev
, priv
);
119 provider
= &priv
->provider
;
121 provider
->set
= exynos_generic_icc_set
;
122 provider
->aggregate
= icc_std_aggregate
;
123 provider
->xlate
= exynos_generic_icc_xlate
;
124 provider
->dev
= bus_dev
;
125 provider
->inter_set
= true;
126 provider
->data
= priv
;
128 icc_provider_init(provider
);
130 icc_node
= icc_node_create(pdev
->id
);
131 if (IS_ERR(icc_node
))
132 return PTR_ERR(icc_node
);
134 priv
->node
= icc_node
;
135 icc_node
->name
= devm_kasprintf(&pdev
->dev
, GFP_KERNEL
, "%pOFn",
137 if (of_property_read_u32(bus_dev
->of_node
, "samsung,data-clock-ratio",
138 &priv
->bus_clk_ratio
))
139 priv
->bus_clk_ratio
= EXYNOS_ICC_DEFAULT_BUS_CLK_RATIO
;
141 icc_node
->data
= priv
;
142 icc_node_add(icc_node
, provider
);
145 * Register a PM QoS request for the parent (devfreq) device.
147 ret
= dev_pm_qos_add_request(bus_dev
, &priv
->qos_req
,
148 DEV_PM_QOS_MIN_FREQUENCY
, 0);
152 icc_parent_node
= exynos_icc_get_parent(bus_dev
->of_node
);
153 if (IS_ERR(icc_parent_node
)) {
154 ret
= PTR_ERR(icc_parent_node
);
157 if (icc_parent_node
) {
158 ret
= icc_link_create(icc_node
, icc_parent_node
->id
);
163 ret
= icc_provider_register(provider
);
170 dev_pm_qos_remove_request(&priv
->qos_req
);
172 icc_nodes_remove(provider
);
177 static struct platform_driver exynos_generic_icc_driver
= {
179 .name
= "exynos-generic-icc",
180 .sync_state
= icc_sync_state
,
182 .probe
= exynos_generic_icc_probe
,
183 .remove_new
= exynos_generic_icc_remove
,
185 module_platform_driver(exynos_generic_icc_driver
);
187 MODULE_DESCRIPTION("Exynos generic interconnect driver");
188 MODULE_AUTHOR("Artur Świgoń <a.swigon@samsung.com>");
189 MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
190 MODULE_LICENSE("GPL v2");
191 MODULE_ALIAS("platform:exynos-generic-icc");