1 | // SPDX-License-Identifier: GPL-2.0 |
2 | |
3 | /* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. |
4 | * Copyright (C) 2018-2022 Linaro Ltd. |
5 | */ |
6 | |
7 | #include <linux/types.h> |
8 | #include <linux/kernel.h> |
9 | |
10 | #include "ipa.h" |
11 | #include "ipa_data.h" |
12 | #include "ipa_reg.h" |
13 | #include "ipa_resource.h" |
14 | |
15 | /** |
16 | * DOC: IPA Resources |
17 | * |
18 | * The IPA manages a set of resources internally for various purposes. |
19 | * A given IPA version has a fixed number of resource types, and a fixed |
20 | * total number of resources of each type. "Source" resource types |
21 | * are separate from "destination" resource types. |
22 | * |
23 | * Each version of IPA also has some number of resource groups. Each |
24 | * endpoint is assigned to a resource group, and all endpoints in the |
25 | * same group share pools of each type of resource. A subset of the |
26 | * total resources of each type is assigned for use by each group. |
27 | */ |
28 | |
29 | static bool ipa_resource_limits_valid(struct ipa *ipa, |
30 | const struct ipa_resource_data *data) |
31 | { |
32 | u32 group_count; |
33 | u32 i; |
34 | u32 j; |
35 | |
36 | /* We program at most 8 source or destination resource group limits */ |
37 | BUILD_BUG_ON(IPA_RESOURCE_GROUP_MAX > 8); |
38 | |
39 | group_count = data->rsrc_group_src_count; |
40 | if (!group_count || group_count > IPA_RESOURCE_GROUP_MAX) |
41 | return false; |
42 | |
43 | /* Return an error if a non-zero resource limit is specified |
44 | * for a resource group not supported by hardware. |
45 | */ |
46 | for (i = 0; i < data->resource_src_count; i++) { |
47 | const struct ipa_resource *resource; |
48 | |
49 | resource = &data->resource_src[i]; |
50 | for (j = group_count; j < IPA_RESOURCE_GROUP_MAX; j++) |
51 | if (resource->limits[j].min || resource->limits[j].max) |
52 | return false; |
53 | } |
54 | |
55 | group_count = data->rsrc_group_dst_count; |
56 | if (!group_count || group_count > IPA_RESOURCE_GROUP_MAX) |
57 | return false; |
58 | |
59 | for (i = 0; i < data->resource_dst_count; i++) { |
60 | const struct ipa_resource *resource; |
61 | |
62 | resource = &data->resource_dst[i]; |
63 | for (j = group_count; j < IPA_RESOURCE_GROUP_MAX; j++) |
64 | if (resource->limits[j].min || resource->limits[j].max) |
65 | return false; |
66 | } |
67 | |
68 | return true; |
69 | } |
70 | |
71 | static void |
72 | ipa_resource_config_common(struct ipa *ipa, u32 resource_type, |
73 | const struct reg *reg, |
74 | const struct ipa_resource_limits *xlimits, |
75 | const struct ipa_resource_limits *ylimits) |
76 | { |
77 | u32 val; |
78 | |
79 | val = reg_encode(reg, field_id: X_MIN_LIM, val: xlimits->min); |
80 | val |= reg_encode(reg, field_id: X_MAX_LIM, val: xlimits->max); |
81 | if (ylimits) { |
82 | val |= reg_encode(reg, field_id: Y_MIN_LIM, val: ylimits->min); |
83 | val |= reg_encode(reg, field_id: Y_MAX_LIM, val: ylimits->max); |
84 | } |
85 | |
86 | iowrite32(val, ipa->reg_virt + reg_n_offset(reg, n: resource_type)); |
87 | } |
88 | |
89 | static void ipa_resource_config_src(struct ipa *ipa, u32 resource_type, |
90 | const struct ipa_resource_data *data) |
91 | { |
92 | u32 group_count = data->rsrc_group_src_count; |
93 | const struct ipa_resource_limits *ylimits; |
94 | const struct ipa_resource *resource; |
95 | const struct reg *reg; |
96 | |
97 | resource = &data->resource_src[resource_type]; |
98 | |
99 | reg = ipa_reg(ipa, reg_id: SRC_RSRC_GRP_01_RSRC_TYPE); |
100 | ylimits = group_count == 1 ? NULL : &resource->limits[1]; |
101 | ipa_resource_config_common(ipa, resource_type, reg, |
102 | xlimits: &resource->limits[0], ylimits); |
103 | if (group_count < 3) |
104 | return; |
105 | |
106 | reg = ipa_reg(ipa, reg_id: SRC_RSRC_GRP_23_RSRC_TYPE); |
107 | ylimits = group_count == 3 ? NULL : &resource->limits[3]; |
108 | ipa_resource_config_common(ipa, resource_type, reg, |
109 | xlimits: &resource->limits[2], ylimits); |
110 | if (group_count < 5) |
111 | return; |
112 | |
113 | reg = ipa_reg(ipa, reg_id: SRC_RSRC_GRP_45_RSRC_TYPE); |
114 | ylimits = group_count == 5 ? NULL : &resource->limits[5]; |
115 | ipa_resource_config_common(ipa, resource_type, reg, |
116 | xlimits: &resource->limits[4], ylimits); |
117 | if (group_count < 7) |
118 | return; |
119 | |
120 | reg = ipa_reg(ipa, reg_id: SRC_RSRC_GRP_67_RSRC_TYPE); |
121 | ylimits = group_count == 7 ? NULL : &resource->limits[7]; |
122 | ipa_resource_config_common(ipa, resource_type, reg, |
123 | xlimits: &resource->limits[6], ylimits); |
124 | } |
125 | |
126 | static void ipa_resource_config_dst(struct ipa *ipa, u32 resource_type, |
127 | const struct ipa_resource_data *data) |
128 | { |
129 | u32 group_count = data->rsrc_group_dst_count; |
130 | const struct ipa_resource_limits *ylimits; |
131 | const struct ipa_resource *resource; |
132 | const struct reg *reg; |
133 | |
134 | resource = &data->resource_dst[resource_type]; |
135 | |
136 | reg = ipa_reg(ipa, reg_id: DST_RSRC_GRP_01_RSRC_TYPE); |
137 | ylimits = group_count == 1 ? NULL : &resource->limits[1]; |
138 | ipa_resource_config_common(ipa, resource_type, reg, |
139 | xlimits: &resource->limits[0], ylimits); |
140 | if (group_count < 3) |
141 | return; |
142 | |
143 | reg = ipa_reg(ipa, reg_id: DST_RSRC_GRP_23_RSRC_TYPE); |
144 | ylimits = group_count == 3 ? NULL : &resource->limits[3]; |
145 | ipa_resource_config_common(ipa, resource_type, reg, |
146 | xlimits: &resource->limits[2], ylimits); |
147 | if (group_count < 5) |
148 | return; |
149 | |
150 | reg = ipa_reg(ipa, reg_id: DST_RSRC_GRP_45_RSRC_TYPE); |
151 | ylimits = group_count == 5 ? NULL : &resource->limits[5]; |
152 | ipa_resource_config_common(ipa, resource_type, reg, |
153 | xlimits: &resource->limits[4], ylimits); |
154 | if (group_count < 7) |
155 | return; |
156 | |
157 | reg = ipa_reg(ipa, reg_id: DST_RSRC_GRP_67_RSRC_TYPE); |
158 | ylimits = group_count == 7 ? NULL : &resource->limits[7]; |
159 | ipa_resource_config_common(ipa, resource_type, reg, |
160 | xlimits: &resource->limits[6], ylimits); |
161 | } |
162 | |
163 | /* Configure resources; there is no ipa_resource_deconfig() */ |
164 | int ipa_resource_config(struct ipa *ipa, const struct ipa_resource_data *data) |
165 | { |
166 | u32 i; |
167 | |
168 | if (!ipa_resource_limits_valid(ipa, data)) |
169 | return -EINVAL; |
170 | |
171 | for (i = 0; i < data->resource_src_count; i++) |
172 | ipa_resource_config_src(ipa, resource_type: i, data); |
173 | |
174 | for (i = 0; i < data->resource_dst_count; i++) |
175 | ipa_resource_config_dst(ipa, resource_type: i, data); |
176 | |
177 | return 0; |
178 | } |
179 | |