1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2020, The Linux Foundation. All rights reserved.
6 #include <linux/bitfield.h>
8 #include <linux/interconnect-provider.h>
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/of_device.h>
13 #include <linux/platform_device.h>
15 #include <dt-bindings/interconnect/qcom,osm-l3.h>
22 #define LUT_MAX_ENTRIES 40U
23 #define LUT_SRC GENMASK(31, 30)
24 #define LUT_L_VAL GENMASK(7, 0)
27 /* OSM Register offsets */
28 #define REG_ENABLE 0x0
29 #define OSM_LUT_ROW_SIZE 32
30 #define OSM_REG_FREQ_LUT 0x110
31 #define OSM_REG_PERF_STATE 0x920
33 /* EPSS Register offsets */
34 #define EPSS_LUT_ROW_SIZE 4
35 #define EPSS_REG_FREQ_LUT 0x100
36 #define EPSS_REG_PERF_STATE 0x320
38 #define OSM_L3_MAX_LINKS 1
40 #define to_qcom_provider(_provider) \
41 container_of(_provider, struct qcom_osm_l3_icc_provider, provider)
43 struct qcom_osm_l3_icc_provider
{
45 unsigned int max_state
;
46 unsigned int reg_perf_state
;
47 unsigned long lut_tables
[LUT_MAX_ENTRIES
];
48 struct icc_provider provider
;
52 * struct qcom_icc_node - Qualcomm specific interconnect nodes
53 * @name: the node name used in debugfs
54 * @links: an array of nodes where we can go next while traversing
55 * @id: a unique node identifier
56 * @num_links: the total number of @links
57 * @buswidth: width of the interconnect between a node and the bus
59 struct qcom_icc_node
{
61 u16 links
[OSM_L3_MAX_LINKS
];
67 struct qcom_icc_desc
{
68 const struct qcom_icc_node
**nodes
;
70 unsigned int lut_row_size
;
71 unsigned int reg_freq_lut
;
72 unsigned int reg_perf_state
;
75 #define DEFINE_QNODE(_name, _id, _buswidth, ...) \
76 static const struct qcom_icc_node _name = { \
79 .buswidth = _buswidth, \
80 .num_links = ARRAY_SIZE(((int[]){ __VA_ARGS__ })), \
81 .links = { __VA_ARGS__ }, \
84 DEFINE_QNODE(sdm845_osm_apps_l3
, SDM845_MASTER_OSM_L3_APPS
, 16, SDM845_SLAVE_OSM_L3
);
85 DEFINE_QNODE(sdm845_osm_l3
, SDM845_SLAVE_OSM_L3
, 16);
87 static const struct qcom_icc_node
*sdm845_osm_l3_nodes
[] = {
88 [MASTER_OSM_L3_APPS
] = &sdm845_osm_apps_l3
,
89 [SLAVE_OSM_L3
] = &sdm845_osm_l3
,
92 static const struct qcom_icc_desc sdm845_icc_osm_l3
= {
93 .nodes
= sdm845_osm_l3_nodes
,
94 .num_nodes
= ARRAY_SIZE(sdm845_osm_l3_nodes
),
95 .lut_row_size
= OSM_LUT_ROW_SIZE
,
96 .reg_freq_lut
= OSM_REG_FREQ_LUT
,
97 .reg_perf_state
= OSM_REG_PERF_STATE
,
100 DEFINE_QNODE(sc7180_osm_apps_l3
, SC7180_MASTER_OSM_L3_APPS
, 16, SC7180_SLAVE_OSM_L3
);
101 DEFINE_QNODE(sc7180_osm_l3
, SC7180_SLAVE_OSM_L3
, 16);
103 static const struct qcom_icc_node
*sc7180_osm_l3_nodes
[] = {
104 [MASTER_OSM_L3_APPS
] = &sc7180_osm_apps_l3
,
105 [SLAVE_OSM_L3
] = &sc7180_osm_l3
,
108 static const struct qcom_icc_desc sc7180_icc_osm_l3
= {
109 .nodes
= sc7180_osm_l3_nodes
,
110 .num_nodes
= ARRAY_SIZE(sc7180_osm_l3_nodes
),
111 .lut_row_size
= OSM_LUT_ROW_SIZE
,
112 .reg_freq_lut
= OSM_REG_FREQ_LUT
,
113 .reg_perf_state
= OSM_REG_PERF_STATE
,
116 DEFINE_QNODE(sm8150_osm_apps_l3
, SM8150_MASTER_OSM_L3_APPS
, 32, SM8150_SLAVE_OSM_L3
);
117 DEFINE_QNODE(sm8150_osm_l3
, SM8150_SLAVE_OSM_L3
, 32);
119 static const struct qcom_icc_node
*sm8150_osm_l3_nodes
[] = {
120 [MASTER_OSM_L3_APPS
] = &sm8150_osm_apps_l3
,
121 [SLAVE_OSM_L3
] = &sm8150_osm_l3
,
124 static const struct qcom_icc_desc sm8150_icc_osm_l3
= {
125 .nodes
= sm8150_osm_l3_nodes
,
126 .num_nodes
= ARRAY_SIZE(sm8150_osm_l3_nodes
),
127 .lut_row_size
= OSM_LUT_ROW_SIZE
,
128 .reg_freq_lut
= OSM_REG_FREQ_LUT
,
129 .reg_perf_state
= OSM_REG_PERF_STATE
,
132 DEFINE_QNODE(sm8250_epss_apps_l3
, SM8250_MASTER_EPSS_L3_APPS
, 32, SM8250_SLAVE_EPSS_L3
);
133 DEFINE_QNODE(sm8250_epss_l3
, SM8250_SLAVE_EPSS_L3
, 32);
135 static const struct qcom_icc_node
*sm8250_epss_l3_nodes
[] = {
136 [MASTER_EPSS_L3_APPS
] = &sm8250_epss_apps_l3
,
137 [SLAVE_EPSS_L3_SHARED
] = &sm8250_epss_l3
,
140 static const struct qcom_icc_desc sm8250_icc_epss_l3
= {
141 .nodes
= sm8250_epss_l3_nodes
,
142 .num_nodes
= ARRAY_SIZE(sm8250_epss_l3_nodes
),
143 .lut_row_size
= EPSS_LUT_ROW_SIZE
,
144 .reg_freq_lut
= EPSS_REG_FREQ_LUT
,
145 .reg_perf_state
= EPSS_REG_PERF_STATE
,
148 static int qcom_icc_set(struct icc_node
*src
, struct icc_node
*dst
)
150 struct qcom_osm_l3_icc_provider
*qp
;
151 struct icc_provider
*provider
;
152 const struct qcom_icc_node
*qn
;
160 provider
= src
->provider
;
161 qp
= to_qcom_provider(provider
);
163 list_for_each_entry(n
, &provider
->nodes
, node_list
)
164 provider
->aggregate(n
, 0, n
->avg_bw
, n
->peak_bw
,
165 &agg_avg
, &agg_peak
);
167 rate
= max(agg_avg
, agg_peak
);
168 rate
= icc_units_to_bps(rate
);
169 do_div(rate
, qn
->buswidth
);
171 for (index
= 0; index
< qp
->max_state
- 1; index
++) {
172 if (qp
->lut_tables
[index
] >= rate
)
176 writel_relaxed(index
, qp
->base
+ qp
->reg_perf_state
);
181 static int qcom_osm_l3_remove(struct platform_device
*pdev
)
183 struct qcom_osm_l3_icc_provider
*qp
= platform_get_drvdata(pdev
);
185 icc_nodes_remove(&qp
->provider
);
186 return icc_provider_del(&qp
->provider
);
189 static int qcom_osm_l3_probe(struct platform_device
*pdev
)
191 u32 info
, src
, lval
, i
, prev_freq
= 0, freq
;
192 static unsigned long hw_rate
, xo_rate
;
193 struct qcom_osm_l3_icc_provider
*qp
;
194 const struct qcom_icc_desc
*desc
;
195 struct icc_onecell_data
*data
;
196 struct icc_provider
*provider
;
197 const struct qcom_icc_node
**qnodes
;
198 struct icc_node
*node
;
203 clk
= clk_get(&pdev
->dev
, "xo");
207 xo_rate
= clk_get_rate(clk
);
210 clk
= clk_get(&pdev
->dev
, "alternate");
214 hw_rate
= clk_get_rate(clk
) / CLK_HW_DIV
;
217 qp
= devm_kzalloc(&pdev
->dev
, sizeof(*qp
), GFP_KERNEL
);
221 qp
->base
= devm_platform_ioremap_resource(pdev
, 0);
222 if (IS_ERR(qp
->base
))
223 return PTR_ERR(qp
->base
);
225 /* HW should be in enabled state to proceed */
226 if (!(readl_relaxed(qp
->base
+ REG_ENABLE
) & 0x1)) {
227 dev_err(&pdev
->dev
, "error hardware not enabled\n");
231 desc
= device_get_match_data(&pdev
->dev
);
235 qp
->reg_perf_state
= desc
->reg_perf_state
;
237 for (i
= 0; i
< LUT_MAX_ENTRIES
; i
++) {
238 info
= readl_relaxed(qp
->base
+ desc
->reg_freq_lut
+
239 i
* desc
->lut_row_size
);
240 src
= FIELD_GET(LUT_SRC
, info
);
241 lval
= FIELD_GET(LUT_L_VAL
, info
);
243 freq
= xo_rate
* lval
;
247 /* Two of the same frequencies signify end of table */
248 if (i
> 0 && prev_freq
== freq
)
251 dev_dbg(&pdev
->dev
, "index=%d freq=%d\n", i
, freq
);
253 qp
->lut_tables
[i
] = freq
;
258 qnodes
= desc
->nodes
;
259 num_nodes
= desc
->num_nodes
;
261 data
= devm_kcalloc(&pdev
->dev
, num_nodes
, sizeof(*node
), GFP_KERNEL
);
265 provider
= &qp
->provider
;
266 provider
->dev
= &pdev
->dev
;
267 provider
->set
= qcom_icc_set
;
268 provider
->aggregate
= icc_std_aggregate
;
269 provider
->xlate
= of_icc_xlate_onecell
;
270 INIT_LIST_HEAD(&provider
->nodes
);
271 provider
->data
= data
;
273 ret
= icc_provider_add(provider
);
275 dev_err(&pdev
->dev
, "error adding interconnect provider\n");
279 for (i
= 0; i
< num_nodes
; i
++) {
282 node
= icc_node_create(qnodes
[i
]->id
);
288 node
->name
= qnodes
[i
]->name
;
289 /* Cast away const and add it back in qcom_icc_set() */
290 node
->data
= (void *)qnodes
[i
];
291 icc_node_add(node
, provider
);
293 for (j
= 0; j
< qnodes
[i
]->num_links
; j
++)
294 icc_link_create(node
, qnodes
[i
]->links
[j
]);
296 data
->nodes
[i
] = node
;
298 data
->num_nodes
= num_nodes
;
300 platform_set_drvdata(pdev
, qp
);
304 icc_nodes_remove(provider
);
305 icc_provider_del(provider
);
310 static const struct of_device_id osm_l3_of_match
[] = {
311 { .compatible
= "qcom,sc7180-osm-l3", .data
= &sc7180_icc_osm_l3
},
312 { .compatible
= "qcom,sdm845-osm-l3", .data
= &sdm845_icc_osm_l3
},
313 { .compatible
= "qcom,sm8150-osm-l3", .data
= &sm8150_icc_osm_l3
},
314 { .compatible
= "qcom,sm8250-epss-l3", .data
= &sm8250_icc_epss_l3
},
317 MODULE_DEVICE_TABLE(of
, osm_l3_of_match
);
319 static struct platform_driver osm_l3_driver
= {
320 .probe
= qcom_osm_l3_probe
,
321 .remove
= qcom_osm_l3_remove
,
324 .of_match_table
= osm_l3_of_match
,
325 .sync_state
= icc_sync_state
,
328 module_platform_driver(osm_l3_driver
);
330 MODULE_DESCRIPTION("Qualcomm OSM L3 interconnect driver");
331 MODULE_LICENSE("GPL v2");