enum {
        PR_TYPE_APR = 0,
+       PR_TYPE_GPR,
 };
 
+/* Some random values tbh which does not collide with static modules */
+#define GPR_DYNAMIC_PORT_START 0x10000000
+#define GPR_DYNAMIC_PORT_END   0x20000000
+
 struct packet_router {
        struct rpmsg_endpoint *ch;
        struct device *dev;
 }
 EXPORT_SYMBOL_GPL(apr_send_pkt);
 
+void gpr_free_port(gpr_port_t *port)
+{
+       struct packet_router *gpr = port->pr;
+       unsigned long flags;
+
+       spin_lock_irqsave(&gpr->svcs_lock, flags);
+       idr_remove(&gpr->svcs_idr, port->id);
+       spin_unlock_irqrestore(&gpr->svcs_lock, flags);
+
+       kfree(port);
+}
+EXPORT_SYMBOL_GPL(gpr_free_port);
+
+gpr_port_t *gpr_alloc_port(struct apr_device *gdev, struct device *dev,
+                               gpr_port_cb cb, void *priv)
+{
+       struct packet_router *pr = dev_get_drvdata(gdev->dev.parent);
+       gpr_port_t *port;
+       struct pkt_router_svc *svc;
+       int id;
+
+       port = kzalloc(sizeof(*port), GFP_KERNEL);
+       if (!port)
+               return ERR_PTR(-ENOMEM);
+
+       svc = port;
+       svc->callback = cb;
+       svc->pr = pr;
+       svc->priv = priv;
+       svc->dev = dev;
+       spin_lock_init(&svc->lock);
+
+       spin_lock(&pr->svcs_lock);
+       id = idr_alloc_cyclic(&pr->svcs_idr, svc, GPR_DYNAMIC_PORT_START,
+                             GPR_DYNAMIC_PORT_END, GFP_ATOMIC);
+       if (id < 0) {
+               dev_err(dev, "Unable to allocate dynamic GPR src port\n");
+               kfree(port);
+               spin_unlock(&pr->svcs_lock);
+               return ERR_PTR(id);
+       }
+
+       svc->id = id;
+       spin_unlock(&pr->svcs_lock);
+
+       return port;
+}
+EXPORT_SYMBOL_GPL(gpr_alloc_port);
+
+static int pkt_router_send_svc_pkt(struct pkt_router_svc *svc, struct gpr_pkt *pkt)
+{
+       struct packet_router *pr = svc->pr;
+       struct gpr_hdr *hdr;
+       unsigned long flags;
+       int ret;
+
+       hdr = &pkt->hdr;
+
+       spin_lock_irqsave(&svc->lock, flags);
+       ret = rpmsg_trysend(pr->ch, pkt, hdr->pkt_size);
+       spin_unlock_irqrestore(&svc->lock, flags);
+
+       return ret ? ret : hdr->pkt_size;
+}
+
+int gpr_send_pkt(struct apr_device *gdev, struct gpr_pkt *pkt)
+{
+       return pkt_router_send_svc_pkt(&gdev->svc, pkt);
+}
+EXPORT_SYMBOL_GPL(gpr_send_pkt);
+
+int gpr_send_port_pkt(gpr_port_t *port, struct gpr_pkt *pkt)
+{
+       return pkt_router_send_svc_pkt(port, pkt);
+}
+EXPORT_SYMBOL_GPL(gpr_send_port_pkt);
+
 static void apr_dev_release(struct device *dev)
 {
        struct apr_device *adev = to_apr_device(dev);
        return 0;
 }
 
+static int gpr_do_rx_callback(struct packet_router *gpr, struct apr_rx_buf *abuf)
+{
+       uint16_t hdr_size, ver;
+       struct pkt_router_svc *svc = NULL;
+       struct gpr_resp_pkt resp;
+       struct gpr_hdr *hdr;
+       unsigned long flags;
+       void *buf = abuf->buf;
+       int len = abuf->len;
+
+       hdr = buf;
+       ver = hdr->version;
+       if (ver > GPR_PKT_VER + 1)
+               return -EINVAL;
+
+       hdr_size = hdr->hdr_size;
+       if (hdr_size < GPR_PKT_HEADER_WORD_SIZE) {
+               dev_err(gpr->dev, "GPR: Wrong hdr size:%d\n", hdr_size);
+               return -EINVAL;
+       }
+
+       if (hdr->pkt_size < GPR_PKT_HEADER_BYTE_SIZE || hdr->pkt_size != len) {
+               dev_err(gpr->dev, "GPR: Wrong packet size\n");
+               return -EINVAL;
+       }
+
+       resp.hdr = *hdr;
+       resp.payload_size = hdr->pkt_size - (hdr_size * 4);
+
+       /*
+        * NOTE: hdr_size is not same as GPR_HDR_SIZE as remote can include
+        * optional headers in to gpr_hdr which should be ignored
+        */
+       if (resp.payload_size > 0)
+               resp.payload = buf + (hdr_size *  4);
+
+
+       spin_lock_irqsave(&gpr->svcs_lock, flags);
+       svc = idr_find(&gpr->svcs_idr, hdr->dest_port);
+       spin_unlock_irqrestore(&gpr->svcs_lock, flags);
+
+       if (!svc) {
+               dev_err(gpr->dev, "GPR: Port(%x) is not registered\n",
+                       hdr->dest_port);
+               return -EINVAL;
+       }
+
+       if (svc->callback)
+               svc->callback(&resp, svc->priv, 0);
+
+       return 0;
+}
+
 static void apr_rxwq(struct work_struct *work)
 {
        struct packet_router *apr = container_of(work, struct packet_router, rx_work);
                        case PR_TYPE_APR:
                                apr_do_rx_callback(apr, abuf);
                                break;
+                       case PR_TYPE_GPR:
+                               gpr_do_rx_callback(apr, abuf);
+                               break;
                        default:
                                break;
                        }
 {
        struct apr_device *adev = to_apr_device(dev);
        struct apr_driver *adrv = to_apr_driver(dev->driver);
+       int ret;
 
-       return adrv->probe(adev);
+       ret = adrv->probe(adev);
+       if (!ret)
+               adev->svc.callback = adrv->gpr_callback;
+
+       return ret;
 }
 
 static void apr_device_remove(struct device *dev)
                dev_set_name(&adev->dev, "aprsvc:%s:%x:%x", adev->name,
                             domain_id, svc_id);
                break;
+       case PR_TYPE_GPR:
+               dev_set_name(&adev->dev, "gprsvc:%s:%x:%x", adev->name,
+                            domain_id, svc_id);
+               break;
        default:
                break;
        }
        of_property_read_string_index(np, "qcom,protection-domain",
                                      1, &adev->service_path);
 
-       dev_info(dev, "Adding APR dev: %s\n", dev_name(&adev->dev));
+       dev_info(dev, "Adding APR/GPR dev: %s\n", dev_name(&adev->dev));
 
        ret = device_register(&adev->dev);
        if (ret) {
                return -ENOMEM;
 
        ret = of_property_read_u32(dev->of_node, "qcom,domain", &apr->dest_domain_id);
-       if (ret) /* try deprecated apr-domain property */
-               ret = of_property_read_u32(dev->of_node, "qcom,apr-domain",
-                                          &apr->dest_domain_id);
-       apr->type = PR_TYPE_APR;
+
+       if (of_device_is_compatible(dev->of_node, "qcom,gpr")) {
+               apr->type = PR_TYPE_GPR;
+       } else {
+               if (ret) /* try deprecated apr-domain property */
+                       ret = of_property_read_u32(dev->of_node, "qcom,apr-domain",
+                                                  &apr->dest_domain_id);
+               apr->type = PR_TYPE_APR;
+       }
+
        if (ret) {
                dev_err(dev, "Domain ID not specified in DT\n");
                return ret;
 static const struct of_device_id pkt_router_of_match[] = {
        { .compatible = "qcom,apr"},
        { .compatible = "qcom,apr-v2"},
+       { .compatible = "qcom,gpr"},
        {}
 };
 MODULE_DEVICE_TABLE(of, pkt_router_of_match);
 
 #include <linux/device.h>
 #include <linux/mod_devicetable.h>
 #include <dt-bindings/soc/qcom,apr.h>
+#include <dt-bindings/soc/qcom,gpr.h>
 
 extern struct bus_type aprbus;
 
        int payload_size;
 };
 
+struct gpr_hdr {
+       uint32_t version:4;
+       uint32_t hdr_size:4;
+       uint32_t pkt_size:24;
+       uint32_t dest_domain:8;
+       uint32_t src_domain:8;
+       uint32_t reserved:16;
+       uint32_t src_port;
+       uint32_t dest_port;
+       uint32_t token;
+       uint32_t opcode;
+} __packed;
+
+struct gpr_pkt {
+       struct gpr_hdr hdr;
+       uint32_t payload[];
+};
+
+struct gpr_resp_pkt {
+       struct gpr_hdr hdr;
+       void *payload;
+       int payload_size;
+};
+
+#define GPR_HDR_SIZE                   sizeof(struct gpr_hdr)
+#define GPR_PKT_VER                    0x0
+#define GPR_PKT_HEADER_WORD_SIZE       ((sizeof(struct gpr_pkt) + 3) >> 2)
+#define GPR_PKT_HEADER_BYTE_SIZE       (GPR_PKT_HEADER_WORD_SIZE << 2)
+
+#define GPR_BASIC_RSP_RESULT           0x02001005
+
+struct gpr_ibasic_rsp_result_t {
+       uint32_t opcode;
+       uint32_t status;
+};
+
+#define GPR_BASIC_EVT_ACCEPTED         0x02001006
+
+struct gpr_ibasic_rsp_accepted_t {
+       uint32_t opcode;
+};
+
 /* Bits 0 to 15 -- Minor version,  Bits 16 to 31 -- Major version */
 #define APR_SVC_MAJOR_VERSION(v)       ((v >> 16) & 0xFF)
 #define APR_SVC_MINOR_VERSION(v)       (v & 0xFF)
 
+typedef int (*gpr_port_cb) (struct gpr_resp_pkt *d, void *priv, int op);
 struct packet_router;
 struct pkt_router_svc {
        struct device *dev;
+       gpr_port_cb callback;
        struct packet_router *pr;
        spinlock_t lock;
        int id;
        void *priv;
 };
 
+typedef struct pkt_router_svc gpr_port_t;
+
 struct apr_device {
        struct device   dev;
        uint16_t        svc_id;
        struct list_head node;
 };
 
+typedef struct apr_device gpr_device_t;
+
 #define to_apr_device(d) container_of(d, struct apr_device, dev)
 #define svc_to_apr_device(d) container_of(d, struct apr_device, svc)
 
        int     (*remove)(struct apr_device *sl);
        int     (*callback)(struct apr_device *a,
                            struct apr_resp_pkt *d);
+       int     (*gpr_callback)(struct gpr_resp_pkt *d, void *data, int op);
        struct device_driver            driver;
        const struct apr_device_id      *id_table;
 };
 
+typedef struct apr_driver gpr_driver_t;
 #define to_apr_driver(d) container_of(d, struct apr_driver, driver)
 
 /*
 #define module_apr_driver(__apr_driver) \
        module_driver(__apr_driver, apr_driver_register, \
                        apr_driver_unregister)
+#define module_gpr_driver(__gpr_driver) module_apr_driver(__gpr_driver)
 
 int apr_send_pkt(struct apr_device *adev, struct apr_pkt *pkt);
 
+gpr_port_t *gpr_alloc_port(gpr_device_t *gdev, struct device *dev,
+                               gpr_port_cb cb, void *priv);
+void gpr_free_port(gpr_port_t *port);
+int gpr_send_port_pkt(gpr_port_t *port, struct gpr_pkt *pkt);
+int gpr_send_pkt(gpr_device_t *gdev, struct gpr_pkt *pkt);
+
 #endif /* __QCOM_APR_H_ */