1 // SPDX-License-Identifier: GPL-2.0+ 2 /* Microchip Sparx5 Switch Reset driver 3 * 4 * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries. 5 * 6 * The Sparx5 Chip Register Model can be browsed at this location: 7 * https://github.com/microchip-ung/sparx-5_reginfo 8 */ 9 #include <linux/mfd/syscon.h> 10 #include <linux/of_device.h> 11 #include <linux/module.h> 12 #include <linux/platform_device.h> 13 #include <linux/regmap.h> 14 #include <linux/reset-controller.h> 15 16 struct reset_props { 17 u32 protect_reg; 18 u32 protect_bit; 19 u32 reset_reg; 20 u32 reset_bit; 21 }; 22 23 struct mchp_reset_context { 24 struct regmap *cpu_ctrl; 25 struct regmap *gcb_ctrl; 26 struct reset_controller_dev rcdev; 27 const struct reset_props *props; 28 }; 29 30 static struct regmap_config sparx5_reset_regmap_config = { 31 .reg_bits = 32, 32 .val_bits = 32, 33 .reg_stride = 4, 34 }; 35 36 static int sparx5_switch_reset(struct mchp_reset_context *ctx) 37 { 38 u32 val; 39 40 /* Make sure the core is PROTECTED from reset */ 41 regmap_update_bits(ctx->cpu_ctrl, ctx->props->protect_reg, 42 ctx->props->protect_bit, ctx->props->protect_bit); 43 44 /* Start soft reset */ 45 regmap_write(ctx->gcb_ctrl, ctx->props->reset_reg, 46 ctx->props->reset_bit); 47 48 /* Wait for soft reset done */ 49 return regmap_read_poll_timeout(ctx->gcb_ctrl, ctx->props->reset_reg, val, 50 (val & ctx->props->reset_bit) == 0, 51 1, 100); 52 } 53 54 static int sparx5_reset_noop(struct reset_controller_dev *rcdev, 55 unsigned long id) 56 { 57 return 0; 58 } 59 60 static const struct reset_control_ops sparx5_reset_ops = { 61 .reset = sparx5_reset_noop, 62 }; 63 64 static int mchp_sparx5_map_syscon(struct platform_device *pdev, char *name, 65 struct regmap **target) 66 { 67 struct device_node *syscon_np; 68 struct regmap *regmap; 69 int err; 70 71 syscon_np = of_parse_phandle(pdev->dev.of_node, name, 0); 72 if (!syscon_np) 73 return -ENODEV; 74 regmap = syscon_node_to_regmap(syscon_np); 75 of_node_put(syscon_np); 76 if (IS_ERR(regmap)) { 77 err = PTR_ERR(regmap); 78 dev_err(&pdev->dev, "No '%s' map: %d\n", name, err); 79 return err; 80 } 81 *target = regmap; 82 return 0; 83 } 84 85 static int mchp_sparx5_map_io(struct platform_device *pdev, int index, 86 struct regmap **target) 87 { 88 struct resource *res; 89 struct regmap *map; 90 void __iomem *mem; 91 92 mem = devm_platform_get_and_ioremap_resource(pdev, index, &res); 93 if (IS_ERR(mem)) { 94 dev_err(&pdev->dev, "Could not map resource %d\n", index); 95 return PTR_ERR(mem); 96 } 97 sparx5_reset_regmap_config.name = res->name; 98 map = devm_regmap_init_mmio(&pdev->dev, mem, &sparx5_reset_regmap_config); 99 if (IS_ERR(map)) 100 return PTR_ERR(map); 101 *target = map; 102 return 0; 103 } 104 105 static int mchp_sparx5_reset_probe(struct platform_device *pdev) 106 { 107 struct device_node *dn = pdev->dev.of_node; 108 struct mchp_reset_context *ctx; 109 int err; 110 111 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); 112 if (!ctx) 113 return -ENOMEM; 114 115 err = mchp_sparx5_map_syscon(pdev, "cpu-syscon", &ctx->cpu_ctrl); 116 if (err) 117 return err; 118 err = mchp_sparx5_map_io(pdev, 0, &ctx->gcb_ctrl); 119 if (err) 120 return err; 121 122 ctx->rcdev.owner = THIS_MODULE; 123 ctx->rcdev.nr_resets = 1; 124 ctx->rcdev.ops = &sparx5_reset_ops; 125 ctx->rcdev.of_node = dn; 126 ctx->props = device_get_match_data(&pdev->dev); 127 128 /* Issue the reset very early, our actual reset callback is a noop. */ 129 err = sparx5_switch_reset(ctx); 130 if (err) 131 return err; 132 133 return devm_reset_controller_register(&pdev->dev, &ctx->rcdev); 134 } 135 136 static const struct reset_props reset_props_sparx5 = { 137 .protect_reg = 0x84, 138 .protect_bit = BIT(10), 139 .reset_reg = 0x0, 140 .reset_bit = BIT(1), 141 }; 142 143 static const struct reset_props reset_props_lan966x = { 144 .protect_reg = 0x88, 145 .protect_bit = BIT(5), 146 .reset_reg = 0x0, 147 .reset_bit = BIT(1), 148 }; 149 150 static const struct of_device_id mchp_sparx5_reset_of_match[] = { 151 { 152 .compatible = "microchip,sparx5-switch-reset", 153 .data = &reset_props_sparx5, 154 }, { 155 .compatible = "microchip,lan966x-switch-reset", 156 .data = &reset_props_lan966x, 157 }, 158 { } 159 }; 160 161 static struct platform_driver mchp_sparx5_reset_driver = { 162 .probe = mchp_sparx5_reset_probe, 163 .driver = { 164 .name = "sparx5-switch-reset", 165 .of_match_table = mchp_sparx5_reset_of_match, 166 }, 167 }; 168 169 static int __init mchp_sparx5_reset_init(void) 170 { 171 return platform_driver_register(&mchp_sparx5_reset_driver); 172 } 173 174 /* 175 * Because this is a global reset, keep this postcore_initcall() to issue the 176 * reset as early as possible during the kernel startup. 177 */ 178 postcore_initcall(mchp_sparx5_reset_init); 179 180 MODULE_DESCRIPTION("Microchip Sparx5 switch reset driver"); 181 MODULE_AUTHOR("Steen Hegelund <steen.hegelund@microchip.com>"); 182