Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

firmware: qcom_scm: Support multiple waitq contexts

Currently, only a single waitqueue context exists in the driver.
Multi-waitqueue mechanism is added in firmware to support the case,
when multiple VMs make SMC calls or single VM making multiple calls on
same CPU. Enhance the driver to support multiple waitqueue when
support is present in the firmware.

When VMs make a SMC call, firmware allocates a waitqueue context,
assuming the SMC call to be a blocking call. The SMC calls that cannot
acquire resources, while execution in firmware, are returned to sleep
in the calling VM. When the resource becomes available in the
firmware, the VM gets notified to wake the sleeping thread and resume
SMC call. The current qcom_scm driver supports single waitqueue as the
old firmwares support only single waitqueue with waitqueue id zero.
Multi-waitqueue mechanism is added in firmware starting SM8650 to
support the case when multiple VMs make SMC calls or single VM making
multiple calls on same CPU. To enable this support in qcom_scm driver,
add support for handling multiple waitqueues. For instance, SM8650
firmware can allocate two such waitq contexts, so the driver needs to
implement two waitqueue contexts. For a generalized approach, the
number of supported waitqueues can be queried from the firmware using
a SMC call.

Introduce qcom_scm_query_waitq_count to get the number of waitqueue
contexts supported by the firmware and allocate “N” unique waitqueue
contexts with a dynamic sized array where each unique wq_ctx is
associated with a struct completion variable for easy lookup. Older
targets which support only a single waitqueue, may return an error for
qcom_scm_query_waitq_count, set the wq_cnt to one for such failures.

Reviewed-by: Bartosz Golaszewski <brgl@kernel.org>
Signed-off-by: Unnathi Chalicheemala <unnathi.chalicheemala@oss.qualcomm.com>
Signed-off-by: Shivendra Pratap <shivendra.pratap@oss.qualcomm.com>
Reviewed-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
Link: https://lore.kernel.org/r/20251217-multi_waitq_scm-v11-2-f21e50e792b8@oss.qualcomm.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>

authored by

Unnathi Chalicheemala and committed by
Bjorn Andersson
ccd207ec da9e6b1a

+50 -22
+50 -22
drivers/firmware/qcom/qcom_scm.c
··· 48 48 struct clk *iface_clk; 49 49 struct clk *bus_clk; 50 50 struct icc_path *path; 51 - struct completion waitq_comp; 51 + struct completion *waitq_comps; 52 52 struct reset_controller_dev reset; 53 53 54 54 /* control access to the interconnect path */ ··· 58 58 u64 dload_mode_addr; 59 59 60 60 struct qcom_tzmem_pool *mempool; 61 + unsigned int wq_cnt; 61 62 }; 62 63 63 64 struct qcom_scm_current_perm_info { ··· 137 136 #define QCOM_DLOAD_FULLDUMP 1 138 137 #define QCOM_DLOAD_MINIDUMP 2 139 138 #define QCOM_DLOAD_BOTHDUMP 3 139 + 140 + #define QCOM_SCM_DEFAULT_WAITQ_COUNT 1 140 141 141 142 static const char * const qcom_scm_convention_names[] = { 142 143 [SMC_CONVENTION_UNKNOWN] = "unknown", ··· 2238 2235 return 0; 2239 2236 } 2240 2237 2238 + static int qcom_scm_query_waitq_count(struct qcom_scm *scm) 2239 + { 2240 + struct qcom_scm_desc desc = { 2241 + .svc = QCOM_SCM_SVC_WAITQ, 2242 + .cmd = QCOM_SCM_WAITQ_GET_INFO, 2243 + .owner = ARM_SMCCC_OWNER_SIP 2244 + }; 2245 + struct qcom_scm_res res; 2246 + int ret; 2247 + 2248 + ret = qcom_scm_call_atomic(scm->dev, &desc, &res); 2249 + if (ret) 2250 + return ret; 2251 + 2252 + return res.result[0] & GENMASK(7, 0); 2253 + } 2254 + 2241 2255 static int qcom_scm_get_waitq_irq(struct qcom_scm *scm) 2242 2256 { 2243 2257 struct qcom_scm_desc desc = { ··· 2286 2266 return irq_create_fwspec_mapping(&fwspec); 2287 2267 } 2288 2268 2289 - static int qcom_scm_assert_valid_wq_ctx(u32 wq_ctx) 2269 + static struct completion *qcom_scm_get_completion(u32 wq_ctx) 2290 2270 { 2291 - /* FW currently only supports a single wq_ctx (zero). 2292 - * TODO: Update this logic to include dynamic allocation and lookup of 2293 - * completion structs when FW supports more wq_ctx values. 2294 - */ 2295 - if (wq_ctx != 0) { 2296 - dev_err(__scm->dev, "Firmware unexpectedly passed non-zero wq_ctx\n"); 2297 - return -EINVAL; 2298 - } 2271 + struct completion *wq; 2299 2272 2300 - return 0; 2273 + if (WARN_ON_ONCE(wq_ctx >= __scm->wq_cnt)) 2274 + return ERR_PTR(-EINVAL); 2275 + 2276 + wq = &__scm->waitq_comps[wq_ctx]; 2277 + 2278 + return wq; 2301 2279 } 2302 2280 2303 2281 int qcom_scm_wait_for_wq_completion(u32 wq_ctx) 2304 2282 { 2305 - int ret; 2283 + struct completion *wq; 2306 2284 2307 - ret = qcom_scm_assert_valid_wq_ctx(wq_ctx); 2308 - if (ret) 2309 - return ret; 2285 + wq = qcom_scm_get_completion(wq_ctx); 2286 + if (IS_ERR(wq)) 2287 + return PTR_ERR(wq); 2310 2288 2311 - wait_for_completion(&__scm->waitq_comp); 2289 + wait_for_completion(wq); 2312 2290 2313 2291 return 0; 2314 2292 } 2315 2293 2316 2294 static int qcom_scm_waitq_wakeup(unsigned int wq_ctx) 2317 2295 { 2318 - int ret; 2296 + struct completion *wq; 2319 2297 2320 - ret = qcom_scm_assert_valid_wq_ctx(wq_ctx); 2321 - if (ret) 2322 - return ret; 2298 + wq = qcom_scm_get_completion(wq_ctx); 2299 + if (IS_ERR(wq)) 2300 + return PTR_ERR(wq); 2323 2301 2324 - complete(&__scm->waitq_comp); 2302 + complete(wq); 2325 2303 2326 2304 return 0; 2327 2305 } ··· 2395 2377 struct qcom_tzmem_pool_config pool_config; 2396 2378 struct qcom_scm *scm; 2397 2379 int irq, ret; 2380 + int i; 2398 2381 2399 2382 scm = devm_kzalloc(&pdev->dev, sizeof(*scm), GFP_KERNEL); 2400 2383 if (!scm) ··· 2406 2387 if (ret < 0) 2407 2388 return ret; 2408 2389 2409 - init_completion(&scm->waitq_comp); 2410 2390 mutex_init(&scm->scm_bw_lock); 2411 2391 2412 2392 scm->path = devm_of_icc_get(&pdev->dev, NULL); ··· 2456 2438 if (IS_ERR(scm->mempool)) 2457 2439 return dev_err_probe(scm->dev, PTR_ERR(scm->mempool), 2458 2440 "Failed to create the SCM memory pool\n"); 2441 + 2442 + ret = qcom_scm_query_waitq_count(scm); 2443 + scm->wq_cnt = ret < 0 ? QCOM_SCM_DEFAULT_WAITQ_COUNT : ret; 2444 + scm->waitq_comps = devm_kcalloc(&pdev->dev, scm->wq_cnt, sizeof(*scm->waitq_comps), 2445 + GFP_KERNEL); 2446 + if (!scm->waitq_comps) 2447 + return -ENOMEM; 2448 + 2449 + for (i = 0; i < scm->wq_cnt; i++) 2450 + init_completion(&scm->waitq_comps[i]); 2459 2451 2460 2452 irq = qcom_scm_get_waitq_irq(scm); 2461 2453 if (irq < 0)