summaryrefslogtreecommitdiff
path: root/drivers/sipc/sipc.c
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/sipc/sipc.c
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/sipc/sipc.c')
-rw-r--r--drivers/sipc/sipc.c488
1 files changed, 488 insertions, 0 deletions
diff --git a/drivers/sipc/sipc.c b/drivers/sipc/sipc.c
new file mode 100644
index 00000000..963d0a25
--- /dev/null
+++ b/drivers/sipc/sipc.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <soc/sprd/sci_glb_regs.h>
+
+#ifdef CONFIG_SPRD_MAILBOX
+#include <soc/sprd/mailbox.h>
+#endif
+
+#include <linux/sipc.h>
+#include <linux/sipc_priv.h>
+
+#define SMSG_TXBUF_ADDR (0)
+#define SMSG_TXBUF_SIZE (SZ_1K)
+#define SMSG_RXBUF_ADDR (SMSG_TXBUF_SIZE)
+#define SMSG_RXBUF_SIZE (SZ_1K)
+
+#define SMSG_RINGHDR (SMSG_TXBUF_SIZE + SMSG_RXBUF_SIZE)
+#define SMSG_TXBUF_RDPTR (SMSG_RINGHDR + 0)
+#define SMSG_TXBUF_WRPTR (SMSG_RINGHDR + 4)
+#define SMSG_RXBUF_RDPTR (SMSG_RINGHDR + 8)
+#define SMSG_RXBUF_WRPTR (SMSG_RINGHDR + 12)
+
+/* if it's upon mailbox arch, overwrite the implementation*/
+#ifdef CONFIG_SPRD_MAILBOX
+
+#define DEFINE_SIPC_RXIRQ_STATUS_FN(id) \
+static int sipc_rxirq_status##id(void) \
+{ \
+ return 1;\
+}
+
+#define DEFINE_SIPC_RXIRQ_CLEAR_FN(id) \
+static void sipc_rxirq_clear##id(void) \
+{ \
+ return;\
+}
+
+#define DEFINE_SIPC_TXIRQ_TRIGGER_FN(id) \
+static void sipc_txirq_trigger##id(void) \
+{ \
+ struct sipc_child_node_info *info = &sipc_dev->pdata->info_table[id];\
+ mbox_raw_sent(info->core_id, 0);\
+}
+
+#else
+
+#define DEFINE_SIPC_RXIRQ_STATUS_FN(id) \
+static int sipc_rxirq_status##id(void) \
+{ \
+ return 1;\
+}
+
+#define DEFINE_SIPC_RXIRQ_CLEAR_FN(id) \
+static void sipc_rxirq_clear##id(void) \
+{ \
+ struct sipc_child_node_info *info = &sipc_dev->pdata->info_table[id];\
+ __raw_writel(info->ap2cp_bit_clr, (volatile void *)info->cp2ap_int_ctrl);\
+}
+
+#define DEFINE_SIPC_TXIRQ_TRIGGER_FN(id) \
+static void sipc_txirq_trigger##id(void) \
+{ \
+ struct sipc_child_node_info *info = &sipc_dev->pdata->info_table[id];\
+ __raw_writel(info->ap2cp_bit_trig, (volatile void *)info->ap2cp_int_ctrl);\
+}
+
+#endif
+
+#define __GET_SIPC_FN_NAME(fn, id) fn##id
+
+#define __GET_SIPC_FN(fn, id) \
+({ \
+ void* ret; \
+ switch(id) { \
+ case 0: \
+ ret = __GET_SIPC_FN_NAME(fn, 0);\
+ break; \
+ case 1: \
+ ret = __GET_SIPC_FN_NAME(fn, 1);\
+ break; \
+ case 2: \
+ ret = __GET_SIPC_FN_NAME(fn, 2);\
+ break; \
+ default: \
+ break; \
+ } \
+ ret; \
+}) \
+
+#define GET_SIPC_RXIRQ_STATUS_FN(id) __GET_SIPC_FN(sipc_rxirq_status, id)
+#define GET_SIPC_RXIRQ_CLEAR_FN(id) __GET_SIPC_FN(sipc_rxirq_clear, id)
+#define GET_SIPC_TXIRQ_TRIGGER_FN(id) __GET_SIPC_FN(sipc_txirq_trigger, id)
+
+struct sipc_child_node_info {
+ uint8_t dst;
+ char * name;
+
+ uint32_t cp_base;
+ uint32_t cp_size;
+
+ uint32_t ring_base;
+ uint32_t ring_size;
+
+ uint32_t smem_base;
+ uint32_t smem_size;
+
+#ifdef CONFIG_SPRD_MAILBOX
+ uint32_t core_id;
+#else
+ uint32_t ap2cp_int_ctrl;
+ uint32_t cp2ap_int_ctrl;
+ uint32_t ap2cp_bit_trig;
+ uint32_t ap2cp_bit_clr;
+
+ uint32_t irq;
+#endif
+};
+
+struct sipc_init_data {
+ int is_alloc;
+ uint32_t chd_nr;
+ uint32_t smem_base;
+ uint32_t smem_size;
+ struct sipc_child_node_info info_table[0];
+};
+
+struct sipc_device {
+ int status;
+ uint32_t inst_nr;
+ struct sipc_init_data *pdata;
+ struct smsg_ipc *smsg_inst;
+};
+
+struct sipc_device *sipc_dev;
+
+DEFINE_SIPC_TXIRQ_TRIGGER_FN(0)
+DEFINE_SIPC_TXIRQ_TRIGGER_FN(1)
+DEFINE_SIPC_TXIRQ_TRIGGER_FN(2)
+
+DEFINE_SIPC_RXIRQ_CLEAR_FN(0)
+DEFINE_SIPC_RXIRQ_CLEAR_FN(1)
+DEFINE_SIPC_RXIRQ_CLEAR_FN(2)
+
+DEFINE_SIPC_RXIRQ_STATUS_FN(0)
+DEFINE_SIPC_RXIRQ_STATUS_FN(1)
+DEFINE_SIPC_RXIRQ_STATUS_FN(2)
+
+
+static int sipc_create(struct sipc_device *sipc)
+{
+ struct sipc_init_data *pdata = sipc->pdata;
+ struct smsg_ipc *inst;
+ struct sipc_child_node_info *info;
+ size_t base;
+ uint32_t num;
+ int ret, i;
+
+ if (!pdata) {
+ return -ENODEV;
+ }
+
+ num = pdata->chd_nr;
+ inst = sipc->smsg_inst;
+ info = pdata->info_table;
+
+ for (i = 0; i < num; i++) {
+ base = (size_t)ioremap_nocache((uint32_t)info[i].ring_base, info[i].ring_size);
+ if(base == NULL){
+ pr_info("sipc:[%d] ioremap return NULL\n");
+ return ENOMEM;
+ }
+ pr_info("sipc:[%d] after ioremap vbase=0x%lx, pbase=0x%x, size=0x%x\n",
+ i, base, info[i].ring_base, info[i].ring_size);
+ inst[i].txbuf_size = SMSG_TXBUF_SIZE / sizeof(struct smsg);
+ inst[i].txbuf_addr = base + SMSG_TXBUF_ADDR;
+ inst[i].txbuf_rdptr = base + SMSG_TXBUF_RDPTR;
+ inst[i].txbuf_wrptr = base + SMSG_TXBUF_WRPTR;
+
+ inst[i].rxbuf_size = SMSG_RXBUF_SIZE / sizeof(struct smsg);
+ inst[i].rxbuf_addr = base + SMSG_RXBUF_ADDR;
+ inst[i].rxbuf_rdptr = base + SMSG_RXBUF_RDPTR;
+ inst[i].rxbuf_wrptr = base + SMSG_RXBUF_WRPTR;
+
+ ret = smsg_ipc_create(inst[i].dst, &inst[i]);
+
+ pr_info("sipc:[%d] created\n", i);
+ if (ret) {
+ break;
+ }
+ }
+ return ret;
+}
+
+
+static int sipc_parse_dt(struct sipc_init_data **init, struct device *dev)
+{
+ struct device_node *np = dev->of_node, *nchd;
+ struct sipc_init_data *pdata;
+ struct sipc_child_node_info *info;
+ struct resource res;
+ uint32_t data, nr, int_ctrl_phy[2];
+ int ret, i;
+
+ nr = of_get_child_count(np);
+ if (nr == 0 || nr >= SIPC_ID_NR) {
+ return (!nr ? -ENODEV : -EINVAL);
+ }
+ pdata = kzalloc(sizeof(struct sipc_init_data) + nr*sizeof(struct sipc_child_node_info), GFP_KERNEL);
+ if (!pdata) {
+ pr_err("sipc: failed to alloc mem for pdata\n");
+ return -ENOMEM;
+ }
+
+ pdata->is_alloc = 1;
+ ret = of_address_to_resource(np, 0, &res);
+ if (ret) {
+ goto error;
+ }
+ pdata->chd_nr = nr;
+ pdata->smem_base = (uint32_t)res.start;
+ pdata->smem_size = (uint32_t)(res.end - res.start + 1);
+ pr_info("sipc: chd_nr=%d smem_base=0x%x, smem_size=0x%x\n", pdata->chd_nr, pdata->smem_base, pdata->smem_size);
+
+ i = 0;
+ info = pdata->info_table;
+
+ for_each_child_of_node(np, nchd) {
+ ret = of_property_read_string(nchd, "sprd,name", (const char **)&info[i].name);
+ if (ret) {
+ goto error;
+ }
+ pr_info("sipc:[%d] name=%s\n", i, info[i].name);
+
+ ret = of_property_read_u32(nchd, "sprd,dst", &data);
+ if (ret) {
+ goto error;
+ }
+ info[i].dst = (uint8_t)data;
+ pr_info("sipc:[%d] dst=%u\n", i, info[i].dst);
+
+#ifndef CONFIG_SPRD_MAILBOX
+ /* get ipi base addr */
+ ret = of_property_read_u32(nchd, "sprd,ap2cp", &int_ctrl_phy[0]);
+ if (ret) {
+ goto error;
+ }
+ info[i].ap2cp_int_ctrl = SPRD_DEV_P2V(int_ctrl_phy[0]);
+
+ ret = of_property_read_u32(nchd, "sprd,cp2ap", &int_ctrl_phy[1]);
+ if (ret) {
+ goto error;
+ }
+ info[i].cp2ap_int_ctrl = SPRD_DEV_P2V(int_ctrl_phy[1]);
+
+ pr_info("sipc:[%d] ap2cp_int_ctrl=0x%lx, ap2cp_ctrl_phy=0x%x, cp2ap_int_ctrl=0x%lx, cp2ap_ctrl_phy=0x%x\n",
+ i, info[i].ap2cp_int_ctrl, int_ctrl_phy[0], info[i].cp2ap_int_ctrl, int_ctrl_phy[1]);
+
+ ret = of_property_read_u32(nchd, "sprd,trig", &info[i].ap2cp_bit_trig);
+ if (ret) {
+ goto error;
+ }
+
+ ret = of_property_read_u32(nchd, "sprd,clr", &info[i].ap2cp_bit_clr);
+ if (ret) {
+ goto error;
+ }
+ pr_info("sipc:[%d] ap2cp_bit_trig=0x%x, ap2cp_bit_clr=0x%x\n",
+ i, info[i].ap2cp_bit_trig, info[i].ap2cp_bit_clr);
+#endif
+ if (strcmp(info[i].name, "sipc-pmsys") != 0) {
+ /* get cp base addr */
+ ret = of_address_to_resource(nchd, 0, &res);
+ if (ret) {
+ goto error;
+ }
+ info[i].cp_base = (uint32_t)res.start;
+ info[i].cp_size = (uint32_t)(res.end - res.start + 1);
+ pr_info("sipc:[%d] cp_base=0x%x, cp_size=0x%x\n",
+ i, info[i].cp_base, info[i].cp_size);
+
+ /* get smem base addr */
+ ret = of_address_to_resource(nchd, 1, &res);
+ if (ret) {
+ goto error;
+ }
+ info[i].smem_base = (uint32_t)res.start;
+ info[i].smem_size = (uint32_t)(res.end - res.start + 1);
+ pr_info("sipc:[%d] smem_base=0x%x, smem_size=0x%x\n", i, info[i].smem_base, info[i].smem_size);
+
+ /* get ring base addr */
+ ret = of_address_to_resource(nchd, 2, &res);
+ if (ret) {
+ goto error;
+ }
+ info[i].ring_base = (uint32_t)res.start;
+ info[i].ring_size = (uint32_t)(res.end - res.start + 1);
+ pr_info("sipc:[%d] ring_base=0x%x, ring_size=0x%x\n", i, info[i].ring_base, info[i].ring_size);
+ } else {
+ /* get ring base addr */
+ ret = of_address_to_resource(nchd, 0, &res);
+ if (ret) {
+ goto error;
+ }
+ info[i].ring_base = (uint32_t)res.start;
+ info[i].ring_size = (uint32_t)(res.end - res.start + 1);
+ pr_info("sipc:[%d] pmsys ring_base=0x%x, ring_size=0x%x\n", i, info[i].ring_base, info[i].ring_size);
+ }
+
+#ifdef CONFIG_SPRD_MAILBOX
+ ret = of_property_read_u32(nchd, "mailbox,core", &info[i].core_id);
+ if (ret) {
+ goto error;
+ }
+ pr_info("sipc:[%d] core_id=%u\n", i, info[i].core_id);
+#else
+ /* get irq */
+ info[i].irq = irq_of_parse_and_map(nchd, 0);
+ if (!info[i].irq) {
+ ret = -EINVAL;
+ goto error;
+ }
+ pr_info("sipc:[%d] irq=%d\n", i, info[i].irq);
+#endif
+ i++;
+ }
+ *init = pdata;
+ return 0;
+error:
+ kfree(pdata);
+ return ret;
+}
+
+static void sipc_destroy_pdata(struct sipc_init_data **ppdata)
+{
+ struct sipc_init_data *pdata = ppdata;
+ if (pdata && pdata->is_alloc) {
+ kfree(pdata);
+ }
+ return;
+}
+
+static int sipc_probe(struct platform_device *pdev)
+{
+ struct sipc_init_data *pdata = pdev->dev.platform_data;
+ struct sipc_device *sipc;
+ struct sipc_child_node_info *info;
+ struct smsg_ipc *smsg;
+ uint32_t num;
+ int ret, i;
+
+ pr_info("sipc:[0] define status=0x%x, clear=0x%x, trigger=0x%x\n",
+ (uint32_t)sipc_rxirq_status0, (uint32_t)sipc_rxirq_clear0, (uint32_t)sipc_txirq_trigger0);
+
+ pr_info("sipc:[1] define status=0x%x, clear=0x%x, trigger=0x%x\n",
+ (uint32_t)sipc_rxirq_status1, (uint32_t)sipc_rxirq_clear1, (uint32_t)sipc_txirq_trigger1);
+
+ pr_info("sipc:[2] define status=0x%x, clear=0x%x, trigger=0x%x\n",
+ (uint32_t)sipc_rxirq_status2, (uint32_t)sipc_rxirq_clear2, (uint32_t)sipc_txirq_trigger2);
+
+ if (!pdata && pdev->dev.of_node) {
+ ret = sipc_parse_dt(&pdata, &pdev->dev);
+ if (ret) {
+ pr_err("sipc: failed to parse dt, ret=%d\n", ret);
+ return -ENODEV;
+ }
+ }
+
+ sipc = kzalloc(sizeof(struct sipc_device), GFP_KERNEL);
+ if (!sipc) {
+ sipc_destroy_pdata(&pdata);
+ return -ENOMEM;
+ }
+
+ num = pdata->chd_nr;
+ smsg = kzalloc(num * sizeof(struct smsg_ipc), GFP_KERNEL);
+ if (!smsg)
+ {
+ sipc_destroy_pdata(&pdata);
+ kfree(sipc);
+ return -ENOMEM;
+ }
+ pr_info("sipc: num=%d\n", num);
+ info = pdata->info_table;
+ for (i = 0; i < num; i++) {
+ smsg[i].name = info[i].name;
+ smsg[i].dst = info[i].dst;
+#ifdef CONFIG_SPRD_MAILBOX
+ smsg[i].core_id = info[i].core_id;
+#else
+ smsg[i].irq = info[i].irq;
+#endif
+ smsg[i].rxirq_status = GET_SIPC_RXIRQ_STATUS_FN(i);
+ smsg[i].rxirq_clear = GET_SIPC_RXIRQ_CLEAR_FN(i);
+ smsg[i].txirq_trigger = GET_SIPC_TXIRQ_TRIGGER_FN(i);
+
+ pr_info("sipc: [%d] fn status=0x%x, clear=0x%x, trigger=0x%x\n",
+ i, (uint32_t)smsg[i].rxirq_status, (uint32_t)smsg[i].rxirq_clear,
+ (uint32_t)smsg[i].txirq_trigger);
+#ifdef CONFIG_SPRD_MAILBOX
+ pr_info("sipc:[%d] smsg name=%s, dst=%u, core_id=%d\n",
+ i, smsg[i].name, smsg[i].dst, smsg[i].core_id);
+#else
+ pr_info("sipc:[%d] smsg name=%s, dst=%u, irq=%d\n",
+ i, smsg[i].name, smsg[i].dst, smsg[i].irq);
+#endif
+ }
+
+ sipc->pdata = pdata;
+ sipc->smsg_inst = smsg;
+ sipc_dev = sipc;
+
+ smsg_suspend_init();
+
+ sipc_create(sipc);
+
+ pr_info("sipc: smem_base=0x%x, smem_size=0x%x\n", pdata->smem_base, pdata->smem_size);
+ smem_init(pdata->smem_base, pdata->smem_size);
+
+ platform_set_drvdata(pdev, sipc);
+ return 0;
+}
+
+static int sipc_remove(struct platform_device *pdev)
+{
+ struct sipc_device *sipc = platform_get_drvdata(pdev);
+
+ sipc_destroy_pdata(&sipc->pdata);
+ kfree(sipc->smsg_inst);
+ kfree(sipc);
+ return 0;
+}
+
+
+static const struct of_device_id sipc_match_table[] = {
+ {.compatible = "sprd,sipc", },
+ { },
+};
+
+static struct platform_driver sipc_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "sipc",
+ .of_match_table = sipc_match_table,
+ },
+ .probe = sipc_probe,
+ .remove = sipc_remove,
+};
+
+static int __init sipc_init(void)
+{
+ return platform_driver_register(&sipc_driver);
+}
+
+static void __exit sipc_exit(void)
+{
+ platform_driver_unregister(&sipc_driver);
+}
+
+arch_initcall(sipc_init);
+module_exit(sipc_exit);
+
+MODULE_AUTHOR("Qiu Yi");
+MODULE_DESCRIPTION("SIPC module driver");
+MODULE_LICENSE("GPL");