Commit | Line | Data |
---|---|---|
453ed428 SH |
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 | ||
8c81620a HV |
16 | struct reset_props { |
17 | u32 protect_reg; | |
18 | u32 protect_bit; | |
19 | u32 reset_reg; | |
20 | u32 reset_bit; | |
21 | }; | |
453ed428 SH |
22 | |
23 | struct mchp_reset_context { | |
24 | struct regmap *cpu_ctrl; | |
25 | struct regmap *gcb_ctrl; | |
26 | struct reset_controller_dev rcdev; | |
8c81620a | 27 | const struct reset_props *props; |
453ed428 SH |
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 | ||
51fd1914 | 36 | static int sparx5_switch_reset(struct mchp_reset_context *ctx) |
453ed428 | 37 | { |
453ed428 SH |
38 | u32 val; |
39 | ||
40 | /* Make sure the core is PROTECTED from reset */ | |
8c81620a HV |
41 | regmap_update_bits(ctx->cpu_ctrl, ctx->props->protect_reg, |
42 | ctx->props->protect_bit, ctx->props->protect_bit); | |
453ed428 SH |
43 | |
44 | /* Start soft reset */ | |
8c81620a HV |
45 | regmap_write(ctx->gcb_ctrl, ctx->props->reset_reg, |
46 | ctx->props->reset_bit); | |
453ed428 SH |
47 | |
48 | /* Wait for soft reset done */ | |
8c81620a HV |
49 | return regmap_read_poll_timeout(ctx->gcb_ctrl, ctx->props->reset_reg, val, |
50 | (val & ctx->props->reset_bit) == 0, | |
453ed428 SH |
51 | 1, 100); |
52 | } | |
53 | ||
51fd1914 MW |
54 | static int sparx5_reset_noop(struct reset_controller_dev *rcdev, |
55 | unsigned long id) | |
56 | { | |
57 | return 0; | |
58 | } | |
59 | ||
453ed428 | 60 | static const struct reset_control_ops sparx5_reset_ops = { |
51fd1914 | 61 | .reset = sparx5_reset_noop, |
453ed428 SH |
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); | |
91105ed6 | 93 | if (IS_ERR(mem)) { |
453ed428 | 94 | dev_err(&pdev->dev, "Could not map resource %d\n", index); |
91105ed6 | 95 | return PTR_ERR(mem); |
453ed428 SH |
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; | |
8c81620a | 126 | ctx->props = device_get_match_data(&pdev->dev); |
453ed428 | 127 | |
51fd1914 MW |
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 | ||
453ed428 SH |
133 | return devm_reset_controller_register(&pdev->dev, &ctx->rcdev); |
134 | } | |
135 | ||
8c81620a HV |
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 | ||
453ed428 SH |
150 | static const struct of_device_id mchp_sparx5_reset_of_match[] = { |
151 | { | |
152 | .compatible = "microchip,sparx5-switch-reset", | |
8c81620a HV |
153 | .data = &reset_props_sparx5, |
154 | }, { | |
155 | .compatible = "microchip,lan966x-switch-reset", | |
156 | .data = &reset_props_lan966x, | |
453ed428 SH |
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 | ||
51fd1914 MW |
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 | */ | |
453ed428 SH |
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 | MODULE_LICENSE("Dual MIT/GPL"); |