/* drivers/soc/qcom/smd_init_dt.c * * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. * * 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 #include #include #include #include #include #include #include "smd_private.h" #define MODULE_NAME "msm_smd" #define IPC_LOG(level, x...) do { \ if (smd_log_ctx) \ ipc_log_string(smd_log_ctx, x); \ else \ printk(level x); \ } while (0) #if defined(CONFIG_MSM_SMD_DEBUG) #define SMD_DBG(x...) do { \ if (msm_smd_debug_mask & MSM_SMD_DEBUG) \ IPC_LOG(KERN_DEBUG, x); \ } while (0) #define SMSM_DBG(x...) do { \ if (msm_smd_debug_mask & MSM_SMSM_DEBUG) \ IPC_LOG(KERN_DEBUG, x); \ } while (0) #else #define SMD_DBG(x...) do { } while (0) #define SMSM_DBG(x...) do { } while (0) #endif static DEFINE_MUTEX(smd_probe_lock); static int first_probe_done; static int msm_smsm_probe(struct platform_device *pdev) { uint32_t edge; char *key; int ret; uint32_t irq_offset; uint32_t irq_bitmask; uint32_t irq_line; struct interrupt_config_item *private_irq; struct device_node *node; void *irq_out_base; resource_size_t irq_out_size; struct platform_device *parent_pdev; struct resource *r; struct interrupt_config *private_intr_config; uint32_t remote_pid; node = pdev->dev.of_node; if (!pdev->dev.parent) { pr_err("%s: missing link to parent device\n", __func__); return -ENODEV; } parent_pdev = to_platform_device(pdev->dev.parent); key = "irq-reg-base"; r = platform_get_resource_byname(parent_pdev, IORESOURCE_MEM, key); if (!r) goto missing_key; irq_out_size = resource_size(r); irq_out_base = ioremap_nocache(r->start, irq_out_size); if (!irq_out_base) { pr_err("%s: ioremap_nocache() of irq_out_base addr:%pr size:%pr\n", __func__, &r->start, &irq_out_size); return -ENOMEM; } SMSM_DBG("%s: %s = %p", __func__, key, irq_out_base); key = "qcom,smsm-edge"; ret = of_property_read_u32(node, key, &edge); if (ret) goto missing_key; SMSM_DBG("%s: %s = %d", __func__, key, edge); key = "qcom,smsm-irq-offset"; ret = of_property_read_u32(node, key, &irq_offset); if (ret) goto missing_key; SMSM_DBG("%s: %s = %x", __func__, key, irq_offset); key = "qcom,smsm-irq-bitmask"; ret = of_property_read_u32(node, key, &irq_bitmask); if (ret) goto missing_key; SMSM_DBG("%s: %s = %x", __func__, key, irq_bitmask); key = "interrupts"; irq_line = irq_of_parse_and_map(node, 0); if (!irq_line) goto missing_key; SMSM_DBG("%s: %s = %d", __func__, key, irq_line); private_intr_config = smd_get_intr_config(edge); if (!private_intr_config) { pr_err("%s: invalid edge\n", __func__); return -ENODEV; } private_irq = &private_intr_config->smsm; private_irq->out_bit_pos = irq_bitmask; private_irq->out_offset = irq_offset; private_irq->out_base = irq_out_base; private_irq->irq_id = irq_line; remote_pid = smd_edge_to_remote_pid(edge); interrupt_stats[remote_pid].smsm_interrupt_id = irq_line; ret = request_irq(irq_line, private_irq->irq_handler, IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND, node->name, NULL); if (ret < 0) { pr_err("%s: request_irq() failed on %d\n", __func__, irq_line); return ret; } else { ret = enable_irq_wake(irq_line); if (ret < 0) pr_err("%s: enable_irq_wake() failed on %d\n", __func__, irq_line); } ret = smsm_post_init(); if (ret) { pr_err("smd_post_init() failed ret=%d\n", ret); return ret; } return 0; missing_key: pr_err("%s: missing key: %s", __func__, key); return -ENODEV; } static int msm_smd_probe(struct platform_device *pdev) { uint32_t edge; char *key; int ret; uint32_t irq_offset; uint32_t irq_bitmask; uint32_t irq_line; const char *subsys_name; struct interrupt_config_item *private_irq; struct device_node *node; void *irq_out_base; resource_size_t irq_out_size; struct platform_device *parent_pdev; struct resource *r; struct interrupt_config *private_intr_config; uint32_t remote_pid; bool skip_pil; node = pdev->dev.of_node; if (!pdev->dev.parent) { pr_err("%s: missing link to parent device\n", __func__); return -ENODEV; } mutex_lock(&smd_probe_lock); if (!first_probe_done) { smd_reset_all_edge_subsys_name(); first_probe_done = 1; } mutex_unlock(&smd_probe_lock); parent_pdev = to_platform_device(pdev->dev.parent); key = "irq-reg-base"; r = platform_get_resource_byname(parent_pdev, IORESOURCE_MEM, key); if (!r) goto missing_key; irq_out_size = resource_size(r); irq_out_base = ioremap_nocache(r->start, irq_out_size); if (!irq_out_base) { pr_err("%s: ioremap_nocache() of irq_out_base addr:%pr size:%pr\n", __func__, &r->start, &irq_out_size); return -ENOMEM; } SMD_DBG("%s: %s = %p", __func__, key, irq_out_base); key = "qcom,smd-edge"; ret = of_property_read_u32(node, key, &edge); if (ret) goto missing_key; SMD_DBG("%s: %s = %d", __func__, key, edge); key = "qcom,smd-irq-offset"; ret = of_property_read_u32(node, key, &irq_offset); if (ret) goto missing_key; SMD_DBG("%s: %s = %x", __func__, key, irq_offset); key = "qcom,smd-irq-bitmask"; ret = of_property_read_u32(node, key, &irq_bitmask); if (ret) goto missing_key; SMD_DBG("%s: %s = %x", __func__, key, irq_bitmask); key = "interrupts"; irq_line = irq_of_parse_and_map(node, 0); if (!irq_line) goto missing_key; SMD_DBG("%s: %s = %d", __func__, key, irq_line); key = "label"; subsys_name = of_get_property(node, key, NULL); SMD_DBG("%s: %s = %s", __func__, key, subsys_name); /* * Backwards compatibility. Although label is required, some DTs may * still list the legacy pil-string. Sanely handle pil-string. */ if (!subsys_name) { pr_warn("msm_smd: Missing required property - label. Using legacy parsing\n"); key = "qcom,pil-string"; subsys_name = of_get_property(node, key, NULL); SMD_DBG("%s: %s = %s", __func__, key, subsys_name); if (subsys_name) skip_pil = false; else skip_pil = true; } else { key = "qcom,not-loadable"; skip_pil = of_property_read_bool(node, key); SMD_DBG("%s: %s = %d\n", __func__, key, skip_pil); } private_intr_config = smd_get_intr_config(edge); if (!private_intr_config) { pr_err("%s: invalid edge\n", __func__); return -ENODEV; } private_irq = &private_intr_config->smd; private_irq->out_bit_pos = irq_bitmask; private_irq->out_offset = irq_offset; private_irq->out_base = irq_out_base; private_irq->irq_id = irq_line; remote_pid = smd_edge_to_remote_pid(edge); interrupt_stats[remote_pid].smd_interrupt_id = irq_line; ret = request_irq(irq_line, private_irq->irq_handler, IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND | IRQF_SHARED, node->name, &pdev->dev); if (ret < 0) { pr_err("%s: request_irq() failed on %d\n", __func__, irq_line); return ret; } else { ret = enable_irq_wake(irq_line); if (ret < 0) pr_err("%s: enable_irq_wake() failed on %d\n", __func__, irq_line); } smd_set_edge_subsys_name(edge, subsys_name); smd_proc_set_skip_pil(smd_edge_to_remote_pid(edge), skip_pil); smd_set_edge_initialized(edge); smd_post_init(remote_pid); return 0; missing_key: pr_err("%s: missing key: %s", __func__, key); return -ENODEV; } static struct of_device_id msm_smd_match_table[] = { { .compatible = "qcom,smd" }, {}, }; static struct platform_driver msm_smd_driver = { .probe = msm_smd_probe, .driver = { .name = MODULE_NAME , .owner = THIS_MODULE, .of_match_table = msm_smd_match_table, }, }; static struct of_device_id msm_smsm_match_table[] = { { .compatible = "qcom,smsm" }, {}, }; static struct platform_driver msm_smsm_driver = { .probe = msm_smsm_probe, .driver = { .name = "msm_smsm", .owner = THIS_MODULE, .of_match_table = msm_smsm_match_table, }, }; int msm_smd_driver_register(void) { int rc; rc = platform_driver_register(&msm_smd_driver); if (rc) { pr_err("%s: smd_driver register failed %d\n", __func__, rc); return rc; } rc = platform_driver_register(&msm_smsm_driver); if (rc) { pr_err("%s: msm_smsm_driver register failed %d\n", __func__, rc); return rc; } return 0; } EXPORT_SYMBOL(msm_smd_driver_register); MODULE_DESCRIPTION("MSM SMD Device Tree Init"); MODULE_LICENSE("GPL v2");