diff options
author | Andrew F. Davis <afd@ti.com> | 2016-06-27 13:12:17 -0400 |
---|---|---|
committer | Philipp Zabel <p.zabel@pengutronix.de> | 2016-06-29 17:39:10 -0400 |
commit | cc7c2bb1493c4118d5ae69e350a405faf3ddfb89 (patch) | |
tree | 4983259634a033ddc5d8220fa98662cd24c6f0d1 | |
parent | a3828519c39aa9c85bae14bad2572d12dc0d1da6 (diff) |
reset: add TI SYSCON based reset driver
Add a reset-controller driver for performing reset management of
various devices present on the SoC, with the reset registers shared
between devices in a common register memory space. This driver uses
the syscon/regmap frameworks to actually implement the various reset
functionalities needed by the reset consumer devices.
Signed-off-by: Andrew F. Davis <afd@ti.com>
[s-anna@ti.com: add documentation, syscon name change]
Signed-off-by: Suman Anna <s-anna@ti.com>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
-rw-r--r-- | drivers/reset/Kconfig | 10 | ||||
-rw-r--r-- | drivers/reset/Makefile | 1 | ||||
-rw-r--r-- | drivers/reset/reset-ti-syscon.c | 237 |
3 files changed, 248 insertions, 0 deletions
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index ab37f4db9642..4be1b8c21f6f 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig | |||
@@ -17,6 +17,16 @@ if RESET_CONTROLLER | |||
17 | config RESET_OXNAS | 17 | config RESET_OXNAS |
18 | bool | 18 | bool |
19 | 19 | ||
20 | config TI_SYSCON_RESET | ||
21 | tristate "TI SYSCON Reset Driver" | ||
22 | depends on HAS_IOMEM | ||
23 | select MFD_SYSCON | ||
24 | help | ||
25 | This enables the reset driver support for TI devices with | ||
26 | memory-mapped reset registers as part of a syscon device node. If | ||
27 | you wish to use the reset framework for such memory-mapped devices, | ||
28 | say Y here. Otherwise, say N. | ||
29 | |||
20 | source "drivers/reset/sti/Kconfig" | 30 | source "drivers/reset/sti/Kconfig" |
21 | source "drivers/reset/hisilicon/Kconfig" | 31 | source "drivers/reset/hisilicon/Kconfig" |
22 | 32 | ||
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 03dc1bb7649e..5d65a93d3c43 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile | |||
@@ -10,3 +10,4 @@ obj-$(CONFIG_ARCH_HISI) += hisilicon/ | |||
10 | obj-$(CONFIG_ARCH_ZYNQ) += reset-zynq.o | 10 | obj-$(CONFIG_ARCH_ZYNQ) += reset-zynq.o |
11 | obj-$(CONFIG_ATH79) += reset-ath79.o | 11 | obj-$(CONFIG_ATH79) += reset-ath79.o |
12 | obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o | 12 | obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o |
13 | obj-$(CONFIG_TI_SYSCON_RESET) += reset-ti-syscon.o | ||
diff --git a/drivers/reset/reset-ti-syscon.c b/drivers/reset/reset-ti-syscon.c new file mode 100644 index 000000000000..47f0ffd3b013 --- /dev/null +++ b/drivers/reset/reset-ti-syscon.c | |||
@@ -0,0 +1,237 @@ | |||
1 | /* | ||
2 | * TI SYSCON regmap reset driver | ||
3 | * | ||
4 | * Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/ | ||
5 | * Andrew F. Davis <afd@ti.com> | ||
6 | * Suman Anna <afd@ti.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | ||
13 | * kind, whether express or implied; without even the implied warranty | ||
14 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | */ | ||
17 | |||
18 | #include <linux/mfd/syscon.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/of.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/regmap.h> | ||
23 | #include <linux/reset-controller.h> | ||
24 | |||
25 | #include <dt-bindings/reset/ti-syscon.h> | ||
26 | |||
27 | /** | ||
28 | * struct ti_syscon_reset_control - reset control structure | ||
29 | * @assert_offset: reset assert control register offset from syscon base | ||
30 | * @assert_bit: reset assert bit in the reset assert control register | ||
31 | * @deassert_offset: reset deassert control register offset from syscon base | ||
32 | * @deassert_bit: reset deassert bit in the reset deassert control register | ||
33 | * @status_offset: reset status register offset from syscon base | ||
34 | * @status_bit: reset status bit in the reset status register | ||
35 | * @flags: reset flag indicating how the (de)assert and status are handled | ||
36 | */ | ||
37 | struct ti_syscon_reset_control { | ||
38 | unsigned int assert_offset; | ||
39 | unsigned int assert_bit; | ||
40 | unsigned int deassert_offset; | ||
41 | unsigned int deassert_bit; | ||
42 | unsigned int status_offset; | ||
43 | unsigned int status_bit; | ||
44 | u32 flags; | ||
45 | }; | ||
46 | |||
47 | /** | ||
48 | * struct ti_syscon_reset_data - reset controller information structure | ||
49 | * @rcdev: reset controller entity | ||
50 | * @regmap: regmap handle containing the memory-mapped reset registers | ||
51 | * @controls: array of reset controls | ||
52 | * @nr_controls: number of controls in control array | ||
53 | */ | ||
54 | struct ti_syscon_reset_data { | ||
55 | struct reset_controller_dev rcdev; | ||
56 | struct regmap *regmap; | ||
57 | struct ti_syscon_reset_control *controls; | ||
58 | unsigned int nr_controls; | ||
59 | }; | ||
60 | |||
61 | #define to_ti_syscon_reset_data(rcdev) \ | ||
62 | container_of(rcdev, struct ti_syscon_reset_data, rcdev) | ||
63 | |||
64 | /** | ||
65 | * ti_syscon_reset_assert() - assert device reset | ||
66 | * @rcdev: reset controller entity | ||
67 | * @id: ID of the reset to be asserted | ||
68 | * | ||
69 | * This function implements the reset driver op to assert a device's reset. | ||
70 | * This asserts the reset in a manner prescribed by the reset flags. | ||
71 | * | ||
72 | * Return: 0 for successful request, else a corresponding error value | ||
73 | */ | ||
74 | static int ti_syscon_reset_assert(struct reset_controller_dev *rcdev, | ||
75 | unsigned long id) | ||
76 | { | ||
77 | struct ti_syscon_reset_data *data = to_ti_syscon_reset_data(rcdev); | ||
78 | struct ti_syscon_reset_control *control; | ||
79 | unsigned int mask, value; | ||
80 | |||
81 | if (id >= data->nr_controls) | ||
82 | return -EINVAL; | ||
83 | |||
84 | control = &data->controls[id]; | ||
85 | |||
86 | if (control->flags & ASSERT_NONE) | ||
87 | return -ENOTSUPP; /* assert not supported for this reset */ | ||
88 | |||
89 | mask = BIT(control->assert_bit); | ||
90 | value = (control->flags & ASSERT_SET) ? mask : 0x0; | ||
91 | |||
92 | return regmap_update_bits(data->regmap, control->assert_offset, mask, value); | ||
93 | } | ||
94 | |||
95 | /** | ||
96 | * ti_syscon_reset_deassert() - deassert device reset | ||
97 | * @rcdev: reset controller entity | ||
98 | * @id: ID of reset to be deasserted | ||
99 | * | ||
100 | * This function implements the reset driver op to deassert a device's reset. | ||
101 | * This deasserts the reset in a manner prescribed by the reset flags. | ||
102 | * | ||
103 | * Return: 0 for successful request, else a corresponding error value | ||
104 | */ | ||
105 | static int ti_syscon_reset_deassert(struct reset_controller_dev *rcdev, | ||
106 | unsigned long id) | ||
107 | { | ||
108 | struct ti_syscon_reset_data *data = to_ti_syscon_reset_data(rcdev); | ||
109 | struct ti_syscon_reset_control *control; | ||
110 | unsigned int mask, value; | ||
111 | |||
112 | if (id >= data->nr_controls) | ||
113 | return -EINVAL; | ||
114 | |||
115 | control = &data->controls[id]; | ||
116 | |||
117 | if (control->flags & DEASSERT_NONE) | ||
118 | return -ENOTSUPP; /* deassert not supported for this reset */ | ||
119 | |||
120 | mask = BIT(control->deassert_bit); | ||
121 | value = (control->flags & DEASSERT_SET) ? mask : 0x0; | ||
122 | |||
123 | return regmap_update_bits(data->regmap, control->deassert_offset, mask, value); | ||
124 | } | ||
125 | |||
126 | /** | ||
127 | * ti_syscon_reset_status() - check device reset status | ||
128 | * @rcdev: reset controller entity | ||
129 | * @id: ID of the reset for which the status is being requested | ||
130 | * | ||
131 | * This function implements the reset driver op to return the status of a | ||
132 | * device's reset. | ||
133 | * | ||
134 | * Return: 0 if reset is deasserted, true if reset is asserted, else a | ||
135 | * corresponding error value | ||
136 | */ | ||
137 | static int ti_syscon_reset_status(struct reset_controller_dev *rcdev, | ||
138 | unsigned long id) | ||
139 | { | ||
140 | struct ti_syscon_reset_data *data = to_ti_syscon_reset_data(rcdev); | ||
141 | struct ti_syscon_reset_control *control; | ||
142 | unsigned int reset_state; | ||
143 | int ret; | ||
144 | |||
145 | if (id >= data->nr_controls) | ||
146 | return -EINVAL; | ||
147 | |||
148 | control = &data->controls[id]; | ||
149 | |||
150 | if (control->flags & STATUS_NONE) | ||
151 | return -ENOTSUPP; /* status not supported for this reset */ | ||
152 | |||
153 | ret = regmap_read(data->regmap, control->status_offset, &reset_state); | ||
154 | if (ret) | ||
155 | return ret; | ||
156 | |||
157 | return (reset_state & BIT(control->status_bit)) && | ||
158 | (control->flags & STATUS_SET); | ||
159 | } | ||
160 | |||
161 | static struct reset_control_ops ti_syscon_reset_ops = { | ||
162 | .assert = ti_syscon_reset_assert, | ||
163 | .deassert = ti_syscon_reset_deassert, | ||
164 | .status = ti_syscon_reset_status, | ||
165 | }; | ||
166 | |||
167 | static int ti_syscon_reset_probe(struct platform_device *pdev) | ||
168 | { | ||
169 | struct device *dev = &pdev->dev; | ||
170 | struct device_node *np = dev->of_node; | ||
171 | struct ti_syscon_reset_data *data; | ||
172 | struct regmap *regmap; | ||
173 | const __be32 *list; | ||
174 | struct ti_syscon_reset_control *controls; | ||
175 | int size, nr_controls, i; | ||
176 | |||
177 | data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); | ||
178 | if (!data) | ||
179 | return -ENOMEM; | ||
180 | |||
181 | regmap = syscon_node_to_regmap(np->parent); | ||
182 | if (IS_ERR(regmap)) | ||
183 | return PTR_ERR(regmap); | ||
184 | |||
185 | list = of_get_property(np, "ti,reset-bits", &size); | ||
186 | if (!list || (size / sizeof(*list)) % 7 != 0) { | ||
187 | dev_err(dev, "invalid DT reset description\n"); | ||
188 | return -EINVAL; | ||
189 | } | ||
190 | |||
191 | nr_controls = (size / sizeof(*list)) / 7; | ||
192 | controls = devm_kzalloc(dev, nr_controls * sizeof(*controls), GFP_KERNEL); | ||
193 | if (!controls) | ||
194 | return -ENOMEM; | ||
195 | |||
196 | for (i = 0; i < nr_controls; i++) { | ||
197 | controls[i].assert_offset = be32_to_cpup(list++); | ||
198 | controls[i].assert_bit = be32_to_cpup(list++); | ||
199 | controls[i].deassert_offset = be32_to_cpup(list++); | ||
200 | controls[i].deassert_bit = be32_to_cpup(list++); | ||
201 | controls[i].status_offset = be32_to_cpup(list++); | ||
202 | controls[i].status_bit = be32_to_cpup(list++); | ||
203 | controls[i].flags = be32_to_cpup(list++); | ||
204 | } | ||
205 | |||
206 | data->rcdev.ops = &ti_syscon_reset_ops; | ||
207 | data->rcdev.owner = THIS_MODULE; | ||
208 | data->rcdev.of_node = np; | ||
209 | data->rcdev.nr_resets = nr_controls; | ||
210 | data->regmap = regmap; | ||
211 | data->controls = controls; | ||
212 | data->nr_controls = nr_controls; | ||
213 | |||
214 | platform_set_drvdata(pdev, data); | ||
215 | |||
216 | return devm_reset_controller_register(dev, &data->rcdev); | ||
217 | } | ||
218 | |||
219 | static const struct of_device_id ti_syscon_reset_of_match[] = { | ||
220 | { .compatible = "ti,syscon-reset", }, | ||
221 | { /* sentinel */ }, | ||
222 | }; | ||
223 | MODULE_DEVICE_TABLE(of, ti_syscon_reset_of_match); | ||
224 | |||
225 | static struct platform_driver ti_syscon_reset_driver = { | ||
226 | .probe = ti_syscon_reset_probe, | ||
227 | .driver = { | ||
228 | .name = "ti-syscon-reset", | ||
229 | .of_match_table = ti_syscon_reset_of_match, | ||
230 | }, | ||
231 | }; | ||
232 | module_platform_driver(ti_syscon_reset_driver); | ||
233 | |||
234 | MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); | ||
235 | MODULE_AUTHOR("Suman Anna <s-anna@ti.com>"); | ||
236 | MODULE_DESCRIPTION("TI SYSCON Regmap Reset Driver"); | ||
237 | MODULE_LICENSE("GPL v2"); | ||