2 * TI SYSCON regmap reset driver
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>
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.
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.
18 #include <linux/mfd/syscon.h>
19 #include <linux/module.h>
21 #include <linux/platform_device.h>
22 #include <linux/regmap.h>
23 #include <linux/reset-controller.h>
25 #include <dt-bindings/reset/ti-syscon.h>
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
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
;
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
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
;
61 #define to_ti_syscon_reset_data(rcdev) \
62 container_of(rcdev, struct ti_syscon_reset_data, rcdev)
65 * ti_syscon_reset_assert() - assert device reset
66 * @rcdev: reset controller entity
67 * @id: ID of the reset to be asserted
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.
72 * Return: 0 for successful request, else a corresponding error value
74 static int ti_syscon_reset_assert(struct reset_controller_dev
*rcdev
,
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
;
81 if (id
>= data
->nr_controls
)
84 control
= &data
->controls
[id
];
86 if (control
->flags
& ASSERT_NONE
)
87 return -ENOTSUPP
; /* assert not supported for this reset */
89 mask
= BIT(control
->assert_bit
);
90 value
= (control
->flags
& ASSERT_SET
) ? mask
: 0x0;
92 return regmap_update_bits(data
->regmap
, control
->assert_offset
, mask
, value
);
96 * ti_syscon_reset_deassert() - deassert device reset
97 * @rcdev: reset controller entity
98 * @id: ID of reset to be deasserted
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.
103 * Return: 0 for successful request, else a corresponding error value
105 static int ti_syscon_reset_deassert(struct reset_controller_dev
*rcdev
,
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
;
112 if (id
>= data
->nr_controls
)
115 control
= &data
->controls
[id
];
117 if (control
->flags
& DEASSERT_NONE
)
118 return -ENOTSUPP
; /* deassert not supported for this reset */
120 mask
= BIT(control
->deassert_bit
);
121 value
= (control
->flags
& DEASSERT_SET
) ? mask
: 0x0;
123 return regmap_update_bits(data
->regmap
, control
->deassert_offset
, mask
, value
);
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
131 * This function implements the reset driver op to return the status of a
134 * Return: 0 if reset is deasserted, true if reset is asserted, else a
135 * corresponding error value
137 static int ti_syscon_reset_status(struct reset_controller_dev
*rcdev
,
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
;
145 if (id
>= data
->nr_controls
)
148 control
= &data
->controls
[id
];
150 if (control
->flags
& STATUS_NONE
)
151 return -ENOTSUPP
; /* status not supported for this reset */
153 ret
= regmap_read(data
->regmap
, control
->status_offset
, &reset_state
);
157 return (reset_state
& BIT(control
->status_bit
)) &&
158 (control
->flags
& STATUS_SET
);
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
,
167 static int ti_syscon_reset_probe(struct platform_device
*pdev
)
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
;
174 struct ti_syscon_reset_control
*controls
;
175 int size
, nr_controls
, i
;
177 data
= devm_kzalloc(dev
, sizeof(*data
), GFP_KERNEL
);
181 regmap
= syscon_node_to_regmap(np
->parent
);
183 return PTR_ERR(regmap
);
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");
191 nr_controls
= (size
/ sizeof(*list
)) / 7;
192 controls
= devm_kzalloc(dev
, nr_controls
* sizeof(*controls
), GFP_KERNEL
);
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
++);
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
;
214 platform_set_drvdata(pdev
, data
);
216 return devm_reset_controller_register(dev
, &data
->rcdev
);
219 static const struct of_device_id ti_syscon_reset_of_match
[] = {
220 { .compatible
= "ti,syscon-reset", },
223 MODULE_DEVICE_TABLE(of
, ti_syscon_reset_of_match
);
225 static struct platform_driver ti_syscon_reset_driver
= {
226 .probe
= ti_syscon_reset_probe
,
228 .name
= "ti-syscon-reset",
229 .of_match_table
= ti_syscon_reset_of_match
,
232 module_platform_driver(ti_syscon_reset_driver
);
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");