1 // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 /* 3 * Microsemi MIPS SoC reset driver 4 * 5 * License: Dual MIT/GPL 6 * Copyright (c) 2017 Microsemi Corporation 7 */ 8 #include <linux/delay.h> 9 #include <linux/io.h> 10 #include <linux/notifier.h> 11 #include <linux/mod_devicetable.h> 12 #include <linux/mfd/syscon.h> 13 #include <linux/platform_device.h> 14 #include <linux/property.h> 15 #include <linux/reboot.h> 16 #include <linux/regmap.h> 17 18 struct reset_props { 19 const char *syscon; 20 u32 protect_reg; 21 u32 vcore_protect; 22 u32 if_si_owner_bit; 23 }; 24 25 struct ocelot_reset_context { 26 void __iomem *base; 27 struct regmap *cpu_ctrl; 28 const struct reset_props *props; 29 struct notifier_block restart_handler; 30 }; 31 32 #define BIT_OFF_INVALID 32 33 34 #define SOFT_CHIP_RST BIT(0) 35 36 #define ICPU_CFG_CPU_SYSTEM_CTRL_GENERAL_CTRL 0x24 37 #define IF_SI_OWNER_MASK GENMASK(1, 0) 38 #define IF_SI_OWNER_SISL 0 39 #define IF_SI_OWNER_SIBM 1 40 #define IF_SI_OWNER_SIMC 2 41 42 static int ocelot_restart_handle(struct notifier_block *this, 43 unsigned long mode, void *cmd) 44 { 45 struct ocelot_reset_context *ctx = container_of(this, struct 46 ocelot_reset_context, 47 restart_handler); 48 u32 if_si_owner_bit = ctx->props->if_si_owner_bit; 49 50 /* Make sure the core is not protected from reset */ 51 regmap_update_bits(ctx->cpu_ctrl, ctx->props->protect_reg, 52 ctx->props->vcore_protect, 0); 53 54 /* Make the SI back to boot mode */ 55 if (if_si_owner_bit != BIT_OFF_INVALID) 56 regmap_update_bits(ctx->cpu_ctrl, 57 ICPU_CFG_CPU_SYSTEM_CTRL_GENERAL_CTRL, 58 IF_SI_OWNER_MASK << if_si_owner_bit, 59 IF_SI_OWNER_SIBM << if_si_owner_bit); 60 61 pr_emerg("Resetting SoC\n"); 62 63 writel(SOFT_CHIP_RST, ctx->base); 64 65 pr_emerg("Unable to restart system\n"); 66 return NOTIFY_DONE; 67 } 68 69 static int ocelot_reset_probe(struct platform_device *pdev) 70 { 71 struct ocelot_reset_context *ctx; 72 struct device *dev = &pdev->dev; 73 int err; 74 75 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); 76 if (!ctx) 77 return -ENOMEM; 78 79 ctx->base = devm_platform_ioremap_resource(pdev, 0); 80 if (IS_ERR(ctx->base)) 81 return PTR_ERR(ctx->base); 82 83 ctx->props = device_get_match_data(dev); 84 85 ctx->cpu_ctrl = syscon_regmap_lookup_by_compatible(ctx->props->syscon); 86 if (IS_ERR(ctx->cpu_ctrl)) { 87 dev_err(dev, "No syscon map: %s\n", ctx->props->syscon); 88 return PTR_ERR(ctx->cpu_ctrl); 89 } 90 91 ctx->restart_handler.notifier_call = ocelot_restart_handle; 92 ctx->restart_handler.priority = 192; 93 err = register_restart_handler(&ctx->restart_handler); 94 if (err) 95 dev_err(dev, "can't register restart notifier (err=%d)\n", err); 96 97 return err; 98 } 99 100 static const struct reset_props reset_props_jaguar2 = { 101 .syscon = "mscc,ocelot-cpu-syscon", 102 .protect_reg = 0x20, 103 .vcore_protect = BIT(2), 104 .if_si_owner_bit = 6, 105 }; 106 107 static const struct reset_props reset_props_luton = { 108 .syscon = "mscc,ocelot-cpu-syscon", 109 .protect_reg = 0x20, 110 .vcore_protect = BIT(2), 111 .if_si_owner_bit = BIT_OFF_INVALID, /* n/a */ 112 }; 113 114 static const struct reset_props reset_props_ocelot = { 115 .syscon = "mscc,ocelot-cpu-syscon", 116 .protect_reg = 0x20, 117 .vcore_protect = BIT(2), 118 .if_si_owner_bit = 4, 119 }; 120 121 static const struct reset_props reset_props_sparx5 = { 122 .syscon = "microchip,sparx5-cpu-syscon", 123 .protect_reg = 0x84, 124 .vcore_protect = BIT(10), 125 .if_si_owner_bit = 6, 126 }; 127 128 static const struct of_device_id ocelot_reset_of_match[] = { 129 { 130 .compatible = "mscc,jaguar2-chip-reset", 131 .data = &reset_props_jaguar2 132 }, { 133 .compatible = "mscc,luton-chip-reset", 134 .data = &reset_props_luton 135 }, { 136 .compatible = "mscc,ocelot-chip-reset", 137 .data = &reset_props_ocelot 138 }, { 139 .compatible = "microchip,sparx5-chip-reset", 140 .data = &reset_props_sparx5 141 }, 142 { /*sentinel*/ } 143 }; 144 145 static struct platform_driver ocelot_reset_driver = { 146 .probe = ocelot_reset_probe, 147 .driver = { 148 .name = "ocelot-chip-reset", 149 .of_match_table = ocelot_reset_of_match, 150 }, 151 }; 152 builtin_platform_driver(ocelot_reset_driver); 153