162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. 362306a36Sopenharmony_ci// Copyright (c) 2018, Linaro Limited 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/kernel.h> 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/device.h> 862306a36Sopenharmony_ci#include <linux/spinlock.h> 962306a36Sopenharmony_ci#include <linux/idr.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/workqueue.h> 1262306a36Sopenharmony_ci#include <linux/of_device.h> 1362306a36Sopenharmony_ci#include <linux/soc/qcom/apr.h> 1462306a36Sopenharmony_ci#include <linux/soc/qcom/pdr.h> 1562306a36Sopenharmony_ci#include <linux/rpmsg.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cienum { 1962306a36Sopenharmony_ci PR_TYPE_APR = 0, 2062306a36Sopenharmony_ci PR_TYPE_GPR, 2162306a36Sopenharmony_ci}; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* Some random values tbh which does not collide with static modules */ 2462306a36Sopenharmony_ci#define GPR_DYNAMIC_PORT_START 0x10000000 2562306a36Sopenharmony_ci#define GPR_DYNAMIC_PORT_END 0x20000000 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct packet_router { 2862306a36Sopenharmony_ci struct rpmsg_endpoint *ch; 2962306a36Sopenharmony_ci struct device *dev; 3062306a36Sopenharmony_ci spinlock_t svcs_lock; 3162306a36Sopenharmony_ci spinlock_t rx_lock; 3262306a36Sopenharmony_ci struct idr svcs_idr; 3362306a36Sopenharmony_ci int dest_domain_id; 3462306a36Sopenharmony_ci int type; 3562306a36Sopenharmony_ci struct pdr_handle *pdr; 3662306a36Sopenharmony_ci struct workqueue_struct *rxwq; 3762306a36Sopenharmony_ci struct work_struct rx_work; 3862306a36Sopenharmony_ci struct list_head rx_list; 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistruct apr_rx_buf { 4262306a36Sopenharmony_ci struct list_head node; 4362306a36Sopenharmony_ci int len; 4462306a36Sopenharmony_ci uint8_t buf[]; 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/** 4862306a36Sopenharmony_ci * apr_send_pkt() - Send a apr message from apr device 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * @adev: Pointer to previously registered apr device. 5162306a36Sopenharmony_ci * @pkt: Pointer to apr packet to send 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * Return: Will be an negative on packet size on success. 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ciint apr_send_pkt(struct apr_device *adev, struct apr_pkt *pkt) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci struct packet_router *apr = dev_get_drvdata(adev->dev.parent); 5862306a36Sopenharmony_ci struct apr_hdr *hdr; 5962306a36Sopenharmony_ci unsigned long flags; 6062306a36Sopenharmony_ci int ret; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci spin_lock_irqsave(&adev->svc.lock, flags); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci hdr = &pkt->hdr; 6562306a36Sopenharmony_ci hdr->src_domain = APR_DOMAIN_APPS; 6662306a36Sopenharmony_ci hdr->src_svc = adev->svc.id; 6762306a36Sopenharmony_ci hdr->dest_domain = adev->domain_id; 6862306a36Sopenharmony_ci hdr->dest_svc = adev->svc.id; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci ret = rpmsg_trysend(apr->ch, pkt, hdr->pkt_size); 7162306a36Sopenharmony_ci spin_unlock_irqrestore(&adev->svc.lock, flags); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci return ret ? ret : hdr->pkt_size; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(apr_send_pkt); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_civoid gpr_free_port(gpr_port_t *port) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct packet_router *gpr = port->pr; 8062306a36Sopenharmony_ci unsigned long flags; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci spin_lock_irqsave(&gpr->svcs_lock, flags); 8362306a36Sopenharmony_ci idr_remove(&gpr->svcs_idr, port->id); 8462306a36Sopenharmony_ci spin_unlock_irqrestore(&gpr->svcs_lock, flags); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci kfree(port); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(gpr_free_port); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cigpr_port_t *gpr_alloc_port(struct apr_device *gdev, struct device *dev, 9162306a36Sopenharmony_ci gpr_port_cb cb, void *priv) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct packet_router *pr = dev_get_drvdata(gdev->dev.parent); 9462306a36Sopenharmony_ci gpr_port_t *port; 9562306a36Sopenharmony_ci struct pkt_router_svc *svc; 9662306a36Sopenharmony_ci int id; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci port = kzalloc(sizeof(*port), GFP_KERNEL); 9962306a36Sopenharmony_ci if (!port) 10062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci svc = port; 10362306a36Sopenharmony_ci svc->callback = cb; 10462306a36Sopenharmony_ci svc->pr = pr; 10562306a36Sopenharmony_ci svc->priv = priv; 10662306a36Sopenharmony_ci svc->dev = dev; 10762306a36Sopenharmony_ci spin_lock_init(&svc->lock); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci spin_lock(&pr->svcs_lock); 11062306a36Sopenharmony_ci id = idr_alloc_cyclic(&pr->svcs_idr, svc, GPR_DYNAMIC_PORT_START, 11162306a36Sopenharmony_ci GPR_DYNAMIC_PORT_END, GFP_ATOMIC); 11262306a36Sopenharmony_ci if (id < 0) { 11362306a36Sopenharmony_ci dev_err(dev, "Unable to allocate dynamic GPR src port\n"); 11462306a36Sopenharmony_ci kfree(port); 11562306a36Sopenharmony_ci spin_unlock(&pr->svcs_lock); 11662306a36Sopenharmony_ci return ERR_PTR(id); 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci svc->id = id; 12062306a36Sopenharmony_ci spin_unlock(&pr->svcs_lock); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return port; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(gpr_alloc_port); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int pkt_router_send_svc_pkt(struct pkt_router_svc *svc, struct gpr_pkt *pkt) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci struct packet_router *pr = svc->pr; 12962306a36Sopenharmony_ci struct gpr_hdr *hdr; 13062306a36Sopenharmony_ci unsigned long flags; 13162306a36Sopenharmony_ci int ret; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci hdr = &pkt->hdr; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci spin_lock_irqsave(&svc->lock, flags); 13662306a36Sopenharmony_ci ret = rpmsg_trysend(pr->ch, pkt, hdr->pkt_size); 13762306a36Sopenharmony_ci spin_unlock_irqrestore(&svc->lock, flags); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci return ret ? ret : hdr->pkt_size; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ciint gpr_send_pkt(struct apr_device *gdev, struct gpr_pkt *pkt) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci return pkt_router_send_svc_pkt(&gdev->svc, pkt); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(gpr_send_pkt); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ciint gpr_send_port_pkt(gpr_port_t *port, struct gpr_pkt *pkt) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci return pkt_router_send_svc_pkt(port, pkt); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(gpr_send_port_pkt); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void apr_dev_release(struct device *dev) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct apr_device *adev = to_apr_device(dev); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci kfree(adev); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic int apr_callback(struct rpmsg_device *rpdev, void *buf, 16262306a36Sopenharmony_ci int len, void *priv, u32 addr) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci struct packet_router *apr = dev_get_drvdata(&rpdev->dev); 16562306a36Sopenharmony_ci struct apr_rx_buf *abuf; 16662306a36Sopenharmony_ci unsigned long flags; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (len <= APR_HDR_SIZE) { 16962306a36Sopenharmony_ci dev_err(apr->dev, "APR: Improper apr pkt received:%p %d\n", 17062306a36Sopenharmony_ci buf, len); 17162306a36Sopenharmony_ci return -EINVAL; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci abuf = kzalloc(sizeof(*abuf) + len, GFP_ATOMIC); 17562306a36Sopenharmony_ci if (!abuf) 17662306a36Sopenharmony_ci return -ENOMEM; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci abuf->len = len; 17962306a36Sopenharmony_ci memcpy(abuf->buf, buf, len); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci spin_lock_irqsave(&apr->rx_lock, flags); 18262306a36Sopenharmony_ci list_add_tail(&abuf->node, &apr->rx_list); 18362306a36Sopenharmony_ci spin_unlock_irqrestore(&apr->rx_lock, flags); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci queue_work(apr->rxwq, &apr->rx_work); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int apr_do_rx_callback(struct packet_router *apr, struct apr_rx_buf *abuf) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci uint16_t hdr_size, msg_type, ver, svc_id; 19362306a36Sopenharmony_ci struct pkt_router_svc *svc; 19462306a36Sopenharmony_ci struct apr_device *adev; 19562306a36Sopenharmony_ci struct apr_driver *adrv = NULL; 19662306a36Sopenharmony_ci struct apr_resp_pkt resp; 19762306a36Sopenharmony_ci struct apr_hdr *hdr; 19862306a36Sopenharmony_ci unsigned long flags; 19962306a36Sopenharmony_ci void *buf = abuf->buf; 20062306a36Sopenharmony_ci int len = abuf->len; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci hdr = buf; 20362306a36Sopenharmony_ci ver = APR_HDR_FIELD_VER(hdr->hdr_field); 20462306a36Sopenharmony_ci if (ver > APR_PKT_VER + 1) 20562306a36Sopenharmony_ci return -EINVAL; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci hdr_size = APR_HDR_FIELD_SIZE_BYTES(hdr->hdr_field); 20862306a36Sopenharmony_ci if (hdr_size < APR_HDR_SIZE) { 20962306a36Sopenharmony_ci dev_err(apr->dev, "APR: Wrong hdr size:%d\n", hdr_size); 21062306a36Sopenharmony_ci return -EINVAL; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (hdr->pkt_size < APR_HDR_SIZE || hdr->pkt_size != len) { 21462306a36Sopenharmony_ci dev_err(apr->dev, "APR: Wrong packet size\n"); 21562306a36Sopenharmony_ci return -EINVAL; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci msg_type = APR_HDR_FIELD_MT(hdr->hdr_field); 21962306a36Sopenharmony_ci if (msg_type >= APR_MSG_TYPE_MAX) { 22062306a36Sopenharmony_ci dev_err(apr->dev, "APR: Wrong message type: %d\n", msg_type); 22162306a36Sopenharmony_ci return -EINVAL; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (hdr->src_domain >= APR_DOMAIN_MAX || 22562306a36Sopenharmony_ci hdr->dest_domain >= APR_DOMAIN_MAX || 22662306a36Sopenharmony_ci hdr->src_svc >= APR_SVC_MAX || 22762306a36Sopenharmony_ci hdr->dest_svc >= APR_SVC_MAX) { 22862306a36Sopenharmony_ci dev_err(apr->dev, "APR: Wrong APR header\n"); 22962306a36Sopenharmony_ci return -EINVAL; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci svc_id = hdr->dest_svc; 23362306a36Sopenharmony_ci spin_lock_irqsave(&apr->svcs_lock, flags); 23462306a36Sopenharmony_ci svc = idr_find(&apr->svcs_idr, svc_id); 23562306a36Sopenharmony_ci if (svc && svc->dev->driver) { 23662306a36Sopenharmony_ci adev = svc_to_apr_device(svc); 23762306a36Sopenharmony_ci adrv = to_apr_driver(adev->dev.driver); 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci spin_unlock_irqrestore(&apr->svcs_lock, flags); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (!adrv || !adev) { 24262306a36Sopenharmony_ci dev_err(apr->dev, "APR: service is not registered (%d)\n", 24362306a36Sopenharmony_ci svc_id); 24462306a36Sopenharmony_ci return -EINVAL; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci resp.hdr = *hdr; 24862306a36Sopenharmony_ci resp.payload_size = hdr->pkt_size - hdr_size; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* 25162306a36Sopenharmony_ci * NOTE: hdr_size is not same as APR_HDR_SIZE as remote can include 25262306a36Sopenharmony_ci * optional headers in to apr_hdr which should be ignored 25362306a36Sopenharmony_ci */ 25462306a36Sopenharmony_ci if (resp.payload_size > 0) 25562306a36Sopenharmony_ci resp.payload = buf + hdr_size; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci adrv->callback(adev, &resp); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci return 0; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic int gpr_do_rx_callback(struct packet_router *gpr, struct apr_rx_buf *abuf) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci uint16_t hdr_size, ver; 26562306a36Sopenharmony_ci struct pkt_router_svc *svc = NULL; 26662306a36Sopenharmony_ci struct gpr_resp_pkt resp; 26762306a36Sopenharmony_ci struct gpr_hdr *hdr; 26862306a36Sopenharmony_ci unsigned long flags; 26962306a36Sopenharmony_ci void *buf = abuf->buf; 27062306a36Sopenharmony_ci int len = abuf->len; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci hdr = buf; 27362306a36Sopenharmony_ci ver = hdr->version; 27462306a36Sopenharmony_ci if (ver > GPR_PKT_VER + 1) 27562306a36Sopenharmony_ci return -EINVAL; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci hdr_size = hdr->hdr_size; 27862306a36Sopenharmony_ci if (hdr_size < GPR_PKT_HEADER_WORD_SIZE) { 27962306a36Sopenharmony_ci dev_err(gpr->dev, "GPR: Wrong hdr size:%d\n", hdr_size); 28062306a36Sopenharmony_ci return -EINVAL; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (hdr->pkt_size < GPR_PKT_HEADER_BYTE_SIZE || hdr->pkt_size != len) { 28462306a36Sopenharmony_ci dev_err(gpr->dev, "GPR: Wrong packet size\n"); 28562306a36Sopenharmony_ci return -EINVAL; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci resp.hdr = *hdr; 28962306a36Sopenharmony_ci resp.payload_size = hdr->pkt_size - (hdr_size * 4); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* 29262306a36Sopenharmony_ci * NOTE: hdr_size is not same as GPR_HDR_SIZE as remote can include 29362306a36Sopenharmony_ci * optional headers in to gpr_hdr which should be ignored 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci if (resp.payload_size > 0) 29662306a36Sopenharmony_ci resp.payload = buf + (hdr_size * 4); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci spin_lock_irqsave(&gpr->svcs_lock, flags); 30062306a36Sopenharmony_ci svc = idr_find(&gpr->svcs_idr, hdr->dest_port); 30162306a36Sopenharmony_ci spin_unlock_irqrestore(&gpr->svcs_lock, flags); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (!svc) { 30462306a36Sopenharmony_ci dev_err(gpr->dev, "GPR: Port(%x) is not registered\n", 30562306a36Sopenharmony_ci hdr->dest_port); 30662306a36Sopenharmony_ci return -EINVAL; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (svc->callback) 31062306a36Sopenharmony_ci svc->callback(&resp, svc->priv, 0); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return 0; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic void apr_rxwq(struct work_struct *work) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct packet_router *apr = container_of(work, struct packet_router, rx_work); 31862306a36Sopenharmony_ci struct apr_rx_buf *abuf, *b; 31962306a36Sopenharmony_ci unsigned long flags; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (!list_empty(&apr->rx_list)) { 32262306a36Sopenharmony_ci list_for_each_entry_safe(abuf, b, &apr->rx_list, node) { 32362306a36Sopenharmony_ci switch (apr->type) { 32462306a36Sopenharmony_ci case PR_TYPE_APR: 32562306a36Sopenharmony_ci apr_do_rx_callback(apr, abuf); 32662306a36Sopenharmony_ci break; 32762306a36Sopenharmony_ci case PR_TYPE_GPR: 32862306a36Sopenharmony_ci gpr_do_rx_callback(apr, abuf); 32962306a36Sopenharmony_ci break; 33062306a36Sopenharmony_ci default: 33162306a36Sopenharmony_ci break; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci spin_lock_irqsave(&apr->rx_lock, flags); 33462306a36Sopenharmony_ci list_del(&abuf->node); 33562306a36Sopenharmony_ci spin_unlock_irqrestore(&apr->rx_lock, flags); 33662306a36Sopenharmony_ci kfree(abuf); 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic int apr_device_match(struct device *dev, struct device_driver *drv) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci struct apr_device *adev = to_apr_device(dev); 34462306a36Sopenharmony_ci struct apr_driver *adrv = to_apr_driver(drv); 34562306a36Sopenharmony_ci const struct apr_device_id *id = adrv->id_table; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci /* Attempt an OF style match first */ 34862306a36Sopenharmony_ci if (of_driver_match_device(dev, drv)) 34962306a36Sopenharmony_ci return 1; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (!id) 35262306a36Sopenharmony_ci return 0; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci while (id->domain_id != 0 || id->svc_id != 0) { 35562306a36Sopenharmony_ci if (id->domain_id == adev->domain_id && 35662306a36Sopenharmony_ci id->svc_id == adev->svc.id) 35762306a36Sopenharmony_ci return 1; 35862306a36Sopenharmony_ci id++; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci return 0; 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic int apr_device_probe(struct device *dev) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct apr_device *adev = to_apr_device(dev); 36762306a36Sopenharmony_ci struct apr_driver *adrv = to_apr_driver(dev->driver); 36862306a36Sopenharmony_ci int ret; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci ret = adrv->probe(adev); 37162306a36Sopenharmony_ci if (!ret) 37262306a36Sopenharmony_ci adev->svc.callback = adrv->gpr_callback; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci return ret; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic void apr_device_remove(struct device *dev) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct apr_device *adev = to_apr_device(dev); 38062306a36Sopenharmony_ci struct apr_driver *adrv = to_apr_driver(dev->driver); 38162306a36Sopenharmony_ci struct packet_router *apr = dev_get_drvdata(adev->dev.parent); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (adrv->remove) 38462306a36Sopenharmony_ci adrv->remove(adev); 38562306a36Sopenharmony_ci spin_lock(&apr->svcs_lock); 38662306a36Sopenharmony_ci idr_remove(&apr->svcs_idr, adev->svc.id); 38762306a36Sopenharmony_ci spin_unlock(&apr->svcs_lock); 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic int apr_uevent(const struct device *dev, struct kobj_uevent_env *env) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci const struct apr_device *adev = to_apr_device(dev); 39362306a36Sopenharmony_ci int ret; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci ret = of_device_uevent_modalias(dev, env); 39662306a36Sopenharmony_ci if (ret != -ENODEV) 39762306a36Sopenharmony_ci return ret; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci return add_uevent_var(env, "MODALIAS=apr:%s", adev->name); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistruct bus_type aprbus = { 40362306a36Sopenharmony_ci .name = "aprbus", 40462306a36Sopenharmony_ci .match = apr_device_match, 40562306a36Sopenharmony_ci .probe = apr_device_probe, 40662306a36Sopenharmony_ci .uevent = apr_uevent, 40762306a36Sopenharmony_ci .remove = apr_device_remove, 40862306a36Sopenharmony_ci}; 40962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(aprbus); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic int apr_add_device(struct device *dev, struct device_node *np, 41262306a36Sopenharmony_ci u32 svc_id, u32 domain_id) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct packet_router *apr = dev_get_drvdata(dev); 41562306a36Sopenharmony_ci struct apr_device *adev = NULL; 41662306a36Sopenharmony_ci struct pkt_router_svc *svc; 41762306a36Sopenharmony_ci int ret; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci adev = kzalloc(sizeof(*adev), GFP_KERNEL); 42062306a36Sopenharmony_ci if (!adev) 42162306a36Sopenharmony_ci return -ENOMEM; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci adev->svc_id = svc_id; 42462306a36Sopenharmony_ci svc = &adev->svc; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci svc->id = svc_id; 42762306a36Sopenharmony_ci svc->pr = apr; 42862306a36Sopenharmony_ci svc->priv = adev; 42962306a36Sopenharmony_ci svc->dev = dev; 43062306a36Sopenharmony_ci spin_lock_init(&svc->lock); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci adev->domain_id = domain_id; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (np) 43562306a36Sopenharmony_ci snprintf(adev->name, APR_NAME_SIZE, "%pOFn", np); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci switch (apr->type) { 43862306a36Sopenharmony_ci case PR_TYPE_APR: 43962306a36Sopenharmony_ci dev_set_name(&adev->dev, "aprsvc:%s:%x:%x", adev->name, 44062306a36Sopenharmony_ci domain_id, svc_id); 44162306a36Sopenharmony_ci break; 44262306a36Sopenharmony_ci case PR_TYPE_GPR: 44362306a36Sopenharmony_ci dev_set_name(&adev->dev, "gprsvc:%s:%x:%x", adev->name, 44462306a36Sopenharmony_ci domain_id, svc_id); 44562306a36Sopenharmony_ci break; 44662306a36Sopenharmony_ci default: 44762306a36Sopenharmony_ci break; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci adev->dev.bus = &aprbus; 45162306a36Sopenharmony_ci adev->dev.parent = dev; 45262306a36Sopenharmony_ci adev->dev.of_node = np; 45362306a36Sopenharmony_ci adev->dev.release = apr_dev_release; 45462306a36Sopenharmony_ci adev->dev.driver = NULL; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci spin_lock(&apr->svcs_lock); 45762306a36Sopenharmony_ci ret = idr_alloc(&apr->svcs_idr, svc, svc_id, svc_id + 1, GFP_ATOMIC); 45862306a36Sopenharmony_ci spin_unlock(&apr->svcs_lock); 45962306a36Sopenharmony_ci if (ret < 0) { 46062306a36Sopenharmony_ci dev_err(dev, "idr_alloc failed: %d\n", ret); 46162306a36Sopenharmony_ci goto out; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* Protection domain is optional, it does not exist on older platforms */ 46562306a36Sopenharmony_ci ret = of_property_read_string_index(np, "qcom,protection-domain", 46662306a36Sopenharmony_ci 1, &adev->service_path); 46762306a36Sopenharmony_ci if (ret < 0 && ret != -EINVAL) { 46862306a36Sopenharmony_ci dev_err(dev, "Failed to read second value of qcom,protection-domain\n"); 46962306a36Sopenharmony_ci goto out; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci dev_info(dev, "Adding APR/GPR dev: %s\n", dev_name(&adev->dev)); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci ret = device_register(&adev->dev); 47562306a36Sopenharmony_ci if (ret) { 47662306a36Sopenharmony_ci dev_err(dev, "device_register failed: %d\n", ret); 47762306a36Sopenharmony_ci put_device(&adev->dev); 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ciout: 48162306a36Sopenharmony_ci return ret; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic int of_apr_add_pd_lookups(struct device *dev) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci const char *service_name, *service_path; 48762306a36Sopenharmony_ci struct packet_router *apr = dev_get_drvdata(dev); 48862306a36Sopenharmony_ci struct device_node *node; 48962306a36Sopenharmony_ci struct pdr_service *pds; 49062306a36Sopenharmony_ci int ret; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci for_each_child_of_node(dev->of_node, node) { 49362306a36Sopenharmony_ci ret = of_property_read_string_index(node, "qcom,protection-domain", 49462306a36Sopenharmony_ci 0, &service_name); 49562306a36Sopenharmony_ci if (ret < 0) 49662306a36Sopenharmony_ci continue; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci ret = of_property_read_string_index(node, "qcom,protection-domain", 49962306a36Sopenharmony_ci 1, &service_path); 50062306a36Sopenharmony_ci if (ret < 0) { 50162306a36Sopenharmony_ci dev_err(dev, "pdr service path missing: %d\n", ret); 50262306a36Sopenharmony_ci of_node_put(node); 50362306a36Sopenharmony_ci return ret; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci pds = pdr_add_lookup(apr->pdr, service_name, service_path); 50762306a36Sopenharmony_ci if (IS_ERR(pds) && PTR_ERR(pds) != -EALREADY) { 50862306a36Sopenharmony_ci dev_err(dev, "pdr add lookup failed: %ld\n", PTR_ERR(pds)); 50962306a36Sopenharmony_ci of_node_put(node); 51062306a36Sopenharmony_ci return PTR_ERR(pds); 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci return 0; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic void of_register_apr_devices(struct device *dev, const char *svc_path) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci struct packet_router *apr = dev_get_drvdata(dev); 52062306a36Sopenharmony_ci struct device_node *node; 52162306a36Sopenharmony_ci const char *service_path; 52262306a36Sopenharmony_ci int ret; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci for_each_child_of_node(dev->of_node, node) { 52562306a36Sopenharmony_ci u32 svc_id; 52662306a36Sopenharmony_ci u32 domain_id; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci /* 52962306a36Sopenharmony_ci * This function is called with svc_path NULL during 53062306a36Sopenharmony_ci * apr_probe(), in which case we register any apr devices 53162306a36Sopenharmony_ci * without a qcom,protection-domain specified. 53262306a36Sopenharmony_ci * 53362306a36Sopenharmony_ci * Then as the protection domains becomes available 53462306a36Sopenharmony_ci * (if applicable) this function is again called, but with 53562306a36Sopenharmony_ci * svc_path representing the service becoming available. In 53662306a36Sopenharmony_ci * this case we register any apr devices with a matching 53762306a36Sopenharmony_ci * qcom,protection-domain. 53862306a36Sopenharmony_ci */ 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci ret = of_property_read_string_index(node, "qcom,protection-domain", 54162306a36Sopenharmony_ci 1, &service_path); 54262306a36Sopenharmony_ci if (svc_path) { 54362306a36Sopenharmony_ci /* skip APR services that are PD independent */ 54462306a36Sopenharmony_ci if (ret) 54562306a36Sopenharmony_ci continue; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* skip APR services whose PD paths don't match */ 54862306a36Sopenharmony_ci if (strcmp(service_path, svc_path)) 54962306a36Sopenharmony_ci continue; 55062306a36Sopenharmony_ci } else { 55162306a36Sopenharmony_ci /* skip APR services whose PD lookups are registered */ 55262306a36Sopenharmony_ci if (ret == 0) 55362306a36Sopenharmony_ci continue; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (of_property_read_u32(node, "reg", &svc_id)) 55762306a36Sopenharmony_ci continue; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci domain_id = apr->dest_domain_id; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci if (apr_add_device(dev, node, svc_id, domain_id)) 56262306a36Sopenharmony_ci dev_err(dev, "Failed to add apr %d svc\n", svc_id); 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic int apr_remove_device(struct device *dev, void *svc_path) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci struct apr_device *adev = to_apr_device(dev); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci if (svc_path && adev->service_path) { 57162306a36Sopenharmony_ci if (!strcmp(adev->service_path, (char *)svc_path)) 57262306a36Sopenharmony_ci device_unregister(&adev->dev); 57362306a36Sopenharmony_ci } else { 57462306a36Sopenharmony_ci device_unregister(&adev->dev); 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci return 0; 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cistatic void apr_pd_status(int state, char *svc_path, void *priv) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci struct packet_router *apr = (struct packet_router *)priv; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci switch (state) { 58562306a36Sopenharmony_ci case SERVREG_SERVICE_STATE_UP: 58662306a36Sopenharmony_ci of_register_apr_devices(apr->dev, svc_path); 58762306a36Sopenharmony_ci break; 58862306a36Sopenharmony_ci case SERVREG_SERVICE_STATE_DOWN: 58962306a36Sopenharmony_ci device_for_each_child(apr->dev, svc_path, apr_remove_device); 59062306a36Sopenharmony_ci break; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_cistatic int apr_probe(struct rpmsg_device *rpdev) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci struct device *dev = &rpdev->dev; 59762306a36Sopenharmony_ci struct packet_router *apr; 59862306a36Sopenharmony_ci int ret; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci apr = devm_kzalloc(dev, sizeof(*apr), GFP_KERNEL); 60162306a36Sopenharmony_ci if (!apr) 60262306a36Sopenharmony_ci return -ENOMEM; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci ret = of_property_read_u32(dev->of_node, "qcom,domain", &apr->dest_domain_id); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci if (of_device_is_compatible(dev->of_node, "qcom,gpr")) { 60762306a36Sopenharmony_ci apr->type = PR_TYPE_GPR; 60862306a36Sopenharmony_ci } else { 60962306a36Sopenharmony_ci if (ret) /* try deprecated apr-domain property */ 61062306a36Sopenharmony_ci ret = of_property_read_u32(dev->of_node, "qcom,apr-domain", 61162306a36Sopenharmony_ci &apr->dest_domain_id); 61262306a36Sopenharmony_ci apr->type = PR_TYPE_APR; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (ret) { 61662306a36Sopenharmony_ci dev_err(dev, "Domain ID not specified in DT\n"); 61762306a36Sopenharmony_ci return ret; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci dev_set_drvdata(dev, apr); 62162306a36Sopenharmony_ci apr->ch = rpdev->ept; 62262306a36Sopenharmony_ci apr->dev = dev; 62362306a36Sopenharmony_ci apr->rxwq = create_singlethread_workqueue("qcom_apr_rx"); 62462306a36Sopenharmony_ci if (!apr->rxwq) { 62562306a36Sopenharmony_ci dev_err(apr->dev, "Failed to start Rx WQ\n"); 62662306a36Sopenharmony_ci return -ENOMEM; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci INIT_WORK(&apr->rx_work, apr_rxwq); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci apr->pdr = pdr_handle_alloc(apr_pd_status, apr); 63162306a36Sopenharmony_ci if (IS_ERR(apr->pdr)) { 63262306a36Sopenharmony_ci dev_err(dev, "Failed to init PDR handle\n"); 63362306a36Sopenharmony_ci ret = PTR_ERR(apr->pdr); 63462306a36Sopenharmony_ci goto destroy_wq; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci INIT_LIST_HEAD(&apr->rx_list); 63862306a36Sopenharmony_ci spin_lock_init(&apr->rx_lock); 63962306a36Sopenharmony_ci spin_lock_init(&apr->svcs_lock); 64062306a36Sopenharmony_ci idr_init(&apr->svcs_idr); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci ret = of_apr_add_pd_lookups(dev); 64362306a36Sopenharmony_ci if (ret) 64462306a36Sopenharmony_ci goto handle_release; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci of_register_apr_devices(dev, NULL); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci return 0; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cihandle_release: 65162306a36Sopenharmony_ci pdr_handle_release(apr->pdr); 65262306a36Sopenharmony_cidestroy_wq: 65362306a36Sopenharmony_ci destroy_workqueue(apr->rxwq); 65462306a36Sopenharmony_ci return ret; 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_cistatic void apr_remove(struct rpmsg_device *rpdev) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci struct packet_router *apr = dev_get_drvdata(&rpdev->dev); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci pdr_handle_release(apr->pdr); 66262306a36Sopenharmony_ci device_for_each_child(&rpdev->dev, NULL, apr_remove_device); 66362306a36Sopenharmony_ci destroy_workqueue(apr->rxwq); 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci/* 66762306a36Sopenharmony_ci * __apr_driver_register() - Client driver registration with aprbus 66862306a36Sopenharmony_ci * 66962306a36Sopenharmony_ci * @drv:Client driver to be associated with client-device. 67062306a36Sopenharmony_ci * @owner: owning module/driver 67162306a36Sopenharmony_ci * 67262306a36Sopenharmony_ci * This API will register the client driver with the aprbus 67362306a36Sopenharmony_ci * It is called from the driver's module-init function. 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_ciint __apr_driver_register(struct apr_driver *drv, struct module *owner) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci drv->driver.bus = &aprbus; 67862306a36Sopenharmony_ci drv->driver.owner = owner; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci return driver_register(&drv->driver); 68162306a36Sopenharmony_ci} 68262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__apr_driver_register); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci/* 68562306a36Sopenharmony_ci * apr_driver_unregister() - Undo effect of apr_driver_register 68662306a36Sopenharmony_ci * 68762306a36Sopenharmony_ci * @drv: Client driver to be unregistered 68862306a36Sopenharmony_ci */ 68962306a36Sopenharmony_civoid apr_driver_unregister(struct apr_driver *drv) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci driver_unregister(&drv->driver); 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(apr_driver_unregister); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic const struct of_device_id pkt_router_of_match[] = { 69662306a36Sopenharmony_ci { .compatible = "qcom,apr"}, 69762306a36Sopenharmony_ci { .compatible = "qcom,apr-v2"}, 69862306a36Sopenharmony_ci { .compatible = "qcom,gpr"}, 69962306a36Sopenharmony_ci {} 70062306a36Sopenharmony_ci}; 70162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, pkt_router_of_match); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_cistatic struct rpmsg_driver packet_router_driver = { 70462306a36Sopenharmony_ci .probe = apr_probe, 70562306a36Sopenharmony_ci .remove = apr_remove, 70662306a36Sopenharmony_ci .callback = apr_callback, 70762306a36Sopenharmony_ci .drv = { 70862306a36Sopenharmony_ci .name = "qcom,apr", 70962306a36Sopenharmony_ci .of_match_table = pkt_router_of_match, 71062306a36Sopenharmony_ci }, 71162306a36Sopenharmony_ci}; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic int __init apr_init(void) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci int ret; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci ret = bus_register(&aprbus); 71862306a36Sopenharmony_ci if (!ret) 71962306a36Sopenharmony_ci ret = register_rpmsg_driver(&packet_router_driver); 72062306a36Sopenharmony_ci else 72162306a36Sopenharmony_ci bus_unregister(&aprbus); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci return ret; 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic void __exit apr_exit(void) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci bus_unregister(&aprbus); 72962306a36Sopenharmony_ci unregister_rpmsg_driver(&packet_router_driver); 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cisubsys_initcall(apr_init); 73362306a36Sopenharmony_cimodule_exit(apr_exit); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 73662306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm APR Bus"); 737