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

soc: qcom: apr: Add avs/audio tracking functionality

Use PDR helper functions to track the protection domains that the apr
services are dependent upon on SDM845 SoC, specifically the "avs/audio"
service running on ADSP Q6.

Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Signed-off-by: Sibi Sankar <sibis@codeaurora.org>
Link: https://lore.kernel.org/r/20200312120842.21991-4-sibis@codeaurora.org
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>

authored by

Sibi Sankar and committed by
Bjorn Andersson
83473566 a03a5b63

+117 -10
+1
drivers/soc/qcom/Kconfig
··· 200 200 tristate "Qualcomm APR Bus (Asynchronous Packet Router)" 201 201 depends on ARCH_QCOM || COMPILE_TEST 202 202 depends on RPMSG 203 + select QCOM_PDR_HELPERS 203 204 help 204 205 Enable APR IPC protocol support between 205 206 application processor and QDSP6. APR is
+115 -10
drivers/soc/qcom/apr.c
··· 11 11 #include <linux/workqueue.h> 12 12 #include <linux/of_device.h> 13 13 #include <linux/soc/qcom/apr.h> 14 + #include <linux/soc/qcom/pdr.h> 14 15 #include <linux/rpmsg.h> 15 16 #include <linux/of.h> 16 17 ··· 22 21 spinlock_t rx_lock; 23 22 struct idr svcs_idr; 24 23 int dest_domain_id; 24 + struct pdr_handle *pdr; 25 25 struct workqueue_struct *rxwq; 26 26 struct work_struct rx_work; 27 27 struct list_head rx_list; ··· 291 289 id->svc_id + 1, GFP_ATOMIC); 292 290 spin_unlock(&apr->svcs_lock); 293 291 292 + of_property_read_string_index(np, "qcom,protection-domain", 293 + 1, &adev->service_path); 294 + 294 295 dev_info(dev, "Adding APR dev: %s\n", dev_name(&adev->dev)); 295 296 296 297 ret = device_register(&adev->dev); ··· 305 300 return ret; 306 301 } 307 302 308 - static void of_register_apr_devices(struct device *dev) 303 + static int of_apr_add_pd_lookups(struct device *dev) 304 + { 305 + const char *service_name, *service_path; 306 + struct apr *apr = dev_get_drvdata(dev); 307 + struct device_node *node; 308 + struct pdr_service *pds; 309 + int ret; 310 + 311 + for_each_child_of_node(dev->of_node, node) { 312 + ret = of_property_read_string_index(node, "qcom,protection-domain", 313 + 0, &service_name); 314 + if (ret < 0) 315 + continue; 316 + 317 + ret = of_property_read_string_index(node, "qcom,protection-domain", 318 + 1, &service_path); 319 + if (ret < 0) { 320 + dev_err(dev, "pdr service path missing: %d\n", ret); 321 + return ret; 322 + } 323 + 324 + pds = pdr_add_lookup(apr->pdr, service_name, service_path); 325 + if (IS_ERR(pds) && PTR_ERR(pds) != -EALREADY) { 326 + dev_err(dev, "pdr add lookup failed: %d\n", ret); 327 + return PTR_ERR(pds); 328 + } 329 + } 330 + 331 + return 0; 332 + } 333 + 334 + static void of_register_apr_devices(struct device *dev, const char *svc_path) 309 335 { 310 336 struct apr *apr = dev_get_drvdata(dev); 311 337 struct device_node *node; 338 + const char *service_path; 339 + int ret; 312 340 313 341 for_each_child_of_node(dev->of_node, node) { 314 342 struct apr_device_id id = { {0} }; 343 + 344 + /* 345 + * This function is called with svc_path NULL during 346 + * apr_probe(), in which case we register any apr devices 347 + * without a qcom,protection-domain specified. 348 + * 349 + * Then as the protection domains becomes available 350 + * (if applicable) this function is again called, but with 351 + * svc_path representing the service becoming available. In 352 + * this case we register any apr devices with a matching 353 + * qcom,protection-domain. 354 + */ 355 + 356 + ret = of_property_read_string_index(node, "qcom,protection-domain", 357 + 1, &service_path); 358 + if (svc_path) { 359 + /* skip APR services that are PD independent */ 360 + if (ret) 361 + continue; 362 + 363 + /* skip APR services whose PD paths don't match */ 364 + if (strcmp(service_path, svc_path)) 365 + continue; 366 + } else { 367 + /* skip APR services whose PD lookups are registered */ 368 + if (ret == 0) 369 + continue; 370 + } 315 371 316 372 if (of_property_read_u32(node, "reg", &id.svc_id)) 317 373 continue; ··· 381 315 382 316 if (apr_add_device(dev, node, &id)) 383 317 dev_err(dev, "Failed to add apr %d svc\n", id.svc_id); 318 + } 319 + } 320 + 321 + static int apr_remove_device(struct device *dev, void *svc_path) 322 + { 323 + struct apr_device *adev = to_apr_device(dev); 324 + 325 + if (svc_path && adev->service_path) { 326 + if (!strcmp(adev->service_path, (char *)svc_path)) 327 + device_unregister(&adev->dev); 328 + } else { 329 + device_unregister(&adev->dev); 330 + } 331 + 332 + return 0; 333 + } 334 + 335 + static void apr_pd_status(int state, char *svc_path, void *priv) 336 + { 337 + struct apr *apr = (struct apr *)priv; 338 + 339 + switch (state) { 340 + case SERVREG_SERVICE_STATE_UP: 341 + of_register_apr_devices(apr->dev, svc_path); 342 + break; 343 + case SERVREG_SERVICE_STATE_DOWN: 344 + device_for_each_child(apr->dev, svc_path, apr_remove_device); 345 + break; 384 346 } 385 347 } 386 348 ··· 437 343 return -ENOMEM; 438 344 } 439 345 INIT_WORK(&apr->rx_work, apr_rxwq); 346 + 347 + apr->pdr = pdr_handle_alloc(apr_pd_status, apr); 348 + if (IS_ERR(apr->pdr)) { 349 + dev_err(dev, "Failed to init PDR handle\n"); 350 + ret = PTR_ERR(apr->pdr); 351 + goto destroy_wq; 352 + } 353 + 440 354 INIT_LIST_HEAD(&apr->rx_list); 441 355 spin_lock_init(&apr->rx_lock); 442 356 spin_lock_init(&apr->svcs_lock); 443 357 idr_init(&apr->svcs_idr); 444 - of_register_apr_devices(dev); 358 + 359 + ret = of_apr_add_pd_lookups(dev); 360 + if (ret) 361 + goto handle_release; 362 + 363 + of_register_apr_devices(dev, NULL); 445 364 446 365 return 0; 447 - } 448 366 449 - static int apr_remove_device(struct device *dev, void *null) 450 - { 451 - struct apr_device *adev = to_apr_device(dev); 452 - 453 - device_unregister(&adev->dev); 454 - 455 - return 0; 367 + handle_release: 368 + pdr_handle_release(apr->pdr); 369 + destroy_wq: 370 + destroy_workqueue(apr->rxwq); 371 + return ret; 456 372 } 457 373 458 374 static void apr_remove(struct rpmsg_device *rpdev) 459 375 { 460 376 struct apr *apr = dev_get_drvdata(&rpdev->dev); 461 377 378 + pdr_handle_release(apr->pdr); 462 379 device_for_each_child(&rpdev->dev, NULL, apr_remove_device); 463 380 flush_workqueue(apr->rxwq); 464 381 destroy_workqueue(apr->rxwq);
+1
include/linux/soc/qcom/apr.h
··· 85 85 uint16_t domain_id; 86 86 uint32_t version; 87 87 char name[APR_NAME_SIZE]; 88 + const char *service_path; 88 89 spinlock_t lock; 89 90 struct list_head node; 90 91 };