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.h> 11 #include <linux/module.h> 12 #include <linux/platform_device.h> 13 #include <linux/property.h> 14 #include <linux/regmap.h> 15 #include <linux/reset-controller.h> 16 17 struct reset_props { 18 u32 protect_reg; 19 u32 protect_bit; 20 u32 reset_reg; 21 u32 reset_bit; 22 }; 23 24 struct mchp_reset_context { 25 struct regmap *cpu_ctrl; 26 struct regmap *gcb_ctrl; 27 struct reset_controller_dev rcdev; 28 const struct reset_props *props; 29 }; 30 31 static struct regmap_config sparx5_reset_regmap_config = { 32 .reg_bits = 32, 33 .val_bits = 32, 34 .reg_stride = 4, 35 }; 36 37 static int sparx5_switch_reset(struct mchp_reset_context *ctx) 38 { 39 u32 val; 40 41 /* Make sure the core is PROTECTED from reset */ 42 regmap_update_bits(ctx->cpu_ctrl, ctx->props->protect_reg, 43 ctx->props->protect_bit, ctx->props->protect_bit); 44 45 /* Start soft reset */ 46 regmap_write(ctx->gcb_ctrl, ctx->props->reset_reg, 47 ctx->props->reset_bit); 48 49 /* Wait for soft reset done */ 50 return regmap_read_poll_timeout(ctx->gcb_ctrl, ctx->props->reset_reg, val, 51 (val & ctx->props->reset_bit) == 0, 52 1, 100); 53 } 54 55 static int sparx5_reset_noop(struct reset_controller_dev *rcdev, 56 unsigned long id) 57 { 58 return 0; 59 } 60 61 static const struct reset_control_ops sparx5_reset_ops = { 62 .reset = sparx5_reset_noop, 63 }; 64 65 static const struct regmap_config mchp_lan966x_syscon_regmap_config = { 66 .reg_bits = 32, 67 .val_bits = 32, 68 .reg_stride = 4, 69 }; 70 71 static struct regmap *mchp_lan966x_syscon_to_regmap(struct device *dev, 72 struct device_node *syscon_np) 73 { 74 struct regmap_config regmap_config = mchp_lan966x_syscon_regmap_config; 75 resource_size_t size; 76 void __iomem *base; 77 78 base = devm_of_iomap(dev, syscon_np, 0, &size); 79 if (IS_ERR(base)) 80 return ERR_CAST(base); 81 82 regmap_config.max_register = size - 4; 83 84 return devm_regmap_init_mmio(dev, base, ®map_config); 85 } 86 87 static int mchp_sparx5_map_syscon(struct platform_device *pdev, char *name, 88 struct regmap **target) 89 { 90 struct device_node *syscon_np; 91 struct regmap *regmap; 92 int err; 93 94 syscon_np = of_parse_phandle(pdev->dev.of_node, name, 0); 95 if (!syscon_np) 96 return -ENODEV; 97 98 /* 99 * The syscon API doesn't support syscon device removal. 100 * When used in LAN966x PCI device, the cpu-syscon device needs to be 101 * removed when the PCI device is removed. 102 * In case of LAN966x, map the syscon device locally to support the 103 * device removal. 104 */ 105 if (of_device_is_compatible(pdev->dev.of_node, "microchip,lan966x-switch-reset")) 106 regmap = mchp_lan966x_syscon_to_regmap(&pdev->dev, syscon_np); 107 else 108 regmap = syscon_node_to_regmap(syscon_np); 109 of_node_put(syscon_np); 110 if (IS_ERR(regmap)) { 111 err = PTR_ERR(regmap); 112 dev_err(&pdev->dev, "No '%s' map: %d\n", name, err); 113 return err; 114 } 115 *target = regmap; 116 return 0; 117 } 118 119 static int mchp_sparx5_map_io(struct platform_device *pdev, int index, 120 struct regmap **target) 121 { 122 struct resource *res; 123 struct regmap *map; 124 void __iomem *mem; 125 126 mem = devm_platform_get_and_ioremap_resource(pdev, index, &res); 127 if (IS_ERR(mem)) { 128 dev_err(&pdev->dev, "Could not map resource %d\n", index); 129 return PTR_ERR(mem); 130 } 131 sparx5_reset_regmap_config.name = res->name; 132 map = devm_regmap_init_mmio(&pdev->dev, mem, &sparx5_reset_regmap_config); 133 if (IS_ERR(map)) 134 return PTR_ERR(map); 135 *target = map; 136 return 0; 137 } 138 139 static int mchp_sparx5_reset_probe(struct platform_device *pdev) 140 { 141 struct device_node *dn = pdev->dev.of_node; 142 struct mchp_reset_context *ctx; 143 int err; 144 145 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); 146 if (!ctx) 147 return -ENOMEM; 148 149 err = mchp_sparx5_map_syscon(pdev, "cpu-syscon", &ctx->cpu_ctrl); 150 if (err) 151 return err; 152 err = mchp_sparx5_map_io(pdev, 0, &ctx->gcb_ctrl); 153 if (err) 154 return err; 155 156 ctx->rcdev.owner = THIS_MODULE; 157 ctx->rcdev.dev = &pdev->dev; 158 ctx->rcdev.nr_resets = 1; 159 ctx->rcdev.ops = &sparx5_reset_ops; 160 ctx->rcdev.of_node = dn; 161 ctx->props = device_get_match_data(&pdev->dev); 162 163 /* Issue the reset very early, our actual reset callback is a noop. */ 164 err = sparx5_switch_reset(ctx); 165 if (err) 166 return err; 167 168 return devm_reset_controller_register(&pdev->dev, &ctx->rcdev); 169 } 170 171 static const struct reset_props reset_props_sparx5 = { 172 .protect_reg = 0x84, 173 .protect_bit = BIT(10), 174 .reset_reg = 0x0, 175 .reset_bit = BIT(1), 176 }; 177 178 static const struct reset_props reset_props_lan966x = { 179 .protect_reg = 0x88, 180 .protect_bit = BIT(5), 181 .reset_reg = 0x0, 182 .reset_bit = BIT(1), 183 }; 184 185 static const struct of_device_id mchp_sparx5_reset_of_match[] = { 186 { 187 .compatible = "microchip,sparx5-switch-reset", 188 .data = &reset_props_sparx5, 189 }, { 190 .compatible = "microchip,lan966x-switch-reset", 191 .data = &reset_props_lan966x, 192 }, 193 { } 194 }; 195 MODULE_DEVICE_TABLE(of, mchp_sparx5_reset_of_match); 196 197 static struct platform_driver mchp_sparx5_reset_driver = { 198 .probe = mchp_sparx5_reset_probe, 199 .driver = { 200 .name = "sparx5-switch-reset", 201 .of_match_table = mchp_sparx5_reset_of_match, 202 }, 203 }; 204 205 static int __init mchp_sparx5_reset_init(void) 206 { 207 return platform_driver_register(&mchp_sparx5_reset_driver); 208 } 209 210 /* 211 * Because this is a global reset, keep this postcore_initcall() to issue the 212 * reset as early as possible during the kernel startup. 213 */ 214 postcore_initcall(mchp_sparx5_reset_init); 215 216 MODULE_DESCRIPTION("Microchip Sparx5 switch reset driver"); 217 MODULE_AUTHOR("Steen Hegelund <steen.hegelund@microchip.com>"); 218 MODULE_LICENSE("GPL"); 219