183 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/* Microchip Sparx5 Switch Reset driver3 *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_reginfo8 */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 17struct reset_props {18 u32 protect_reg;19 u32 protect_bit;20 u32 reset_reg;21 u32 reset_bit;22};23 24struct 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 31static struct regmap_config sparx5_reset_regmap_config = {32 .reg_bits = 32,33 .val_bits = 32,34 .reg_stride = 4,35};36 37static 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 55static int sparx5_reset_noop(struct reset_controller_dev *rcdev,56 unsigned long id)57{58 return 0;59}60 61static const struct reset_control_ops sparx5_reset_ops = {62 .reset = sparx5_reset_noop,63};64 65static int mchp_sparx5_map_syscon(struct platform_device *pdev, char *name,66 struct regmap **target)67{68 struct device_node *syscon_np;69 struct regmap *regmap;70 int err;71 72 syscon_np = of_parse_phandle(pdev->dev.of_node, name, 0);73 if (!syscon_np)74 return -ENODEV;75 regmap = syscon_node_to_regmap(syscon_np);76 of_node_put(syscon_np);77 if (IS_ERR(regmap)) {78 err = PTR_ERR(regmap);79 dev_err(&pdev->dev, "No '%s' map: %d\n", name, err);80 return err;81 }82 *target = regmap;83 return 0;84}85 86static int mchp_sparx5_map_io(struct platform_device *pdev, int index,87 struct regmap **target)88{89 struct resource *res;90 struct regmap *map;91 void __iomem *mem;92 93 mem = devm_platform_get_and_ioremap_resource(pdev, index, &res);94 if (IS_ERR(mem)) {95 dev_err(&pdev->dev, "Could not map resource %d\n", index);96 return PTR_ERR(mem);97 }98 sparx5_reset_regmap_config.name = res->name;99 map = devm_regmap_init_mmio(&pdev->dev, mem, &sparx5_reset_regmap_config);100 if (IS_ERR(map))101 return PTR_ERR(map);102 *target = map;103 return 0;104}105 106static int mchp_sparx5_reset_probe(struct platform_device *pdev)107{108 struct device_node *dn = pdev->dev.of_node;109 struct mchp_reset_context *ctx;110 int err;111 112 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);113 if (!ctx)114 return -ENOMEM;115 116 err = mchp_sparx5_map_syscon(pdev, "cpu-syscon", &ctx->cpu_ctrl);117 if (err)118 return err;119 err = mchp_sparx5_map_io(pdev, 0, &ctx->gcb_ctrl);120 if (err)121 return err;122 123 ctx->rcdev.owner = THIS_MODULE;124 ctx->rcdev.nr_resets = 1;125 ctx->rcdev.ops = &sparx5_reset_ops;126 ctx->rcdev.of_node = dn;127 ctx->props = device_get_match_data(&pdev->dev);128 129 /* Issue the reset very early, our actual reset callback is a noop. */130 err = sparx5_switch_reset(ctx);131 if (err)132 return err;133 134 return devm_reset_controller_register(&pdev->dev, &ctx->rcdev);135}136 137static const struct reset_props reset_props_sparx5 = {138 .protect_reg = 0x84,139 .protect_bit = BIT(10),140 .reset_reg = 0x0,141 .reset_bit = BIT(1),142};143 144static const struct reset_props reset_props_lan966x = {145 .protect_reg = 0x88,146 .protect_bit = BIT(5),147 .reset_reg = 0x0,148 .reset_bit = BIT(1),149};150 151static const struct of_device_id mchp_sparx5_reset_of_match[] = {152 {153 .compatible = "microchip,sparx5-switch-reset",154 .data = &reset_props_sparx5,155 }, {156 .compatible = "microchip,lan966x-switch-reset",157 .data = &reset_props_lan966x,158 },159 { }160};161 162static struct platform_driver mchp_sparx5_reset_driver = {163 .probe = mchp_sparx5_reset_probe,164 .driver = {165 .name = "sparx5-switch-reset",166 .of_match_table = mchp_sparx5_reset_of_match,167 },168};169 170static int __init mchp_sparx5_reset_init(void)171{172 return platform_driver_register(&mchp_sparx5_reset_driver);173}174 175/*176 * Because this is a global reset, keep this postcore_initcall() to issue the177 * reset as early as possible during the kernel startup.178 */179postcore_initcall(mchp_sparx5_reset_init);180 181MODULE_DESCRIPTION("Microchip Sparx5 switch reset driver");182MODULE_AUTHOR("Steen Hegelund <steen.hegelund@microchip.com>");183