reset-microchip-sparx5.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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. struct reset_props {
  16. u32 protect_reg;
  17. u32 protect_bit;
  18. u32 reset_reg;
  19. u32 reset_bit;
  20. };
  21. struct mchp_reset_context {
  22. struct regmap *cpu_ctrl;
  23. struct regmap *gcb_ctrl;
  24. struct reset_controller_dev rcdev;
  25. const struct reset_props *props;
  26. };
  27. static struct regmap_config sparx5_reset_regmap_config = {
  28. .reg_bits = 32,
  29. .val_bits = 32,
  30. .reg_stride = 4,
  31. };
  32. static int sparx5_switch_reset(struct mchp_reset_context *ctx)
  33. {
  34. u32 val;
  35. /* Make sure the core is PROTECTED from reset */
  36. regmap_update_bits(ctx->cpu_ctrl, ctx->props->protect_reg,
  37. ctx->props->protect_bit, ctx->props->protect_bit);
  38. /* Start soft reset */
  39. regmap_write(ctx->gcb_ctrl, ctx->props->reset_reg,
  40. ctx->props->reset_bit);
  41. /* Wait for soft reset done */
  42. return regmap_read_poll_timeout(ctx->gcb_ctrl, ctx->props->reset_reg, val,
  43. (val & ctx->props->reset_bit) == 0,
  44. 1, 100);
  45. }
  46. static int sparx5_reset_noop(struct reset_controller_dev *rcdev,
  47. unsigned long id)
  48. {
  49. return 0;
  50. }
  51. static const struct reset_control_ops sparx5_reset_ops = {
  52. .reset = sparx5_reset_noop,
  53. };
  54. static int mchp_sparx5_map_syscon(struct platform_device *pdev, char *name,
  55. struct regmap **target)
  56. {
  57. struct device_node *syscon_np;
  58. struct regmap *regmap;
  59. int err;
  60. syscon_np = of_parse_phandle(pdev->dev.of_node, name, 0);
  61. if (!syscon_np)
  62. return -ENODEV;
  63. regmap = syscon_node_to_regmap(syscon_np);
  64. of_node_put(syscon_np);
  65. if (IS_ERR(regmap)) {
  66. err = PTR_ERR(regmap);
  67. dev_err(&pdev->dev, "No '%s' map: %d\n", name, err);
  68. return err;
  69. }
  70. *target = regmap;
  71. return 0;
  72. }
  73. static int mchp_sparx5_map_io(struct platform_device *pdev, int index,
  74. struct regmap **target)
  75. {
  76. struct resource *res;
  77. struct regmap *map;
  78. void __iomem *mem;
  79. mem = devm_platform_get_and_ioremap_resource(pdev, index, &res);
  80. if (IS_ERR(mem)) {
  81. dev_err(&pdev->dev, "Could not map resource %d\n", index);
  82. return PTR_ERR(mem);
  83. }
  84. sparx5_reset_regmap_config.name = res->name;
  85. map = devm_regmap_init_mmio(&pdev->dev, mem, &sparx5_reset_regmap_config);
  86. if (IS_ERR(map))
  87. return PTR_ERR(map);
  88. *target = map;
  89. return 0;
  90. }
  91. static int mchp_sparx5_reset_probe(struct platform_device *pdev)
  92. {
  93. struct device_node *dn = pdev->dev.of_node;
  94. struct mchp_reset_context *ctx;
  95. int err;
  96. ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
  97. if (!ctx)
  98. return -ENOMEM;
  99. err = mchp_sparx5_map_syscon(pdev, "cpu-syscon", &ctx->cpu_ctrl);
  100. if (err)
  101. return err;
  102. err = mchp_sparx5_map_io(pdev, 0, &ctx->gcb_ctrl);
  103. if (err)
  104. return err;
  105. ctx->rcdev.owner = THIS_MODULE;
  106. ctx->rcdev.nr_resets = 1;
  107. ctx->rcdev.ops = &sparx5_reset_ops;
  108. ctx->rcdev.of_node = dn;
  109. ctx->props = device_get_match_data(&pdev->dev);
  110. /* Issue the reset very early, our actual reset callback is a noop. */
  111. err = sparx5_switch_reset(ctx);
  112. if (err)
  113. return err;
  114. return devm_reset_controller_register(&pdev->dev, &ctx->rcdev);
  115. }
  116. static const struct reset_props reset_props_sparx5 = {
  117. .protect_reg = 0x84,
  118. .protect_bit = BIT(10),
  119. .reset_reg = 0x0,
  120. .reset_bit = BIT(1),
  121. };
  122. static const struct reset_props reset_props_lan966x = {
  123. .protect_reg = 0x88,
  124. .protect_bit = BIT(5),
  125. .reset_reg = 0x0,
  126. .reset_bit = BIT(1),
  127. };
  128. static const struct of_device_id mchp_sparx5_reset_of_match[] = {
  129. {
  130. .compatible = "microchip,sparx5-switch-reset",
  131. .data = &reset_props_sparx5,
  132. }, {
  133. .compatible = "microchip,lan966x-switch-reset",
  134. .data = &reset_props_lan966x,
  135. },
  136. { }
  137. };
  138. static struct platform_driver mchp_sparx5_reset_driver = {
  139. .probe = mchp_sparx5_reset_probe,
  140. .driver = {
  141. .name = "sparx5-switch-reset",
  142. .of_match_table = mchp_sparx5_reset_of_match,
  143. },
  144. };
  145. static int __init mchp_sparx5_reset_init(void)
  146. {
  147. return platform_driver_register(&mchp_sparx5_reset_driver);
  148. }
  149. /*
  150. * Because this is a global reset, keep this postcore_initcall() to issue the
  151. * reset as early as possible during the kernel startup.
  152. */
  153. postcore_initcall(mchp_sparx5_reset_init);
  154. MODULE_DESCRIPTION("Microchip Sparx5 switch reset driver");
  155. MODULE_AUTHOR("Steen Hegelund <[email protected]>");
  156. MODULE_LICENSE("Dual MIT/GPL");