Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | /* * Copyright (C) 2013 Broadcom Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation version 2. * * This program is distributed "as is" WITHOUT ANY WARRANTY of any * kind, whether express or implied; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include <linux/bitops.h> #include <linux/device.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/io.h> #include <linux/jiffies.h> #include <linux/notifier.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/printk.h> #include <linux/reboot.h> #include <linux/regmap.h> #include <linux/smp.h> #include <linux/mfd/syscon.h> #define RESET_SOURCE_ENABLE_REG 1 #define SW_MASTER_RESET_REG 2 static struct regmap *regmap; static u32 rst_src_en; static u32 sw_mstr_rst; struct reset_reg_mask { u32 rst_src_en_mask; u32 sw_mstr_rst_mask; }; static const struct reset_reg_mask *reset_masks; static int brcmstb_restart_handler(struct notifier_block *this, unsigned long mode, void *cmd) { int rc; u32 tmp; rc = regmap_write(regmap, rst_src_en, reset_masks->rst_src_en_mask); if (rc) { pr_err("failed to write rst_src_en (%d)\n", rc); return NOTIFY_DONE; } rc = regmap_read(regmap, rst_src_en, &tmp); if (rc) { pr_err("failed to read rst_src_en (%d)\n", rc); return NOTIFY_DONE; } rc = regmap_write(regmap, sw_mstr_rst, reset_masks->sw_mstr_rst_mask); if (rc) { pr_err("failed to write sw_mstr_rst (%d)\n", rc); return NOTIFY_DONE; } rc = regmap_read(regmap, sw_mstr_rst, &tmp); if (rc) { pr_err("failed to read sw_mstr_rst (%d)\n", rc); return NOTIFY_DONE; } while (1) ; return NOTIFY_DONE; } static struct notifier_block brcmstb_restart_nb = { .notifier_call = brcmstb_restart_handler, .priority = 128, }; static const struct reset_reg_mask reset_bits_40nm = { .rst_src_en_mask = BIT(0), .sw_mstr_rst_mask = BIT(0), }; static const struct reset_reg_mask reset_bits_65nm = { .rst_src_en_mask = BIT(3), .sw_mstr_rst_mask = BIT(31), }; static const struct of_device_id of_match[] = { { .compatible = "brcm,brcmstb-reboot", .data = &reset_bits_40nm }, { .compatible = "brcm,bcm7038-reboot", .data = &reset_bits_65nm }, {}, }; static int brcmstb_reboot_probe(struct platform_device *pdev) { int rc; struct device_node *np = pdev->dev.of_node; const struct of_device_id *of_id; of_id = of_match_node(of_match, np); if (!of_id) { pr_err("failed to look up compatible string\n"); return -EINVAL; } reset_masks = of_id->data; regmap = syscon_regmap_lookup_by_phandle(np, "syscon"); if (IS_ERR(regmap)) { pr_err("failed to get syscon phandle\n"); return -EINVAL; } rc = of_property_read_u32_index(np, "syscon", RESET_SOURCE_ENABLE_REG, &rst_src_en); if (rc) { pr_err("can't get rst_src_en offset (%d)\n", rc); return -EINVAL; } rc = of_property_read_u32_index(np, "syscon", SW_MASTER_RESET_REG, &sw_mstr_rst); if (rc) { pr_err("can't get sw_mstr_rst offset (%d)\n", rc); return -EINVAL; } rc = register_restart_handler(&brcmstb_restart_nb); if (rc) dev_err(&pdev->dev, "cannot register restart handler (err=%d)\n", rc); return rc; } static struct platform_driver brcmstb_reboot_driver = { .probe = brcmstb_reboot_probe, .driver = { .name = "brcmstb-reboot", .of_match_table = of_match, }, }; static int __init brcmstb_reboot_init(void) { return platform_driver_probe(&brcmstb_reboot_driver, brcmstb_reboot_probe); } subsys_initcall(brcmstb_reboot_init); |