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/mfd/syscon.h> 12 #include <linux/of_address.h> 13 #include <linux/of_device.h> 14 #include <linux/platform_device.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 resource *res; 73 74 struct device *dev = &pdev->dev; 75 int err; 76 77 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); 78 if (!ctx) 79 return -ENOMEM; 80 81 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 82 ctx->base = devm_ioremap_resource(dev, res); 83 if (IS_ERR(ctx->base)) 84 return PTR_ERR(ctx->base); 85 86 ctx->props = device_get_match_data(dev); 87 88 ctx->cpu_ctrl = syscon_regmap_lookup_by_compatible(ctx->props->syscon); 89 if (IS_ERR(ctx->cpu_ctrl)) { 90 dev_err(dev, "No syscon map: %s\n", ctx->props->syscon); 91 return PTR_ERR(ctx->cpu_ctrl); 92 } 93 94 ctx->restart_handler.notifier_call = ocelot_restart_handle; 95 ctx->restart_handler.priority = 192; 96 err = register_restart_handler(&ctx->restart_handler); 97 if (err) 98 dev_err(dev, "can't register restart notifier (err=%d)\n", err); 99 100 return err; 101 } 102 103 static const struct reset_props reset_props_jaguar2 = { 104 .syscon = "mscc,ocelot-cpu-syscon", 105 .protect_reg = 0x20, 106 .vcore_protect = BIT(2), 107 .if_si_owner_bit = 6, 108 }; 109 110 static const struct reset_props reset_props_luton = { 111 .syscon = "mscc,ocelot-cpu-syscon", 112 .protect_reg = 0x20, 113 .vcore_protect = BIT(2), 114 .if_si_owner_bit = BIT_OFF_INVALID, /* n/a */ 115 }; 116 117 static const struct reset_props reset_props_ocelot = { 118 .syscon = "mscc,ocelot-cpu-syscon", 119 .protect_reg = 0x20, 120 .vcore_protect = BIT(2), 121 .if_si_owner_bit = 4, 122 }; 123 124 static const struct reset_props reset_props_sparx5 = { 125 .syscon = "microchip,sparx5-cpu-syscon", 126 .protect_reg = 0x84, 127 .vcore_protect = BIT(10), 128 .if_si_owner_bit = 6, 129 }; 130 131 static const struct of_device_id ocelot_reset_of_match[] = { 132 { 133 .compatible = "mscc,jaguar2-chip-reset", 134 .data = &reset_props_jaguar2 135 }, { 136 .compatible = "mscc,luton-chip-reset", 137 .data = &reset_props_luton 138 }, { 139 .compatible = "mscc,ocelot-chip-reset", 140 .data = &reset_props_ocelot 141 }, { 142 .compatible = "microchip,sparx5-chip-reset", 143 .data = &reset_props_sparx5 144 }, 145 { /*sentinel*/ } 146 }; 147 148 static struct platform_driver ocelot_reset_driver = { 149 .probe = ocelot_reset_probe, 150 .driver = { 151 .name = "ocelot-chip-reset", 152 .of_match_table = ocelot_reset_of_match, 153 }, 154 }; 155 builtin_platform_driver(ocelot_reset_driver); 156