/* * Copyright (c) 2017 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * * Exynos - Support SoC specific handler * Author: Hosung Kim * Youngmin Nam * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include extern struct dbg_snapshot_helper_ops *dss_soc_ops; struct exynos_handler { int irq; char name[SZ_16]; irqreturn_t (*handle_irq)(int irq, void *data); }; static int handler_nr_irq; static struct exynos_handler *ecc_handler; static irqreturn_t exynos_ecc_handler(int irq, void *data) { struct exynos_handler *ecc = (struct exynos_handler *)data; dss_soc_ops->soc_dump_info(NULL); panic("Detected ECC error: irq: %d, name: %s", ecc->irq, ecc->name); return 0; } static int __init exynos_handler_setup(struct device_node *np) { int err = 0, i; if (of_property_read_u32(np, "handler_nr_irq", &handler_nr_irq)) { handler_nr_irq = 0; pr_err("%s: handler_nr_irq property is not defined in device tree\n", __func__); } pr_info("%s: handler_nr_irq = %d\n", __func__, handler_nr_irq); /* memory alloc for handler */ if (handler_nr_irq > 0) { ecc_handler = kzalloc(sizeof(struct exynos_handler) * handler_nr_irq, GFP_KERNEL); if (!ecc_handler) { pr_err("%s: fail to kzalloc\n", __func__); err = -ENOMEM; goto out; } } /* setup ecc_handler */ for (i = 0; i < handler_nr_irq; i++) { ecc_handler[i].irq = irq_of_parse_and_map(np, i); snprintf(ecc_handler[i].name, sizeof(ecc_handler[i].name), "ecc_handler%d", i); ecc_handler[i].handle_irq = exynos_ecc_handler; err = request_irq(ecc_handler[i].irq, ecc_handler[i].handle_irq, IRQ_TYPE_LEVEL_HIGH | IRQF_NOBALANCING | IRQF_GIC_MULTI_TARGET, ecc_handler[i].name, &ecc_handler[i]); if (err) { pr_err("unable to request irq%d for %s ecc handler\n", ecc_handler[i].irq, ecc_handler[i].name); break; } else { pr_info("Success to request irq%d for %s ecc handler\n", ecc_handler[i].irq, ecc_handler[i].name); } } out: of_node_put(np); return err; } static const struct of_device_id handler_of_match[] __initconst = { { .compatible = "samsung,exynos-handler", .data = exynos_handler_setup}, {}, }; typedef int (*handler_initcall_t)(const struct device_node *); static int __init exynos_handler_init(void) { struct device_node *np; const struct of_device_id *matched_np; handler_initcall_t init_fn; np = of_find_matching_node_and_match(NULL, handler_of_match, &matched_np); if (!np) return -ENODEV; init_fn = (handler_initcall_t)matched_np->data; return init_fn(np); } subsys_initcall(exynos_handler_init);